摘要.
最近在研究 CPython,深入到 Python VM 需要大量借助 GDB 调试,但是问题在于,Python 的底层实现中,大量使用了 Python C API 和各种 Object,如果试图通过 GDB 指令查看 Object:
367 unicode = PyUnicode_FromWideChar(filename, wcslen(filename));
(gdb) n
368 if (unicode != NULL) {
(gdb) p *unicode
$3 = {ob_refcnt = 1, ob_type = 0x328880 <PyUnicode_Type>}
这是一个 unicode object,也就是 Python 中的 String,我们无法方便的查看该 String 对象保存的到底是什么数据,同样的,试图查看其他高级数据类型如 Dict、List 也非常不便。
# GDB + Python
GDB 从 7.0 版本之后,支持 Python 插件功能,如果注意观察 Python 源码编译目录,会发现存在一个 python-gdb.py 文件,我们可以在这个 GDB 插件的帮助下,轻松的调试 Python。你所需要的做的是,在 ~/.gdbinit 文件中增加:
add-auto-load-safe-path /your/python/source/root/dir
我的开发环境是树莓派,源码在 /home/pi/Python-3.8.3 目录下,那么按照上面的提示:
touch ~/.gdbinit
echo "add-auto-load-safe-path /home/pi/Python-3.8.3" >> ~/.gdbinit
此时,重新启动 GDB,尝试查看 CPython 中的对象:
(gdb) p unicode
$1 = 'test.py'
(gdb) p (*(PyUnicodeObject *)unicode)
$3 = {_base = {_base = {ob_base = {ob_refcnt = 1, ob_type = 0x328880 <PyUnicode_Type>}, length = 7,
hash = -1, state = {interned = 0, kind = 1, compact = 1, ascii = 1, ready = 1}, wstr = 0x0},
utf8_length = 1953719668, utf8 = 0x79702e <error: Cannot access memory at address 0x79702e>,
wstr_length = 0}, data = {any = 0x0, latin1 = 0x0, ucs2 = 0x0, ucs4 = 0x0}}
(gdb) p filename
$4 = 0x34dd70 L"test.py"
可以看到,GDB 已经可以正确的解析 CPython 中的对象了。
需要注意到,如果是 GDB 插件的输出,字符串两端是单引号(p unicode 的输出),如果是 GDB 原生输出,字符串两侧是双引号(p filename 的输出)。
关注公众号 “江川 Go”,了解程序员的烧脑日常。
https://zhuanlan.zhihu.com/p/223200719 https://zhuanlan.zhihu.com/p/223200719