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

发布于 2024-12-28 03:26:03 字数 169 浏览 0 评论 0原文

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

我想让 GDB 在新的终端窗口中显示我自己的应用程序的输出,同时我仍然能够通过 GDB 终端窗口控制断点处理,但我似乎找不到合适的开关。有什么办法让 GDB 在它自己的窗口中显示我的程序的输出吗?

I'm using GDB to debug some of my C applications. What I currently do is load the target application, set a breakpoint on line 30 and run it.

I would like to make GDB display the output of my own application in a new terminal window while I'm still able to control the breakpoint handling via the GDB terminal window, but I couldn't seem to find a proper switch. Is there any way making GDB display my program's output in its own window?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

万人眼中万个我 2025-01-04 03:26:03

对于想知道如何使用 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 方法。

For people wondering how to use the GDB tty command here is a short description...

  • Open a new console window. We will redirect output from the program running under GDB here. This is our output window.
  • Run the tty command in the output window. This will show the name of the tty being used by the underlying console.

    $ tty
    /dev/pts/4

  • Open another console window and start GDB here. Let us call this the GDB window.

  • Now run the tty command in GDB using the tty file name obtained above and then start the debugging process.

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

Now you should be able to see the program output separately in the output window.

Note: The GDB set new-console on command does not work on Linux! It is meant to be run on windows only. Use the tty method described above on Linux.

吻安 2025-01-04 03:26:03

您可以使用 set new-console on 来完成此操作,如此处所示。

You can use set new-console on to accomplish this as shown here.

甜味超标? 2025-01-04 03:26:03

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

GNU gdbserver 文档

从窗口 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) 

Another way to do this would be to start your target program with gdbserver (assuming it is available to you). Then you can connect GDB started in a separate window to gdbserver.

GNU gdbserver documentation

From window A:

gdbserver :12345 myprog [args...]

From window 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) 
原来分手还会想你 2025-01-04 03:26:03

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

The best way I know is to redirect the output of the program to a file, and then tail -f that file in another terminal. Redirection is done with run > filename, as documented in the GDB documentation.

无声情话 2025-01-04 03:26:03

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。因此,为了停止idleterm,您必须从单独的窗口中终止它。这就是 idleterm 打印其 pid 的原因。复制 pid,然后将其粘贴到 Kill 命令中:

> kill 3405922

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

GDB's tty command does work, but it doesn't work well with interactive programs, like if you wanted to debug bash. Even for non-interactive programs, you get the following:

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

I wrote a small program to fix both of these issues:

// 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;
}

Suppose you save the program to idleterm.c. To compile it:

> gcc -o idleterm idleterm.c -luring

To use it, start one terminal window, and on that window, run idleterm. It will print the name of the tty to attach to:

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

Copy that tty path and paste it into a gdb session in a second window:

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

A bash prompt will appear in the first window. All of the interactions with bash requiring special TTY behaviour will work normally, including ^C, ^Z, etc.

idleterm passes all keyboard input through to the child process being debugged, including ^C and ^Z. So in order to stop idleterm, you'll have to kill it from a separate window. This is why idleterm prints its pid. Copy the pid, then paste it into the kill command:

> kill 3405922

If there's only one instance of idleterm running on the system, you can of course just use killall.

愁杀 2025-01-04 03:26:03

只需使用 tty 命令即可。如果您希望程序的输出重定向到 /dev/pts/5 ,请输入:

tty /dev/pts/5

Just use tty command. If you want output of your program redirected to /dev/pts/5 type:

tty /dev/pts/5
半世蒼涼 2025-01-04 03:26:03

对于 Mac 上的 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>

With lldb on Mac the following runs the program in w new terminal window while the debugger controls from the original window:

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

This runs the program as a process in specified tty (terminal window):

$ 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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文