
最近在解程序cordump的问题,代码是C/C++与python混合部署的,gdb的bt命令只能看到C/C++的调用栈,看不到python的调用栈。踩了一些坑,这里记录下最终的结果。下面都是在Python-3.11.12版本上实现的。python调用栈打印网上搜到的一些教程,是让下载对应python版本的libpython.py文件,然后在gdb里source一下,就有py-bt命令了,但是我执行了之后,py-bt命令并不生效。我在这里卡了几个小时,最后发现py-bt命令要生效有个大前提,就是python命令需要是debug版本!!!源码编译python debug版本创建build_python.sh:#!/bin/bashset-ewget--no-check-certificate https://www.python.org/ftp/python/3.11.12/Python-3.11.12.tgztar-xzfPython-3.11.12.tgzcdPython-3.11.12 ./configure --with-pydebug\--enable-shared\--prefix=/usr/local\CFLAGS="-g -O0 -fno-omit-frame-pointer"\LDFLAGS="-Wl,-rpath=/usr/local/lib"# --prefix= 这里要注意下,由于我这里是临时容器,所以我直接替换掉系统的python了。建议你安装到其他的目录,方便回退到系统自带的python。make-jmakeinstallldconfig使用py-bt打印python的调用栈写个简单的测试脚本test.py:importtime a=10deffunc_c():globala a=a+1b=5print("now in func_c")whileTrue:print("func_c is running")time.sleep(3)b=b+1print("func_c done")deffunc_b():globala a=a+1b=4print("now in func_b")func_c()print("func_b done")deffunc_a():globala a=a+1print("now in func_a")func_b()if__name__=="__main__":func_a()运行上面的脚本python test.py,使用gdb -p /pid/of/python test.py,然后在gdb命令窗口执行py-bt就能看到调用栈了。其中/workspace/python_gdb/libpython.py文件是从Python-3.11.12/Tools/gdb/libpython.py目录拷过来的。(gdb)source/workspace/python_gdb/libpython.py(gdb)py-bt Traceback(most recent call first): File"/workspace/test.py", line12,infunc_c time.sleep(3)File"/workspace/test.py", line21,infunc_b func_c()File"/workspace/test.py", line28,infunc_a func_b()File"/workspace/test.py", line31,inmodulefunc_a()(gdb)py-list8b=59print("now in func_c")10whileTrue:11print("func_c is running")12time.sleep(3)13b=b +114print("func_c done")1516def func_b():17global a18a=a +1(gdb)py-print blocal'b'=8libpython.py中的命令命令作用py-bt打印python调用栈py-bt-full打印所有线程python调用栈py-up调转到上一层python调用栈py-down调转到下一层python调用栈py-list打印当前python frame附近的代码行py-print打印变量值py-locals打印当前python frame的所有局部变量运行中的python代码打断点调试py-bt能成功运行后,我就想能动态调试正在运行中的python代码。这个功能对多进程、多机场景就特别有用,尤其那种拉起个服务耗时很久的场景,能节约调试时间。原来是有个py-break命令的PR合入请求的,但是最终社区没有合入这个PR,py-break这个命令就没有很好维护的地方,提到py-break命令的blog多是10年前的了,和当前python版本不匹配。最终我参照libpython.py写了个gdb自定义命令py-break,后面又加了py-next命令,都保存在py_break.py文件中:#!/usr/bin/env python3# -*- coding: utf-8 -*-""" GDB commands for Python source-level debugging (pydebug CPython): py-break file:lineno — break on a Python source line py-next — step to next Python line (step over calls) IMPORTANT --------- These hooks use `maybe_call_line_trace`, which only runs when line tracing is on. Start the inferior with: PYTHON_GDB_LINEBREAK=1 gdb --args /usr/local/bin/python3.11d your.py (sitecustomize installs a no-op sys.settrace when that env var is set.) Usage: (gdb) py-break test.py:13 (gdb) run (gdb) py-next """from__future__importprint_functionimportosimportreimportsysimportgdb _GDB_PYTHON_DIR="/workspace/python_gdb"if_GDB_PYTHON_DIRnotinsys.path:sys.path.insert(0,_GDB_PYTHON_DIR)def_filename_match(expected,actual):ifnotactual:returnFalseifexpected==actual:returnTrueifos.path.basename(actual)==os