GDBlinux 系统上常用的 c/c++ 调试工具, 功能十分强大. 对于较为复杂的系统, 比如多进程系统, 如何使用 GDB 调试呢?

考虑下面这个三进程系统 :

进程 ProcessChildProcessParent 的子进程, ProcessParentThread 又是 ProcParent 的子线程. 如何使用 GDB 调试 子进程 ProcessChild 或者子线程 ProcessParentThread 呢 ?

实际上, GDB 没有对多进程程序调试提供直接支持. 例如, 使用 GDB 调试某个进程, 如果该进程 fork 了子进程, GDB 会继续调试该进程, 子进程会不受干扰地运行下去. 如果你事先在子进程代码里设定了断点, 子进程会收到SIGTRAP 信号并终止. 那么该如何调试子进程呢? 其实我们可以利用 GDB 的特点或者其他一些辅助手段来达到目的. 此外, GDB 也在较新内核上加入一些多进程调试支持.

接下来我们详细介绍几种方法, 分别是 follow-fork-mode 方法, attach 子进程方法和 GDB wrapper 方法.

1 follow-fork-mode方法


参考 gdb调试多进程和多线程命令

1.1 follow-fork-mode方法简介


默认设置下, 在调试多进程程序时 GDB 只会调试主进程. 但是 GDB > V7.0 支持多进程的分别以及同时调试, 换句话说, GDB 可以同时调试多个程序. 只需要设置 follow-fork-mode (默认值 parent) 和 detach-on-fork (默认值 on )即可.

follow-fork-mode detach-on-fork 说明
parent on 只调试主进程( GDB 默认)
child on 只调试子进程
parent off 同时调试两个进程, gdb 跟主进程, 子进程 blockfork 位置
child off 同时调试两个进程, gdb 跟子进程, 主进程 blockfork 位置

设置方法

set follow-fork-mode [parent|child]set detach-on-fork [on|off]
  • 1
  • 2
  • 3

查询正在调试的进程

info inferiors
  • 1

切换调试的进程

inferior <infer number>
  • 1

添加新的调试进程

add-inferior [-copies n] [-exec executable]
  • 1

可以用 file executable 来分配给 inferior 可执行文件.

其他 :

remove-inferiors infnodetach inferior
  • 1
  • 2
  • 3

GDB 默认支持调试多线程, 跟主线程, 子线程 blockcreate thread

查询线程

info threads
  • 1

切换调试线程

thread <thread number>
  • 1

1.2 示例程序例程

#include <stdio.h>
#include <pthread.h>#include <unistd.h>void processParent( );
void processChild( );void * processParentworker(void *arg);int main(int argc, const char *argv[])
{int pid;pid = fork( );if(pid != 0)    // fork return child pid in parent processprocessParent( );else        // fork return 0 in child processprocessChild( );return 0;
}void processParent( )
{pid_t pid = getpid();char prefix[] = "ProcessParent: ";//char tprefix[] = "thread ";int tstatus;pthread_t pt;printf("%s%d %s\n", prefix, pid, "step1");tstatus = pthread_create(&pt, NULL, processParentworker, NULL);if(tstatus != 0){printf("ProcessParent: Can not create new thread.");}processParentworker(NULL);sleep(1);
}void * processParentworker(void *arg)
{pid_t pid = getpid( );pthread_t tid = pthread_self( );char prefix[] = "ProcessParentThread: ";char tprefix[] = "thread ";printf("%s%d %s%ld %s\n", prefix, pid, tprefix, tid, "step2");printf("%s%d %s%ld %s\n", prefix, pid, tprefix, tid, "step3");return NULL;
}void processChild( )
{pid_t pid = getpid( );char prefix[] = "ProcessChild: ";printf("%s%d %s\n", prefix, pid, "step1");printf("%s%d %s\n", prefix, pid, "step2");printf("%s%d %s\n", prefix, pid, "step3");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

主程序 fork 出一个子进程, 然后父进程在执行的过程中通过 pthread_create 创建出一个子线程.

输出:

 ./test
ProcessParent: 7993 step1
ProcessChild: 7994 step1
ProcessChild: 7994 step2
ProcessChild: 7994 step3
ProcessParentThread: 7993 thread 140427861296960 step2
ProcessParentThread: 7993 thread 140427861296960 step3
ProcessParentThread: 7993 thread 140427853031168 step2
ProcessParentThread: 7993 thread 140427853031168 step3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1.3 调试


1.3.1 开始调试


首先调试主进程, block 子进程在 fork 的位置.

set follow-fork-mode parent
set detach-on-fork off
  • 1
  • 2
show follow-fork-mode
show detach-on-fork
  • 1
  • 2

接着在主进程 fork 的时候和三个进程(线程)的内部均设置断电. 下面分别跟踪三个进程

# 在主进程fork的时候设置断点
b 16
# 在主进程pthread_create的时候设置断点
b 36
# 在父进程执行的开始位置设置断点
b 28
# 在子进程执行的开始位置设置断点
b 48
# 在子线程执行的开始位置设置断点
b 61
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

1.3.2 调试父进程


首先开始调试父进程

# run 运行程序
r
  • 1
  • 2

进程停在了 fork 的位置

查看进程和线程的信息, 由于进程停在了 fork 的地址, 还没有创建子进程, 因此只有主进程

由于设置了 follow-fork-mode = parentdetach-on-fork = off. 因此在 fork 之后, gdb 进程会开始调试主进程, 而子进程会阻塞在 fork 之后的位置. 而我们在每个进程的执行函数路径的开始都打了断点, 那么我们继续执行, 进程将运行主程序, 走到 ProcessParent 的位置.

下面我们来验证一下

#继续程序的执行
c OR continue
#也可以next执行
next
  • 1
  • 2
  • 3
  • 4

可以看到程序停在了主进程 ProcessParent 的开始位置.

现在我们查看进程和线程的信息, 此时父进程(pid = 9766)创建了子进程(pid = 10036), gdb 目前正在调试父进程

info inferiors
info threads
  • 1
  • 2

1.3.3 调试子进程


下面我们开始跟进子进程, 之前通过 info inferiors/threads 来查看进程信息的时候可以看到, 进程的编号信息, 父进程(pid = 9766)编号为 1, 子进程(pid = 10036)编号为 2.

我们接着将 gdb attach 到子进程.

inferiors 2
  • 1

然后 continue 运行程序, 程序会停止在子进程 ProcessChild 的位置.

continue
  • 1

1.3.4 调试子线程


现在我们继续回到主线程然后待其创建子线程之后, 跟踪子线程.

由于在父进程的程序逻辑处 pthread_create 处设置了断点, 将会停在 pthread_create 的地方

接着往下执行, 这样子线程就会被创建, 然后我们查看进程和线程的信息

info inferiors
info threads
  • 1
  • 2

我们可以查看到 info inferiors 不能看到父进程创建的子线程, 但是 info threads 可以看到.

然后开始调试子线程

thread 3
  • 1

2 Attach子进程


https://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/

众所周知, GDB 有附着(attach)到正在运行的进程的功能, 即 attach <pid> 命令. 因此我们可以利用该命令 attach 到子进程然后进行调试.

例如我们要调试某个进程 process, 首先得到该进程的 pid

ps -ef | grep process
  • 1

然后可以通过 pstree 可以看到该进程下的线程信息

pstree -H pid
  • 1

启动 GDB attach 到该进程

现在就可以调试了. 一个新的问题是, 子进程一直在运行, attach 上去后都不知道运行到哪里了. 有没有办法解决呢?

一个办法是, 在要调试的子进程初始代码中, 比如 main 函数开始处, 加入一段特殊代码, 使子进程在某个条件成立时便循环睡眠等待, attach 到进程后在该代码段后设上断点, 再把成立的条件取消, 使代码可以继续执行下去.

至于这段代码所采用的条件, 看你的偏好了. 比如我们可以检查一个指定的环境变量的值, 或者检查一个特定的文件存不存在. 以文件为例, 其形式可以如下 :

void debug_wait(char *tag_file)
{while(1){if (tag_file存在)睡眠一段时间;elsebreak;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

attach 到进程后, 在该段代码之后设上断点, 再把该文件删除就 OK 了. 当然你也可以采用其他的条件或形式, 只要这个条件可以设置检测即可.

Attach 进程方法还是很方便的, 它能够应付各种各样复杂的进程系统, 比如孙子/曾孙进程, 比如守护进程(daemon process), 唯一需要的就是加入一小段代码.

3 gdb swapper


Debugging with GDB学习记录(二)

GDB调试及其调试脚本的使用

使用 GDB 调试多进程程序

  • 本作品/博文 ( AderStep-紫夜阑珊-青伶巷草 Copyright ©2013-2017 ), 由 成坚(gatieme) 创作,

GDB 调试多进程或者多线程应用相关推荐

  1. 使用gdb调试多进程和多线程

    GDB调试多进程 1. 默认设置下,在调试多进程程序时GDB只会调试主进程.但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,GDB可以同时调试多个程序.只需要设置follow-f ...

  2. gdb调试多进程和多线程命令 .

    1. 默认设置下,在调试多进程程序时GDB只会调试主进程.但是GDB(>V7.0)支持多进程的 分别以及同时 调试,换句话说,GDB可以同时调试多个程序.只需要设置follow-fork-mod ...

  3. gdb调试多进程和多线程命令

    1. 默认设置下,在调试多进程程序时GDB只会调试主进程.但是GDB(>V7.0)支持多进程的 分别以及同时 调试,换句话说,GDB可以同时调试多个程序.只需要设置follow-fork-mod ...

  4. gdb调试多进程和多线程

    ※进程 (1)默认设置下,在调试多进程程序时GDB只会调试主进程.但是GDB支持多进程的分别以及同时调试,也就是说,GDB可以同时调试多个程序.只需要设置follow-fork-mode和detach ...

  5. [Linux]gdb调试多进程多线程例程

    gdb相信学linux的同学已经比较熟悉了吧,它是linux下代码调试工具.我们在写c语言,c++的代码时经常会用到,它有一些常用的调试命令: run(r):运行程序,如果有断点在下一个断点处停止 s ...

  6. 使用 GDB 调试多进程程序

    使用 GDB 调试多进程程序 来源 https://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/index.html GDB 是 linux 系统上常 ...

  7. GDB调试多进程|多线程程序

    1. 默认设置下,在调试多进程程序时GDB只会调试主进程.但是GDB(>V7.0)支持多进程的分别以及同时调试,换句话说,GDB可以同时调试多个程序.只需要设置follow-fork-mode( ...

  8. linux中多进程调试,linux下用gdb调试多进程

    今天来学习一下linux下gdb如何调试多进程,在学习之前我我们能先看一张表: 这张表是gdb调试的命令表,这对那些对gdb不熟的同学来说是非常有必要的. 一.多进程调试的命令 1.set follo ...

  9. gdb调试多进程程序

        1.gdb下调试多进程程序只需要以下几条命令即可              除此之外还可以查看正在调试的进程 info inferiors, 同时也可以将当前正在调试的进程切换到另外一个进程中 ...

最新文章

  1. nginx产生【413 request entity too large】错误的原因与解决方法
  2. 自适应lasso_线性回归模型优化算法(Lasso)
  3. CIO客观评价SAP和用友的差异
  4. [MySQL]--gt;查询5天之内过生日的同事中的闰年2月29日问题的解决过程
  5. Linux 查看文件指定行数 内容
  6. WARNING: 997: Failure to setup sound, err = -50
  7. MySQL SQL error: #1271 - Illegal mix of collations for operation 'UNION'
  8. linux安装vmware没有网络,关于无桌面的linux安装VMWare Tools配置的教程
  9. Python基础-基本语法
  10. 阮一峰ES6入门读书笔记(十五):Class
  11. Java写个人博客,附超全教程文档
  12. Ubuntu下录制视频并在PPT上放映
  13. 均值漂移(Meanshift)算法
  14. 视频必备资源:免费音效素材下载
  15. yocto的hello world
  16. 搭建测试环境详细步骤
  17. 第一行代码-第二版(郭霖著)笔记十一(Material Design)
  18. 前后端分别实现集合根据中文拼音排序
  19. 中台搞了2年,项目叫停,CIO被裁!本以为中台是道送分题,没想到是送命题!...
  20. HEVC代码学习15:AMVP相关函数

热门文章

  1. 文件操作:fread()和fwrite()
  2. Leetcode 每日一题 40 组合2
  3. 几本国外著名反演书籍(高清版资源)
  4. 制作Python的安装模块
  5. 【云炬大学生创业基础笔记】第1章第3节 什么是创业的讨论
  6. 【考研保研直通车】C9高校考研真题
  7. [云炬商业计划书阅读分享]
  8. 3Dslicer2:数据与帮助
  9. 3Dslicer1:入门及基本控制
  10. 独立成分分析ICA系列5:信息极大化的 ICA 算法