GDB 是 linux 系统上常用的 c/c++ 调试工具,功能十分强大。对于较为复杂的系统,比如多进程系统,如何使用 GDB 调试呢?考虑下面这个三进程系统:

进程

Proc2 是 Proc1 的子进程,Proc3 又是 Proc2 的子进程。如何使用 GDB 调试 proc2 或者 proc3 呢?

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

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

follow-fork-mode

在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试。

follow-fork-mode的用法为:

set follow-fork-mode [parent|child]

  • parent: fork之后继续调试父进程,子进程不受影响。
  • child: fork之后调试子进程,父进程不受影响。

因此如果需要调试子进程,在启动gdb后:

(gdb) set follow-fork-mode child

并在子进程代码设置断点。

此外还有detach-on-fork参数,指示GDB在fork之后是否断开(detach)某个进程的调试,或者都交由GDB控制:

set detach-on-fork [on|off]

  • on: 断开调试follow-fork-mode指定的进程。
  • off: gdb将控制父进程和子进程。follow-fork-mode指定的进程将被调试,另一个进程置于暂停(suspended)状态。

注意,最好使用GDB 6.6或以上版本,如果你使用的是GDB6.4,就只有follow-fork-mode模式。

follow-fork-mode/detach-on-fork的使用还是比较简单的,但由于其系统内核/gdb版本限 制,我们只能在符合要求的系统上才能使用。而且,由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程 或曾孙进程的系统,例如上图3进程系统,调试起来并不方便。

Attach子进程

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

例如我们要调试某个进程RIM_Oracle_Agent.9i,首先得到该进程的pid

[root@tivf09 tianq]# ps -ef|grep RIM_Oracle_Agent.9inobody    6722  6721  0 05:57 ?        00:00:00 RIM_Oracle_Agent.9iroot      7541 27816  0 06:10 pts/3    00:00:00 grep -i rim_oracle_agent.9i

通过pstree可以看到,这是一个三进程系统,oserv是RIM_Oracle_prog的父进程,RIM_Oracle_prog又是RIM_Oracle_Agent.9i的父进程。

[root@tivf09 root]# pstree -H 6722

通过 pstree 察看进程

启动GDB,attach到该进程

用 GDB 连接进程

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

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

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

void debug_wait(char *tag_file){    while(1)    {        if (tag_file存在)            睡眠一段时间;        else            break;    }}

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

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

GDB wrapper

很多时候,父进程 fork 出子进程,子进程会紧接着调用 exec族函数来执行新的代码。对于这种情况,我们也可以使用gdb wrapper 方法。它的优点是不用添加额外代码。

其基本原理是以gdb调用待执行代码作为一个新的整体来被exec函数执行,使得待执行代码始终处于gdb的控制中,这样我们自然能够调试该子进程代码。

还是上面那个例子,RIM_Oracle_prog fork出子进程后将紧接着执行RIM_Oracle_Agent.9i的二进制代码文件。我们将该文件重命名为 RIM_Oracle_Agent.9i.binary,并新建一个名为RIM_Oracle_Agent.9i的shell脚本文件,其内容如下:

[root@tivf09 bin]# mv RIM_Oracle_Agent.9i RIM_Oracle_Agent.9i.binary[root@tivf09 bin]# cat RIM_Oracle_Agent.9i#!/bin/shgdb RIM_Oracle_Agent.binary

当fork的子进程执行名为RIM_Oracle_Agent.9i的文件时,gdb会被首先启动,使得要调试的代码处于gdb控制之下。

新的问题来了。子进程是在gdb的控制下了,但还是不能调试:如何与gdb交互呢?我们必须以某种方式启动gdb,以便能在某个窗口/终端与gdb交互。具体来说,可以使用xterm生成这个窗口。

xterm是X window系统下的模拟终端程序。比如我们在Linux桌面环境GNOME中敲入xterm命令:

xterm

就会跳出一个终端窗口:

终端

如果你是在一台远程linux服务器上调试,那么可以使用VNC(Virtual Network Computing) viewer从本地机器连接到服务器上使用xterm。在此之前,需要在你的本地机器上安装VNC viewer,在服务器上安装并启动VNC server。大多数linux发行版都预装了vnc-server软件包,所以我们可以直接运行vncserver命令。注意,第一次运行 vncserver时会提示输入密码,用作VNC viewer从客户端连接时的密码。可以在VNC server机器上使用vncpasswd命令修改密码。

[root@tivf09 root]# vncserver 

New 'tivf09:1 (root)' desktop is tivf09:1

Starting applications specified in /root/.vnc/xstartupLog file is /root/.vnc/tivf09:1.log

[root@tivf09 root]#[root@tivf09 root]# ps -ef|grep -i vncroot     19609     1  0 Jun05 ?        00:08:46 Xvnc :1 -desktop tivf09:1 (root)   -httpd /usr/share/vnc/classes -auth /root/.Xauthority -geometry 1024x768   -depth 16 -rfbwait 30000 -rfbauth /root/.vnc/passwd -rfbport 5901 -pnroot     19627     1  0 Jun05 ?        00:00:00 vncconfig -iconicroot     12714 10599  0 01:23 pts/0    00:00:00 grep -i vnc[root@tivf09 root]#

Vncserver是一个Perl脚本,用来启动Xvnc(X VNC server)。X client应用,比如xterm,VNC viewer都是和它通信的。如上所示,我们可以使用的DISPLAY值为tivf09:1。现在就可以从本地机器使用VNC viewer连接过去:

VNC viewer:输入服务器

输入密码:

VNC viewer:输入密码

登录成功,界面和服务器本地桌面上一样:

VNC viewer

下面我们来修改RIM_Oracle_Agent.9i脚本,使它看起来像下面这样:

#!/bin/shexport DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary

如果你的程序在exec的时候还传入了参数,可以改成:

#!/bin/shexport DISPLAY=tivf09:1.0; xterm -e gdb --args RIM_Oracle_Agent.binary $@ 

最后加上执行权限

[root@tivf09 bin]# chmod 755 RIM_Oracle_Agent.9i

现在就可以调试了。运行启动子进程的程序:

[root@tivf09 root]# wrimtest -l 9i_linuxResource Type  : RIMResource Label : 9i_linuxHost Name      : tivf09User Name      : mdstatusVendor         : OracleDatabase       : rimDatabase Home  : /data/oracle9i/920Server ID      : rimInstance Home  : Instance Name  : Opening Regular Session...

程序停住了。从VNC viewer中可以看到,一个新的gdb xterm窗口在服务器端打开了

gdb xterm 窗口

[root@tivf09 root]# ps -ef|grep gdbnobody   24312 24311  0 04:30 ?        00:00:00 xterm -e gdb RIM_Oracle_Agent.binarynobody   24314 24312  0 04:30 pts/2    00:00:00 gdb RIM_Oracle_Agent.binaryroot     24326 10599  0 04:30 pts/0    00:00:00 grep gdb

运行的正是要调试的程序。设置好断点,开始调试吧!

注意,下面的错误一般是权限的问题,使用 xhost 命令来修改权限:

xterm 错误

[root@tivf09 bin]# export DISPLAY=tivf09:1.0[root@tivf09 bin]# xhost +access control disabled, clients can connect from any host

xhost + 禁止了访问控制,从任何机器都可以连接过来。考虑到安全问题,你也可以使用xhost + <你的机器名>。

小结

上述三种方法各有特点和优劣,因此适应于不同的场合和环境:

  • follow-fork-mode方法:方便易用,对系统内核和GDB版本有限制,适合于较为简单的多进程系统
  • attach子进程方法:灵活强大,但需要添加额外代码,适合于各种复杂情况,特别是守护进程
  • GDB wrapper方法:专用于fork+exec模式,不用添加额外代码,但需要X环境支持(xterm/VNC)。

原文连接:http://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/index.html

转载于:https://www.cnblogs.com/leealways87/archive/2012/03/17/2403342.html

【转】gdb调试多进程程序相关推荐

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

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

  2. gdb调试多进程程序

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

  3. GDB调试多进程程序或同时调试多个程序

    转自:http://hellogcc.blogbus.com/logs/71170939.html 在以前,如果在GDB中想调试多进程的程序,需要在fork以前"set follow-for ...

  4. 使用gdb调试多进程程序、同时调试父进程和子进程

    参考: [1] GDB debugging multi-process programs [2] Debugging programs with multiple processes 根据这两篇参考链 ...

  5. GDB 调试多进程或者多线程应用

    GDB 是 linux 系统上常用的 c/c++ 调试工具, 功能十分强大. 对于较为复杂的系统, 比如多进程系统, 如何使用 GDB 调试呢? 考虑下面这个三进程系统 : 进程 ProcessChi ...

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

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

  7. 得到进程id_GDB调试多进程程序

    GDB调试器不只可以调试多线程程序,还可以调试多进程程序.对于 C 和 C++ 程序而言,多进程的实现往往借助的是头文件中的 fork() 函数或者 vfork() 函数.举个例子: #include ...

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

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

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

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

最新文章

  1. 无家可归的iPhone
  2. 华为的鸿蒙系统营销,全球第一款搭载华为鸿蒙系统的荣耀智慧屏终端在万众期待中正式上线销售...
  3. tableau可视化函数使用案例(六十七)-如何用Tableau获取数据并对数据进行操作?
  4. jackson 驼峰注解_jackson - jackson annotations注解详解
  5. C++描述杭电OJ 2005.第几天? ||
  6. CentOS6.8下安装memcached并设置开机自启动
  7. 移植RTT使用cubeMx配置后出现 cannot open source input file stm32f1xx_hal_exti.h: No such file or directory
  8. Java中的引用数据类型-BigDecimal
  9. ESP8266/ESP32 驱动ST7789屏幕
  10. flag计算机语言的意思,flag是什么意思-c语言flag的用法
  11. web结课作业的源码 HTML5+CSS大作业——个人博客-功能齐全(48页) html大学生网站开发实践作业
  12. Yuga Labs 收购 CryptoPunks 的始末
  13. 您无法登录 MSN Messenger
  14. 屏蔽 FutureWarning
  15. 17个学术论文搜索引擎
  16. Linux 条件变量详解
  17. Linux信号sigaction / signal
  18. HP-UX实战:扩展HP小机rx8640上的swap和dump空间
  19. P2PNet(代码阅读笔记)
  20. audio realtek 耳机没声_win7系统下realtek high definition audio声卡耳机无声怎么办

热门文章

  1. 【数据平台】pandas按条件去重
  2. MapReduce基础开发之五分布式下载ftp文件到本地再迁移到hdfs
  3. 模糊数学笔记:四、模糊矩阵与模糊关系
  4. 哈夫曼编码之大根堆小根堆揭西县
  5. 题目:三个售票员 卖出 30张票 || 多线程编程的企业级套路+模板||synchronized与Lock的区别
  6. Redis的常用命令——hash的常用命令
  7. C#上位机串口软件开发--第1讲 工程创建
  8. CTFshow php特性 web91
  9. CTFshow 命令执行 web121
  10. 第四周实践项目7 多项式求和