使用GDB调试CPython进程
本文运行环境基于的debian8, gdb7.7, python2.7
首先需要安装gdb和python-dbg:apt-get install gdb
、apt-get install python-dbg
或apt-get install python2.7-dbg
(CentOS使用yum install gdb
、yum install python-debuginfo
)
GDB附加到正在运行的进程上:gdb [EXECUTOR_NAME] -p [PID]
下载对应python版本的gdb工具 libpython.py
文件:https://github.com/python/cpython/blob/2.7/Tools/gdb/libpython.py
放到指定目录下,如/home/username
,然后在GDB中加载python工具:
(gdb) python
>import sys
>sys.path.insert(0, '/home/username')
>import libpython
>end
(gdb)
然后就可以使用py相关的指令,可以敲py然后按Tab键查看可用的python命令(py-bt, py-list, py_locals, py-print等):
实际线上环境可使用generate-core-file
指令生成coredump文件,然后对coredump文件进行调试gdb [EXECUTOR_NAME] core.*
,避免debug指令影响线上环境。
查看正在运行的线程,确定运行python的线程:info threads
,进程信息中包含有PyEval_EvalFrameEx
的就是了(如果没有,是无法定位调试的):
可以看到,打印它的是cpython源码中的 ceval.c 文件。
切换到python所在的线程:thread [N]
查看python堆栈信息:py-bt
或 py-bt-full
,
查看寄存器信息:info registers
查看所有局部变量:py-locals
打印指定变量:py-print [var_name]
注意:局部变量只能打印一层,无法通过.
或->
访问其它成员,如 py-print self._last_interval
或 py-print self->_last_interval
都是无效的,会告诉你找不到这个值。变量查找的顺序是先当前进程的局部(local)属性,再全局(global),再内置(builtin)。py-print __name__
和py-print int
分别查到的是全局属性和内置类型。
要查看某个无法显示的的值,如<float at remote 0x7fde3c0334e8>
,手动访问这个内存地址可能是目前惟一的方法。
每一个Python对象的指针都可以转换为PyObject* 。类似地,每一个大小不固定的Python对象指针可以cast为 PyVarObject* 。可以在 CPython源码 的Include文件夹下找到它们以及其他基本类型的定义(如floatobject.h定义了PyFloatObject
的结构包含一个PyObject
的头部和一个ob_fval
的属性)。根据具体类型,转换成对应的类指针,便可以访问其内部属性。
(gdb) p ((PyObject*)0x7fde3c0334e8)
$2 = <float at remote 0x7fde3c0334e8>
(gdb) p ((PyObject*)0x7fde3c0334e8)->ob_type
$3 = (struct _typeobject *) 0xafd180 <PyFloat_Type>
(gdb) p ((PyObject*)0x7fde3c0334e8)->ob_type->tp_name
$1 = 0x81d53f "float"
(gdb) p ((PyObject*)0x7fde3c0334e8)->ob_type->tp_as_number
$9 = (PyNumberMethods *) 0xafd340 <float_as_number>
(gdb) p ((PyFloatObject*)0x7fde3c0334e8)->ob_fval
$18 = 0.299999999999999999
显然这样的访问方式并不是很优雅,好在libpython.py
文件已经帮我们封装好了一部分方法,使得我们现在可以看到一些数据。虽然不知道为什么不支持float类型,但是我们可以仿照int类型的解析方法,手动改造自己的libpython.py,添加自己的解析的方法。
GDB启动时会在 用户根目录 或 当前目录 查找.gdbinit
文件,并将其中的内容作为gdb命令执行。如果不想每次debug都手动导入 libpython.py 的话,可以将命令写入.gdbinint
文件中,这样就不用每次手动输入了。
参考文章:
Debugging of CPython processes with gdb
[Python源码学习]之PyObject和PyTypeObject
https://devguide.python.org/gdb/
https://wiki.python.org/moin/DebuggingWithGdb
https://sourceware.org/gdb/onlinedocs/gdb/Commands-In-Python.html
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 using1174@foxmail.com
文章标题: 使用GDB调试CPython进程
文章字数: 828
本文作者: Jun
发布时间: 2018-12-17, 10:46:00
最后更新: 2019-01-24, 15:38:55
原始链接: http://yoursite.com/2018/12/17/使用GDB调试CPython/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。