使用GDB调试产生多进程的程序
如果一个进程fork了多个进程,这时使用GBD工具对程序进行调试会如何呢?
实际上,GDB 没有对多进程程序调试提供直接支持。例如,使用GDB调试某个进程,如果该进程fork了子进程,GDB会继续调试该进程,子进程会不受干扰地运行下去。
如果你事先在子进程代码里设定了断点,子进程会收到SIGTRAP信号,如果没有对此信号进行捕捉处理,就会按默认的处理方式处理——终止进程。
那么该如何调试子进程呢?有3种方法:
1.follow-fork-mode
在2.5.60版Linux内核及以后,GDB对使用fork/vfork创建子进程的程序提供了follow-fork-mode选项来支持多进程调试:
follow-fork-mode的用法为:
set follow-fork-mode [parent|child]
parent:调试父进程,子进程不受影响
child:调试子进程,父进程不受影响
我们需要调试子进程,所以在启动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)状态。
这两种模式支持需要GDB6.6及以上版本。由于follow-fork-mode的调试必然是从父进程开始的,对于fork多次,以至于出现孙进程或曾孙进程的系统,调试起来并不方便。
2.Attach子进程
GDB有附着(attach)到正在运行的进程的功能,即attach <pid>命令。因此我们可以利用该命令attach到子进程然后进行调试。
首先,要获得进程的ID,终端中使用命令:
ps -ef | grep YOUR_PROCESS_NAME
然后进入gdb使用命令
attach pid
现在可以调试了。但是如果子进程一直在运行,那么attach上之后就不知道运行到哪里去了。一个办法是,在刚刚进入子进程程序段时加入一段睡眠程序,即调用sleep函数,使子进程进入一定时间的睡眠状态,确保在子进程睡眠状态结束之前要attach上子进程,之后在子进程代码段设置断点,然后就可以对子进程进行调试了。
Attach进程方法还是很方便的,它能够应付各种各样复杂的进程系统,比如孙子/曾孙进程,比如守护进程(daemon process),唯一需要的就是加入一小段代码。
3.GDB wrapper
很多时候,父进程 fork 出子进程,子进程会紧接着调用 exec族函数来执行新的代码。对于这种情况,我们也可以使用gdb wrapper 方法。它的优点是不用添加额外代码。
其基本原理是以gdb调用待执行代码作为一个新的整体来被exec函数执行,使得待执行代码始终处于gdb的控制中,这样我们自然能够调试该子进程代码。
这个方法适合fork+exec模式,需要需要X环境支持(xterm/VNC)。
参考资料:
https://www.ibm.com/developerworks/cn/linux/l-cn-gdbmp/