GDB - 在单独的窗口中显示目标应用程序的输出

bet*_*eta 15 c gdb

我正在使用GDB来调试我的一些C应用程序.我目前所做的是加载目标应用程序,在第30行设置断点并运行它.

我想让GDB在新的终端窗口中显示我自己的应用程序的输出,而我仍然可以通过GDB终端窗口控制断点处理,但我似乎找不到合适的开关.有没有办法让GDB在自己的窗口中显示我的程序输出?

Aut*_*act 21

对于想知道如何使用GDB tty命令的人来说,这是一个简短的描述......

  • 打开一个新的控制台窗口.我们将在这里重定向GDB下运行的程序的输出.这是我们的输出窗口.
  • tty在输出窗口中运行该命令.这将显示底层控制台使用的tty的名称.

    $ tty
    /dev/pts/4

  • 打开另一个控制台窗口并在此处启动GDB.我们称之为GDB窗口.

  • 现在使用上面获得的tty文件名在GDB中运行tty命令,然后启动调试过程.

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

现在,您应该能够在输出窗口中单独看到程序输出.

注意:GDB set new-console on命令在Linux上不起作用!它只能在Windows上运行.在Linux上使用上面描述的tty方法.

  • 这是我发现在 Linux 上获得单独终端输出的最佳方式。 (2认同)
  • @MinhTran:我找到了解决此错误消息的方法。@x-yuri:GDB 的“tty”*确实*适用于交互式命令,但您指定的 tty 不得已附加任何子进程。你们俩,请参阅 /sf/answers/4992043171/ 。 (2认同)

gre*_*olf 15

您可以使用此处set new-console on显示完成此操作.

  • 似乎对我不起作用:当前上下文中没有“新”符号。 (4认同)

Dig*_*uma 7

另一种方法是使用gdbserver启动目标程序(假设它可用).然后你可以在一个单独的窗口中将GDB连接到gdbserver.

GNU gdbserver文档

从窗口A:

gdbserver :12345 myprog [args...]
Run Code Online (Sandbox Code Playgroud)

从窗口B:

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

Breakpoint 1, main (argc=1, argv=0xffff8904) at test.c:40
40          int i = 1;
(gdb) 
Run Code Online (Sandbox Code Playgroud)

  • 您可能希望绑定到“127.0.0.1:12345”。 (2认同)

drr*_*lvn 5

我知道的最好方法是将程序的输出重定向到一个文件,然后tail -f将该文件重定向到另一个终端。run > filenameGDB 文档中所述,重定向是通过 完成的。


eni*_*ist 5

GDB 的tty命令确实有效,但它不适用于交互式程序,例如您想调试 bash。即使对于非交互式程序,您也会得到以下结果:

\n
warning: GDB: Failed to set controlling terminal: Operation not permitted\n
Run Code Online (Sandbox Code Playgroud)\n

我编写了一个小程序来解决这两个问题:

\n
// Open a pty and let it idle, so that a process spawned in a different window\n// can attach to it, start a new session, and set it as the controlling\n// terminal. Useful for gdb debugging with gdb's `tty` command.\n \n#include <inttypes.h>\ntypedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64;\ntypedef  int8_t i8; typedef  int16_t i16; typedef  int32_t i32; typedef  int64_t i64;\n#include <stdlib.h>\n#include <assert.h>\n#include <errno.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include <termios.h>\n#include <pty.h>\n#include <liburing.h>\n\n#define BSIZE 4096\n\nvoid raw_terminal(void)\n{\n    if (!isatty(0))\n        return;\n    struct termios t;\n    tcgetattr(0, &t);\n    t.c_lflag &= ~(ISIG | ICANON | ECHO);\n    tcsetattr(0, TCSANOW, &t);\n}\n\n// Refers to the state of a Joint /while it's waiting in io_uring_enter/.\nenum State {\n    READ,\n    WRITE\n};\n// Joins two fds together, like splice, but not a syscall and works on any two\n// fds.\nstruct Joint {\n    u8 buf[BSIZE];\n    i32 ifd;\n    i32 ofd;\n    enum State state;\n    u32 nread;\n};\nvoid roll_joint(struct Joint *j, struct io_uring *ur, i32 ifd, i32 ofd)\n{\n    j->ifd = ifd;\n    j->ofd = ofd;\n    j->state = READ;\n    struct io_uring_sqe *sqe = io_uring_get_sqe(ur);\n    io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);\n    io_uring_sqe_set_data(sqe, j);\n    io_uring_submit(ur);\n}\ni32 main(i32 argc, char **argv)\n{\n    raw_terminal();\n    struct io_uring ur;\n    assert(io_uring_queue_init(256, &ur, 0) == 0);\n\n    i32 ptm, pts;\n    assert(openpty(&ptm, &pts, NULL, NULL, NULL) == 0);\n    dprintf(2, "pid = %u   tty = %s\\n", getpid(), ttyname(pts));\n\n    struct Joint jkbd;\n    roll_joint(&jkbd, &ur, 0, ptm);\n    struct Joint jscreen;\n    roll_joint(&jscreen, &ur, ptm, 1);\n\n    for (;;) {\n        struct io_uring_cqe *cqe;\n        for (;;) {\n            // Actions like suspend to RAM can interrupt the io_uring_enter\n            // syscall. If we get interrupted, try again. For all other errors,\n            // bail. Also, wait_cqe negates the error for no reason. It never\n            // returns positive numbers. Very silly.\n            u32 res = -io_uring_wait_cqe(&ur, &cqe);\n            if (res == 0)\n                break;\n            else if (res != EINTR) {\n                dprintf(2, "io_uring_enter returns errno %d\\n", res);\n                exit(res);\n            }\n        }\n        struct Joint *j = io_uring_cqe_get_data(cqe);\n        if (j->state == READ) {\n            // Exiting READ state. Finish with the read...\n            j->nread = cqe->res;\n            assert(j->nread > 0);\n\n            // Now, start the write.\n            j->state = WRITE;\n            struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);\n            io_uring_prep_write(sqe, j->ofd, j->buf, j->nread, 0);\n            io_uring_sqe_set_data(sqe, j);\n            io_uring_submit(&ur);\n        }\n        else if (j->state == WRITE) {\n            // Exiting WRITE state. Finish with the write...\n            i64 nwritten = cqe->res;\n            assert(nwritten == j->nread);\n\n            // Now, start the read.\n            j->state = READ;\n            struct io_uring_sqe *sqe = io_uring_get_sqe(&ur);\n            io_uring_prep_read(sqe, j->ifd, j->buf, BSIZE, 0);\n            io_uring_sqe_set_data(sqe, j);\n            io_uring_submit(&ur);\n        }\n        io_uring_cqe_seen(&ur, cqe);\n    }\n\n    io_uring_queue_exit(&ur);\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

假设您将程序保存到idleterm.c. 编译它:

\n
> gcc -o idleterm idleterm.c -luring\n
Run Code Online (Sandbox Code Playgroud)\n

要使用它,请启动一个终端窗口,然后在该窗口上运行idleterm. 它将打印要附加到的 tty 的名称:

\n
> ./idleterm\npid = 3405922   tty = /dev/pts/0\n\xe2\x96\x88\n
Run Code Online (Sandbox Code Playgroud)\n

复制该 tty 路径并将其粘贴到第二个窗口中的 gdb 会话中:

\n
> gdb bash\nReading symbols from bash...\n(No debugging symbols found in bash)\n(gdb) tty /dev/pts/0\n(gdb) r\nStarting program: /usr/bin/bash\n\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

第一个窗口中将出现 bash 提示符。所有需要特殊 TTY 行为的 bash 交互都将正常工作,包括^C^Z等。

\n

idleterm所有键盘输入传递给正在调试的子进程,包括 ^C 和 ^Z。因此,为了停止idleterm,您必须从单独的窗口中终止它。idleterm这就是打印其 pid 的原因。复制 pid,然后将其粘贴到 Kill 命令中:

\n
> kill 3405922\n
Run Code Online (Sandbox Code Playgroud)\n

idleterm如果系统上只有一个运行实例,您当然可以只使用killall.

\n