gdb调试子进程

2023-10-31

GDB 是 linux 系统上常用的调试工具,本文介绍了使用 GDB 调试多进程程序的几种方法,并对各种方法进行比较。

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.9i
nobody 6722 6721 0 05:57 ? 00:00:00 RIM_Oracle_Agent.9i
root 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/sh
gdb 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/xstartup
Log file is /root/.vnc/tivf09:1.log

[root@tivf09 root]#
[root@tivf09 root]# ps -ef|grep -i vnc
root 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 -pn
root 19627 1 0 Jun05 ? 00:00:00 vncconfig -iconic
root 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/sh
export DISPLAY=tivf09:1.0; xterm -e gdb RIM_Oracle_Agent.binary

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

#!/bin/sh
export 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_linux
Resource Type : RIM
Resource Label : 9i_linux
Host Name : tivf09
User Name : mdstatus
Vendor : Oracle
Database : rim
Database Home : /data/oracle9i/920
Server ID : rim
Instance Home :
Instance Name :
Opening Regular Session...

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


gdb xterm 窗口

[root@tivf09 root]# ps -ef|grep gdb
nobody 24312 24311 0 04:30 ? 00:00:00 xterm -e gdb RIM_Oracle_Agent.binary
nobody 24314 24312 0 04:30 pts/2 00:00:00 gdb RIM_Oracle_Agent.binary
root 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)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

gdb调试子进程 的相关文章

随机推荐

  • 助力游戏开发者,看 Google 有哪些“上新”?

    Cloud Ace 是谷歌云全球战略合作伙伴 拥有 300 多名工程师 也是谷歌最高级别合作伙伴 多次获得 Google Cloud 合作伙伴奖 作为谷歌托管服务商 我们提供谷歌云 谷歌地图 谷歌办公套件 谷歌云认证培训服务 官网 http
  • docker: Error response from daemon: driver failed programming external connectivity on endpoint lamp

    Docker容器做端口映射报错 docker Error response from daemon driver failed programming external connectivity on endpoint lamp3 46b7
  • 【C语言精讲】——代码调试方法

    1 调试 调试 Debugging Debug 又称除错 是发现和减少计算机程序或电子仪器设备中程序 错误的一个过程 1 1 调试的基本步骤 发现程序错误的存在 以隔离 消除等方式对错误进行定位 确定错误产生的原因 提出纠正错误的解决办法
  • node.js 与 redis 与 express 和session协同工作

    var RedisStore require connect redis express var redis ip 192 168 238 135 redis port 6379 app use express session secret
  • 基于 Opencv python实现批量图片去黑边—裁剪—压缩软件

    简介 批量处理图片文件 批量提取GIF图片中的每一帧 具有范围裁剪 自动去除黑 白边 调整大小 压缩体积等功能 先看一些软件的界面 是基于Tkinter写的GUI 裁剪等功能基于Opencv 下载 我添加了处理GIF的github 原作者的
  • codeStriker(及与svn、bugzilla集成)使用过程记录 续

    下面是codeStriker与svn bugzilla集成使用的脚本 放在codestriker安装目录的bin下 usr bin perl w Create codestriker topic based on subversion co
  • linux后台开发必知的io优化知识总结

    尊重原创版权 https www hanzhangsy com hot 105435 html 更多内容参考 https www hanzhangsy com linux后台开发必知的io优化知识总结 系统学习 IO性能对于一个系统的影响是
  • lua堆栈

    首先了解下c 与lua之间的通信 假设在一个lua文件中有如下定义 hello lua 文件 myName beauty girl 请注意红色数字 代表通信顺序 1 C 想获取Lua的myName字符串的值 所以它把myName放到Lua堆
  • 计算机算法常用术语中英对照(分为两部分 其中一部分表格形式 )

    第一部分 Data Structures 基本数据结构 Dictionaries 字典 Priority Queues 堆 Graph Data Structures 图 Set Data Structures 集合 Kd Trees 线段
  • Python实现子线程代码“同时”执行完毕后,再执行后续函数模块

    在python中用多线程完成任务时 在后面添加了一个提醒函数 结果发现线程开启后提醒函数就触发了 解决方法 将多线程join 起来 就会跑完所有多线程任务再执行后续的函数模块 join 的使用方法 import threading impo
  • pgsql:获取分组中最大或最小的一条数据

    步骤1 查询并排序各分组的记录 sql的查询语句如下 SELECT tb curr read tb arch id ROW NUMBER OVER PARTITION by arch id ORDER BY year month desc
  • AWS实例3T磁盘初始化分区及docker环境部署

    文章目录 一 背景说明 二 磁盘分区 2 1 安装分区工具 2 2 查看是否存在数据盘 2 3 使用Parted工具为数据盘进行分区 2 4 系统重读分区表 2 5 为 dev vdb1分区创建文件系统 2 6 挂载磁盘 三 安装docke
  • Qt进行CSV文件操作

    Qt操作csv文件 csv文件简述 逗号分隔值 Comma Separated Values CSV 有时也称为字符分隔值 因为分隔字符也可以不是逗号 其文件以纯文本形式存储表格数据 数字和文本 纯文本意味着该文件是一个字符序列 不含必须像
  • X86指令:NOP指令

    NOP指令的作用 通常NOP用于对齐指令的地址 NOP指令的特性 因为NOP是X86指令中最短的 只有1byte 处理器执行NOP指令时 不进行任何的操作 不会影响系统的状态 NOP也有其他的用途 1 编程时如果使用了支持lable标记的编
  • javascript函数相关例题

    前言 虽然for也能实现一些简单的 重复操作 但是 比较具有局限性 我们js 里面 也有非常多的相同代码 可能需要大量重复使用 此时我们可以利用函数 一 函数是什么 函数 就是 封装了 一段 可被重复调用执行的 代码块 可以实现大量代码的重
  • 删除git在windows上的凭证

    由于本人安装git的客户端以及开始下载github上的项目代码 第一次输入的账户名以及密码错误 需要删除windows上自己保存的账号密码凭证 我自己的电脑配置 运用命令行打开控制面板也十分方便 快捷键 Win R 打开运行窗口 输入 co
  • vs2010 使用QT

    首先不要使用中文目录 1 下载Qt的安装包和VS2010的Qt插件 2 安装Qt SDK 3 安装Qt的VS开发插件 4 编译Qt Qt默认使用mingw进行编译 如果要使用VS2010开发 需要将Qt重新编译 进入开始菜单Microsof
  • 在VS8里面 fatal error C1083: 无法打开包括文件:“iostream.h”: No such file or directory

    fatal error C1083 无法打开包括文件
  • pointCloudLibrary点云库使用

    pointCloudLibrary点云库使用 准备 下载源码 https github com PointCloudLibrary pcl 这个是pointCloudLibrary 但不包括 Boost Eigen FLANN OpenNI
  • gdb调试子进程

    GDB 是 linux 系统上常用的调试工具 本文介绍了使用 GDB 调试多进程程序的几种方法 并对各种方法进行比较 GDB 是 linux 系统上常用的 c c 调试工具 功能十分强大 对于较为复杂的系统 比如多进程系统 如何使用 GDB