如何在GDB中调试fork-exec进程的入口点?

17
我有一个C语言的Linux应用程序(A),当启动(A)时,它会生成另一个进程(P)。当我想要调试P时,我像往常一样启动A并连接ddd/gdb到P。
当我想要调试P的入口点(main的开头)时就会出现问题。如果我按照通常的方法进行调试,当我连接调试器到P时已经太晚了。 我找到的解决方法是在P的main函数开头插入一个sleep,这样我就有时间连接gdb,但这不是一个非常优雅的解决方案。
我还尝试使用asm("int $3") ,但似乎不起作用。
你有什么办法可以解决这个问题吗?(最好不要更改A或P的代码)
5个回答

21

你应该使用这个选项:

set follow-fork-mode</code> <i>mode</i></pre>

<p>Where <em>mode</em> is one of <code>parent</code>, <code>child</code> or <code>ask</code>.</p>

<p>To follow the parent (this is the default) use:</p>

<pre><code>set follow-fork-mode parent

跟随子元素:

set follow-fork-mode child

让调试器每次都询问您:

set follow-fork-mode ask

基本上,您将首先连接gdb到A,然后设置gdb跟随子进程,当A生成P时,gdb将连接到P并从A分离。


1
“set follow-fork-mode ask”已被移除。根据GDB手册的说明,目前仅有的选项是“parent”和“child”。 - Fabio says Reinstate Monica
@FabiosaysReinstateMonica 我也注意到了。你有什么想法吗? - cassepipe

11

除了 Nathan Fellman 的答案 外,catchpoints 也很方便,例如:

catch exec

Catchpoint作为一个断点运行。每次检测到exec()系统调用时,GDB会停止。这使您能够在继续之前设置任何新加载的可执行文件中的任何断点(例如break main)。另一个catchpoint catch fork也类似地检测fork()系统调用。

这特别方便:

  • 当需要跟踪父进程和子进程时(set detach-on-fork off
  • 当父进程频繁分叉并加载各种可执行文件时。

1

exec部分使用file + break main

关于fork的部分已经在这里解释过了: https://dev59.com/BHRC5IYBdhLWcg3wMeLg#377295

现在我们来讲一下exec:

a.c:

#include <unistd.h>

int main(void) {
    execl("./b", "./b", "ab", "cd", (char*)NULL);
    return 1;
}

b.c:

#include <stdio.h>

int main(int argc, char **argv ) {
    printf("%s\n", argv[0]);
    printf("%s\n", argv[1]);
}

然后:

gcc -g a.c -o a
gcc -g b.c -o b
gdb -nh -q a

现在进入交互式会话:
Reading symbols from a...done.
(gdb) start
Temporary breakpoint 1 at 0x4004ea: file a.c, line 4.
Starting program: /home/ciro/test/gdb-exec/a 

Temporary breakpoint 1, main () at a.c:4
4           execl("./b", "./b", "ab", "cd", (char*)NULL);
(gdb) file b
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "b"? (y or n) y
Reading symbols from b...done.
(gdb) b main
Breakpoint 2 at 0x4004f5: file b.c, line 4.
(gdb) n

Breakpoint 2, main (argc=0, argv=0x7fffffffa570) at b.c:4
4               printf("%s\n", argv[1]);
(gdb) n
process 4877 is executing new program: /home/ciro/test/gdb-exec/b

Breakpoint 2, main (argc=3, argv=0x7fffffffa598) at b.c:4
4               printf("%s\n", argv[1]);
(gdb) n
ab
5               printf("%s\n", argv[2]);
(gdb) n
cd
6       }
(gdb) 

在运行文件之前,您只需确保到达 exec,可能需要使用 b execl,因为在此之后您将使用新文件中的符号。

在 Ubuntu 14.04,gdb 7.7.1 中进行了测试。


-1

在main()函数设一个断点,它也会在被执行程序的main()函数处中断。


-1

您可以通过利用gdb的远程调试功能,特别是 gdbserver 来实现此目标。实际上,使用 gdbserver 启动(P)。以下链接提供了更详细的信息:


gdbserver允许远程调试,但并不能解决手头的问题,这更多是关于GDB如何跟踪fork/clone的情况。 - Phillip Whelan
我认为这不是远程调试的问题。更多的是关于gdb在fork时跟随哪个进程的问题。 - Nathan Fellman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接