对于某些多进程的应用,子进程是有主进程拉起来的,此时想对子进程的启动流程打断点就比较麻烦。如果在子进程启动时调试器自动挂载就完美了。网上找到了gdbhelpers的开源工具库,其对gdb的现有命令集做了扩展,有一个工具是实现预挂载的,原理也比较简单,就是对系统接口加探测回调,然后做进程名称的匹配,若匹中自动调用GDB的attach命令,挂载到该进程。 源码路径如下:
GitHub - tromey/gdb-helpers: GDB helper scripts
下载解压后,放到某个路径下,最好是跟其他命令放在同路径。进入源码路径,切换到MakeFile所在文件夹,执行如下命令:
make hack-gdbinit
执行后,会做两件事:
1) 生成gdb启动时执行的脚本文件load-helpers.py
2) 在$HOME路径下创建.gdbinit文件,并将1)中的文件路径写入到该文件中。
从而实现gdbhelpers中的扩展命令注册。注意,移动了gdbhelpers的文件夹路径会导致load-helpers.py文件找不到,扩展命令失效。此时,要删掉load-helpers.py及.gdbinit文件,退出gdb,再次执行上述步骤完成工具部署。
由于这个工具集依赖SystemTap实现接口捕获,所以先要用apt get命令装一下。否则找不到stap命令。此外,扩展命令都是Python实现的,由于python3.4到3.8的语法变化,preattach执行中会遇到字符串拼接的解析错误,此时只要改一下preattach.py中的代码实现:
# preattach
import gdb, re
import os
import subprocess
class Preattach(gdb.Command):
"""Attach to the next instance of a program.
Usage:
preattach BASENAME
This runs a SystemTap script that watches for the next time a program
that has the given basename is invoked. Then, a SIGSTOP is delivered
to that process, and gdb attaches to it."""
def __init__(self):
super(Preattach, self).__init__("preattach", gdb.COMMAND_RUNNING,
gdb.COMPLETE_NONE)
def invoke(self, arg, from_tty):
script = os.path.join(os.path.dirname(__file__), 'preattach.stp')
pid = subprocess.check_output(['stap', '-g', script, arg.strip()])
pids = re.findall(r"\d+", str(pid))
if pids:
gdb.execute('attach ' + pids[0], from_tty)
Preattach()
接下来是挂在过程:
1) sudo gdb启动调试器,一定要以root权限启动,否则stap命令执行报错;
2) set solib-search-path设置可执行文件的检索路径,break设置pending的断点;
3) preattach YOUR_PROCESS_NAME,注意是进程名字,不是绝对路径。
4) 启动程序,触发子进程加载。挂在后可能会收到多次SIG_STOP信号,导致进程执行中止,直接点c,继续执行就可以。顺利的话会命中刚设置的断点。
剩下的就是常规的调试命令了。