GDB调试多进程的命令介绍和演示
命令汇总
命令 | 作用 |
---|---|
info inferiors | 查看所有进程 |
inferiors 2 | 切换到编号为2的进程 |
detach inferiors 2 | detach掉编号为2的进程 |
kill inferiors 2 | kill掉编号为2的进程 |
set follow-fork-mode parent | 只调试父进程(GDB默认) |
set follow-fork-mode child | 只调试子进程 |
show follow-fork-mode | 查看follow-fork-mode当前值 |
set detach-on-fork on | 只调试一个进程,父进程或子进程(GDB默认) |
set detach-on-fork off | 同时调试父子进程,另一个进程阻塞在fork位置 |
show detach-on-fork | 查看detach-on-fork当前值 |
set schedule-multiple off | 只有当前进程会执行,其他进程挂起(GDB默认) |
set schedule-multiple on | 所有的进程都会正常执行 |
show schedule-multiple | 查看schedule-multiple当前值 |
测试用例
/*================================================================
* Author: LiuHanxu
* Date: 2022-12-23
* Description: how to debug multi-process by gdb
================================================================*/
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
pid_t pid = fork();
if(pid == 0) { // 子进程
printf("I am child, my pid = %d, my parent pid = %d\n", getpid(), getppid());
} else if(pid > 0) { // 父进程
printf("I am parent, my pid = %d, my child pid = %d\n", getpid(), pid);
wait(NULL); // 等待子进程退出
} else {
perror("fork error!\n");
return -1;
}
return 0;
}
测试用例中一个是父进程,另一个是通过系统调用fork()创建的子进程。当fork()返回值为0说明当前在子进程里;返回值大于0说明在父进程里,此时返回值表示子进程的pid;返回值小于0说明fork时发生错误。
在默认情况下,在多进程程序中,GDB只调试主进程,也就是说无论程序调用了多少次fork()函数创建了多少个子进程,GDB在默认情况下,只调试父进程。
lhx@ubuntu:~/paopao-notes/code$ gdb ./test_multi_process
Reading symbols from ./test_multi_process...
(gdb) run
Starting program: /home/lhx/paopao-notes/code/test_multi_process
[Detaching after fork from child process 92666]
I am parent, my pid = 92663, my child pid = 92666
I am child, my pid = 92666, my parent pid = 92663
[Inferior 1 (process 92663) exited normally]
(gdb)
其中[Detaching after fork from child process xxx]则说明在fork出子进程后就释放了,只跟踪父进程。
进程操作命令inferiors
info inferiors
查看所有进程,带*号表示当前进程。
inferiors 2
切换到编号为2的进程。
detach inferiors 2
detach掉编号为2的进程,注意这个进程还存在,可以再次用run命令执行它。
kill inferiors 2
kill掉编号为2的进程,注意这个进程还存在,可以再次用run命令执行它。
Thread 1.1 "test_multi_proc" hit Breakpoint 1, main () at test_multi_process.c:16
16 printf("I am parent, my pid = %d, my child pid = %d\n", getpid(), pid);
(gdb) i inferiors
Num Description Connection Executable
* 1 process 92828 1 (native) /home/lhx/paopao-notes/code/test_multi_process
2 process 92831 1 (native) /home/lhx/paopao-notes/code/test_multi_process
(gdb) inferior 2
[Switching to inferior 2 [process 92831] (/home/lhx/paopao-notes/code/test_multi_process)]
[Switching to thread 2.1 (process 92831)]
#0 0x00007ffff7e9cf3f in fork () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) n
Single stepping until exit from function fork,
which has no line number information.
main () at test_multi_process.c:13
13 if(pid == 0) { // 子进程
(gdb) n
14 printf("I am child, my pid = %d, my parent pid = %d\n", getpid(), getppid());
(gdb) i inferiors
Num Description Connection Executable
1 process 92828 1 (native) /home/lhx/paopao-notes/code/test_multi_process
* 2 process 92831 1 (native) /home/lhx/paopao-notes/code/test_multi_process
(gdb) kill inferiors 1
(gdb) i inferiors
Num Description Connection Executable
1 <null> /home/lhx/paopao-notes/code/test_multi_process
* 2 process 92831 1 (native) /home/lhx/paopao-notes/code/test_multi_process
(gdb) detach inferiors 2
Detaching from program: /home/lhx/paopao-notes/code/test_multi_process, process 92831
[Inferior 2 (process 92831) detached]
I am child, my pid = 92831, my parent pid = 1
(gdb) i inferiors
Num Description Connection Executable
1 <null> /home/lhx/paopao-notes/code/test_multi_process
* 2 <null> /home/lhx/paopao-notes/code/test_multi_process
(gdb)
只调试子进程set follow-fork-mode child
set follow-fork-mode parent
只调试父进程,子进程继续运行(GDB默认)。
set follow-fork-mode child
只调试子进程,父进程继续运行。
show follow-fork-mode
查看follow-fork-mode当前值
lhx@ubuntu:~/paopao-notes/code$ gdb ./test_multi_process
Reading symbols from ./test_multi_process...
(gdb) show follow-fork-mode
Debugger response to a program call of fork or vfork is "parent".
(gdb) list 14
9
10 int main()
11 {
12 pid_t pid = fork();
13 if(pid == 0) { // 子进程
14 printf("I am child, my pid = %d, my parent pid = %d\n", getpid(), getppid());
15 } else if(pid > 0) { // 父进程
16 printf("I am parent, my pid = %d, my child pid = %d\n", getpid(), pid);
17 wait(NULL); // 等待子进程退出
18 } else {
(gdb) b 14
Breakpoint 1 at 0x1204: file test_multi_process.c, line 14.
(gdb) r
Starting program: /home/lhx/paopao-notes/code/test_multi_process
[Detaching after fork from child process 92674]
I am parent, my pid = 92671, my child pid = 92674
I am child, my pid = 92674, my parent pid = 92671
[Inferior 1 (process 92671) exited normally]
(gdb)
(gdb) set follow-fork-mode child
(gdb) r
Starting program: /home/lhx/paopao-notes/code/test_multi_process
[Attaching after process 92675 fork to child process 92676]
[New inferior 2 (process 92676)]
[Detaching after fork from parent process 92675]
[Inferior 1 (process 92675) detached]
I am parent, my pid = 92675, my child pid = 92676
[Switching to process 92676]
Thread 2.1 "test_multi_proc" hit Breakpoint 1, main () at test_multi_process.c:14
14 printf("I am child, my pid = %d, my parent pid = %d\n", getpid(), getppid());
(gdb) n
I am child, my pid = 92676, my parent pid = 92675
22 return 0;
(gdb)
通过上面测试发现,当保持gdb默认值时,只调试父进程。当在14行子进程里设断点后,发现程序不会停住。将follow-fork-mode设为child后,父进程继续执行,而此时子进程能够停在断点处。
同时调试父子进程set detach-on-fork off
set detach-on-fork on
只调试一个进程,可以是父进程或子进程(GDB默认)。
set detach-on-fork off
同时调试父子进程,如果follow-fork-mode是parent,则gdb跟踪父进程,子进程阻塞在fork位置。如果follow-fork-mode是child,则gdb跟踪子进程,父进程阻塞在fork位置。此时用户可以根据调试情况在父进程和子进程之间来回切换调试。
show detach-on-fork
查看detach-on-fork当前值。
lhx@ubuntu:~/paopao-notes/code$ gdb ./test_multi_process
Reading symbols from ./test_multi_process...
(gdb) set follow-fork-mode child
(gdb) set detach-on-fork off
(gdb) b 14
Breakpoint 1 at 0x1204: file test_multi_process.c, line 14.
(gdb) b 16
Breakpoint 2 at 0x122d: file test_multi_process.c, line 16.
(gdb) r
Starting program: /home/lhx/paopao-notes/code/test_multi_process
[Attaching after process 92736 fork to child process 92739]
[New inferior 2 (process 92739)]
[Switching to process 92739]
Thread 2.1 "test_multi_proc" hit Breakpoint 1, main () at test_multi_process.c:14
14 printf("I am child, my pid = %d, my parent pid = %d\n", getpid(), getppid());
(gdb) n
I am child, my pid = 92739, my parent pid = 92736
22 return 0;
(gdb) i inferiors
Num Description Connection Executable
1 process 92736 1 (native) /home/lhx/paopao-notes/code/test_multi_process
* 2 process 92739 1 (native) /home/lhx/paopao-notes/code/test_multi_process
(gdb) inferior 1
[Switching to inferior 1 [process 92736] (/home/lhx/paopao-notes/code/test_multi_process)]
[Switching to thread 1.1 (process 92736)]
#0 0x00007ffff7e9cf3f in fork () from /lib/x86_64-linux-gnu/libc.so.6
(gdb) c
Continuing.
Thread 1.1 "test_multi_proc" hit Breakpoint 2, main () at test_multi_process.c:16
16 printf("I am parent, my pid = %d, my child pid = %d\n", getpid(), pid);
(gdb) n
I am parent, my pid = 92736, my child pid = 92739
17 wait(NULL); // 等待子进程退出
(gdb)
上面测试用例中,通过set follow-fork-mode child
和set detach-on-fork off
设置为gdb跟踪子进程,父进程阻塞在fork位置。然后切换到父进程,继续运行父进程。
所有进程同步执行set schedule-multiple on
在调试多进程时,默认情况下,除了当前调试的进程,其他进程都处于挂起状态,所以,如果需要在调试当前进程的时候,其他进程也能正常执行,那么通过设置set schedule-multiple on
即可。
set schedule-multiple off
gdb发出执行命令后,只有当前进程会执行,其他进程挂起(GDB默认)。
set schedule-multiple on
当gdb发出执行命令后,所有的进程都会正常执行。
show schedule-multiple
查看schedule-multiple当前值。