programing

GDB - 별도창에 대상어플리케이션 출력 표시

linuxpc 2023. 9. 13. 22:21
반응형

GDB - 별도창에 대상어플리케이션 출력 표시

저는 GDB를 사용하여 제 C 애플리케이션 중 일부를 디버깅하고 있습니다.현재 제가 하는 일은 대상 어플리케이션을 로드하고 30번 라인에서 중단점을 설정하여 실행하는 것입니다.

GDB 터미널 창을 통해 중단점 처리를 제어할 수 있는 상태에서 새로운 터미널 창에 자신의 어플리케이션의 출력을 표시하도록 하고 싶지만 적절한 스위치를 찾을 수 없었던 것 같습니다.GDB에서 제 프로그램의 출력을 자체 창에 표시할 수 있는 방법은 없습니까?

GDbtty 명령어 사용법에 대해 궁금해하는 사람들을 위해 여기 간단한 설명이 있습니다.

  • 새 콘솔 창을 엽니다.GDB에서 실행 중인 프로그램의 출력을 여기로 리디렉션합니다.이것이 우리의 출력창입니다.
  • 더 실행tty출력 창에 명령을 입력합니다.기본 콘솔에서 사용 중인 tty의 이름이 표시됩니다.

    $ tty
    /dev/pts/4

  • 다른 콘솔 창을 열고 여기서 GDB를 시작합니다.이것을 GDB 창이라고 부르도록 하겠습니다.

  • 이제 위에서 얻은 tty 파일명을 사용하여 GDB에서 tty 명령을 실행한 후 디버깅 과정을 시작합니다.

    (gdb) tty /dev/pts/4
    (gdb) run

이제 출력창에서 프로그램 출력을 따로 볼 수 있을 것입니다.

: GDBset new-console on명령어는 Linux에서 작동하지 않습니다!이것은 윈도우에서만 실행되도록 되어있습니다.리눅스에서 위에 설명한 tty 메서드를 사용합니다.

을 사용할 수 .set new-console on이를 달성하기 위해 여기에 나와 있는 것입니다.

이를 위한 또 다른 방법은 (사용자가 사용할 수 있다고 가정할 때) gdb서버에서 대상 프로그램을 시작하는 것입니다.그러면 별도의 창에서 시작한 GDB를 gdb 서버에 연결할 수 있습니다.

GNU gdb서버 설명서

창 A에서:

gdbserver :12345 myprog [args...]

창 B에서:

gdb myprog
GNU gdb 6.6
...
(gdb) target remote localhost:12345
Remote debugging using localhost:12345
0x009867c0 in ?? ()
(gdb) b main
Breakpoint 1 at 0x804834a: file myprog.c, line 40.
(gdb) c
Continuing.

Breakpoint 1, main (argc=1, argv=0xffff8904) at myprog.c:40
40          int i = 1;
(gdb) 

가장 , 은 의 을 로 가 로 을 가 tail -f다른 터미널에 있는 파일입니다.리디렉션은 다음과 같이 수행됩니다.run > filename, GDB 문서에 기록된 바와 같이.

GDB의tty명령어는 작동하지만 bash를 디버깅하려는 경우처럼 대화형 프로그램에서는 잘 작동하지 않습니다.대화형이 아닌 프로그램의 경우에도 다음을 얻을 수 있습니다.

warning: GDB: Failed to set controlling terminal: Operation not permitted

이 두 가지 문제를 모두 해결하기 위해 작은 프로그램을 작성했습니다.

// Open a pty and let it idle, so that a process spawned in a different window
// can attach to it, start a new session, and set it as the controlling
// terminal. Useful for gdb debugging with gdb's `tty` command.
 
#include <inttypes.h>
typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64;
typedef  int8_t i8; typedef  int16_t i16; typedef  int32_t i32; typedef  int64_t i64;
#include <stdlib.h>
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

#include <termios.h>
#include <pty.h>
#include <liburing.h>

#define BSIZE 4096

void raw_terminal(void)
{
    if (!isatty(0))
        return;
    struct termios t;
    tcgetattr(0, &t);
    t.c_lflag &= ~(ISIG | ICANON | ECHO);
    tcsetattr(0, TCSANOW, &t);
}

// Refers to the state of a Joint /while it's waiting in io_uring_enter/.
enum State {
    READ,
    WRITE
};
// Joins two fds together, like splice, but not a syscall and works on any two
// fds.
struct Joint {
    u8 buf[BSIZE];
    i32 ifd;
    i32 ofd;
    enum State state;
    u32 nread;
};
void roll_joint(struct Joint *j, struct io_uring *ur, i32 ifd, i32 ofd)
{
    j->ifd = ifd;
    j->ofd = ofd;
    j->state = READ;
    struct io_uring_sqe *sqe = io_uring_get_sqe(ur);
    io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);
    io_uring_sqe_set_data(sqe, j);
    io_uring_submit(ur);
}
i32 main(i32 argc, char **argv)
{
    raw_terminal();
    struct io_uring ur;
    assert(io_uring_queue_init(256, &ur, 0) == 0);

    i32 ptm, pts;
    assert(openpty(&ptm, &pts, NULL, NULL, NULL) == 0);
    dprintf(2, "pid = %u   tty = %s\n", getpid(), ttyname(pts));

    struct Joint jkbd;
    roll_joint(&jkbd, &ur, 0, ptm);
    struct Joint jscreen;
    roll_joint(&jscreen, &ur, ptm, 1);

    for (;;) {
        struct io_uring_cqe *cqe;
        for (;;) {
            // Actions like suspend to RAM can interrupt the io_uring_enter
            // syscall. If we get interrupted, try again. For all other errors,
            // bail. Also, wait_cqe negates the error for no reason. It never
            // returns positive numbers. Very silly.
            u32 res = -io_uring_wait_cqe(&ur, &cqe);
            if (res == 0)
                break;
            else if (res != EINTR) {
                dprintf(2, "io_uring_enter returns errno %d\n", res);
                exit(res);
            }
        }
        struct Joint *j = io_uring_cqe_get_data(cqe);
        if (j->state == READ) {
            // Exiting READ state. Finish with the read...
            j->nread = cqe->res;
            assert(j->nread > 0);

            // Now, start the write.
            j->state = WRITE;
            struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);
            io_uring_prep_write(sqe, j->ofd, j->buf, j->nread, 0);
            io_uring_sqe_set_data(sqe, j);
            io_uring_submit(&ur);
        }
        else if (j->state == WRITE) {
            // Exiting WRITE state. Finish with the write...
            i64 nwritten = cqe->res;
            assert(nwritten == j->nread);

            // Now, start the read.
            j->state = READ;
            struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);
            io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);
            io_uring_sqe_set_data(sqe, j);
            io_uring_submit(&ur);
        }
        io_uring_cqe_seen(&ur, cqe);
    }

    io_uring_queue_exit(&ur);
    return 0;
}

프로그램을 다음에 저장한다고 가정합니다.idleterm.c 컴파일하기:

> gcc -o idleterm idleterm.c -luring

사용하려면 하나의 터미널 창을 시작하고, 그 창에서 실행합니다.idleterm의 이름을 첨부할 tty의 이름을 인쇄합니다.

> ./idleterm
pid = 3405922   tty = /dev/pts/0
█

해당 tty 경로를 복사하여 두 번째 창에서 gdb 세션에 붙여넣습니다.

> gdb bash
Reading symbols from bash...
(No debugging symbols found in bash)
(gdb) tty /dev/pts/0
(gdb) r
Starting program: /usr/bin/bash
…

첫 번째 창에 bash 프롬프트가 나타납니다.특별한 TTY 동작이 필요한 bash와의 모든 상호 작용은 다음을 포함하여 정상적으로 작동합니다.^C,^Z,기타.

idleterm는 ^C 및 ^Z를 포함한 모든 키보드 입력을 디버깅 중인 자식 프로세스로 전달합니다.따라서 유휴 기간을 멈추려면 별도의 창에서 이 기간을 죽여야 합니다.그래서.idletermpid를 찍어냅니다.pid를 복사한 다음 kill 명령에 붙여넣습니다.

> kill 3405922

단 한 가지의 예가 있다면idleterm시스템 상에서 실행되는 것, 물론 당신은 단지 사용할 수 있습니다.killall.

그냥 사용.tty지휘.프로그램의 출력을 /dev/pts/5로 리디렉션하려면 다음 유형을 입력합니다.

tty /dev/pts/5

맥에서 lldb를 사용하면 디버거가 원래 창에서 제어하는 동안 새 터미널 창에서 프로그램을 실행합니다.

$ lldb   <prog>
(lldb) b main   # if you want to set a breakpoint
(lldb) process launch --tty -- <args>

지정된 tty(단말기 창)의 프로세스로 프로그램을 실행합니다.

$ tty   # (in other window, get tty name)
/dev/ttys002

$ lldb  
(lldb) b main   # if you want to set a breakpoint
(lldb) process launch --tty=/dev/ttys002 -- <args>

언급URL : https://stackoverflow.com/questions/8963208/gdb-display-output-of-target-application-in-a-separate-window

반응형