GDB 是 linux 下的调试利器, 在 c/c++ 程序开发过程中必不可少的这里总结一下多进程和多线程的调试方法和技巧
多进程的调试:
如下示例
- #include <sys/mman.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- static int glob = 23;
- void test()
- {
- int i = 0;
- printf("child pid: %d\n", getpid());
- while(1)
- {
- i++;
- sleep(3);
- printf("child running\n");
- }
- }
- int main()
- {
- int pid = fork();
- if(pid == 0)
- {
- test();
- }
- else if(pid> 0){
- printf("father pid : %d\n", getpid());
- int n = 0;
- while(1)
- {
- n++;
- sleep(4);
- printf("father running\n");
- }
- }
- wait(pid);
- return 0;
- }
编译 gcc -g process.c -o process -g 一定要加上, 否则没有调试信息
1. 如果我想要锁定子进程 / 父进程该怎样?
这里在 fork 之后就会产生子进程, 如果我们要锁定子进程或者父进程可以使用 set follow-fork-mode [parent|child] 来完成
- (gdb) set follow-fork-mode child
- (gdb) b 23
- Note: breakpoint 1 also set at pc 0x4006e4.
- Breakpoint 2 at 0x4006e4: file process.c, line 23.
- (gdb) info breakpoints
- Num Type Disp Enb Address What
- 1 breakpoint keep y 0x00000000004006e4 in main
- at process.c:23 inf 2, 1
- breakpoint already hit 1 time
- 2 breakpoint keep y 0x00000000004006e4 in main
- at process.c:23 inf 2, 1
- (gdb) delete breakpoints 2
- (gdb) r
Starting program: /home/cps / 桌面 / IPC/process
- Breakpoint 1, main () at process.c:23
- 23 int pid = fork();
- (gdb) n
- [New process 37322]
- father pid : 37321
- [Switching to process 37322]
- 24 if(pid == 0)
- (gdb) sparent running
- 26 test();
- (gdb) parent running
这里可以看到父进程一直在 running, 跟踪子进程并没有停止父进程 如果想要让父进程处于等待状态可以设置 set detach-on-fork [on | off]
- (gdb) set follow-fork-mode child
- (gdb) b 23
- Breakpoint 1 at 0x4006e4: file process.c, line 23.
- (gdb) set detach-on-fork off
- (gdb) r
Starting program: /home/cps / 桌面 / IPC/process
- Breakpoint 1, main () at process.c:23
- 23 int pid = fork();
- Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7.x86_64
- (gdb) n
- [New process 37548]
- child pid: 37548
- child running
- child running
- child running
- child running
可以看到父进程并没有执行, 而是暂停状态 只有子进程处于运行状态
2. 如何跟踪一个正在运行的进程?
这里就要说到 attach 一个进程, 可以使用 gdb -p pid execfilepath 来跟踪一个进程
- [cps@cps IPC]$ gdb -p 37682 process
- GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-94.el7
- Missing separate debuginfos, use: debuginfo-install glibc-2.17-157.el7.x86_64
- (gdb) bt
- #0 0x00007fb8d8fad650 in __nanosleep_nocancel () from /lib64/libc.so.6
- #1 0x00007fb8d8fad504 in sleep () from /lib64/libc.so.6
- #2 0x00000000004006d0 in test () at process.c:16
- #3 0x00000000004006fc in main () at process.c:26
- (gdb) print i
- No symbol "i" in current context.
- (gdb) frame 2
- #2 0x00000000004006d0 in test () at process.c:16
- 16 sleep(3);
- (gdb) print i
- $1 = 8
- (gdb)
这里的第一个 print i 并没有打印东西, 原因是没有进入堆栈, 我们进入 test 的堆栈后就可以查看变量同时在 gdb 中也可以 attach 到一个进程中 attach pid
3. 进程异常 crash 怎样查看?
这种情况下要打开 coredump, 使用命令 ulimit -c 1024 设置 coredump 开启最后将 dump 文件和可执行文件 一同加载到 gdb gdb coredump execfile 进入 gdb 后 执行 bt 和 where 查看出错的地方但是一般情况下的段错误用这种方法可很难查到一般做法就是一步一步的调试, 这种情况一般都是非法访问内存造成的, 在最有可能出错的地方打断点这种情况并没有较为直接的方法
多线程调试:
1. 查看当前进程中的所有线程
info threads 查看当前进程下的所有线程前面有 * 代表当前处于的线程
thread id 可以切换当前处于的线程, bt 查看线程的堆栈
2. 锁定一个线程
当我们在调试程序时, 若是想要调试某个线程, 程序在执行过程中容易在线程之间来回切换, 我们可以选择一个线程后可以锁定它
thread id 选定这个线程
set scheduler-locking on 可以用来锁定这个线程 只观察这个线程的运行情况 当锁定这个线程时, 其他线程就处于了暂停状态
3. 锁定一个线程, 让其他线程照常执行
锁定一个线程让其他线程照常运行, 这种用法在 gdb 7.0 以上的版本是支持的可以如下设置 gdb
- set target-async 1
- set pagination off
- set non-stop on
这里的几个命令要在程序运行之前运行这些
多进程和多线程的调试技巧还有很多, 这里只是说了一些常见的基本用法 至于其他的一些 gdb 用法可以查看 gdb help
来源: https://www.cnblogs.com/MaAce/p/8467182.html