我正在使用GDB来调试我的一些C应用程序.我目前所做的是加载目标应用程序,在第30行设置断点并运行它.
我想让GDB在新的终端窗口中显示我自己的应用程序的输出,而我仍然可以通过GDB终端窗口控制断点处理,但我似乎找不到合适的开关.有没有办法让GDB在自己的窗口中显示我的程序输出?
Aut*_*act 21
对于想知道如何使用GDB tty命令的人来说,这是一个简短的描述......
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方法.
另一种方法是使用gdbserver启动目标程序(假设它可用).然后你可以在一个单独的窗口中将GDB连接到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)
GDB 的tty命令确实有效,但它不适用于交互式程序,例如您想调试 bash。即使对于非交互式程序,您也会得到以下结果:
warning: GDB: Failed to set controlling terminal: Operation not permitted\nRun 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}\nRun Code Online (Sandbox Code Playgroud)\n假设您将程序保存到idleterm.c. 编译它:
> gcc -o idleterm idleterm.c -luring\nRun Code Online (Sandbox Code Playgroud)\n要使用它,请启动一个终端窗口,然后在该窗口上运行idleterm. 它将打印要附加到的 tty 的名称:
> ./idleterm\npid = 3405922 tty = /dev/pts/0\n\xe2\x96\x88\nRun 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\nRun Code Online (Sandbox Code Playgroud)\n第一个窗口中将出现 bash 提示符。所有需要特殊 TTY 行为的 bash 交互都将正常工作,包括^C、^Z等。
idleterm将所有键盘输入传递给正在调试的子进程,包括 ^C 和 ^Z。因此,为了停止idleterm,您必须从单独的窗口中终止它。idleterm这就是打印其 pid 的原因。复制 pid,然后将其粘贴到 Kill 命令中:
> kill 3405922\nRun Code Online (Sandbox Code Playgroud)\nidleterm如果系统上只有一个运行实例,您当然可以只使用killall.