目录

GDB之4调试Python代码

GDB之(4)调试Python代码

GDB之(4)调试Python代码

Author:Once Day Date:2023年3月22日/2024年2月26日

漫漫长路,才刚刚开始…

全系列文章请查看专栏:

推荐参考文档:

文章目录

1. 概述

如果Python程序挂住了,想查看Python代码的栈,但是用GDB看到的是C栈,本文介绍使用gdb的python扩展来查看python代码栈。

一般而言,现在的ubuntu设备上,安装gdb时都会自带装上python的扩展工具:

onceday->~:# find /usr/ -name "gdb"
/usr/include/gdb
/usr/share/doc/gdb
/usr/share/gdb
/usr/share/gdb/python/gdb
/usr/share/bash-completion/completions/gdb
/usr/bin/gdb

如上面,搜索一下目录,看看是否有 /usr/share/gdb/python/gdb ,如果没有需要安装对应的工具,这里自带的是python3的调试扩展

对于Python2,需要安装对应的包扩展才行。

onceday->~:# apt search python-gdb
Sorting... Done
Full Text Search... Done
python-gdbm/jammy 2.7.18-1build1 amd64
  GNU dbm database support for Python2

python-gdbm-dbg/jammy 2.7.18-1build1 amd64
  GNU dbm database support for Python2 (debug extension)

如上所示,安装对应的python2-gdb扩展即可

2. 加载libpython文件

下载地址为:

下载文件需要在仓库分支那里选择对应当前版本的分支下载该文件,然后操作如下:

  • 下载到本地文件夹中。 可以放到系统路径去,这样后面不需要额外添加系统路径了

首先找到目标进程ID,或者coredump文件,皆可以:

onceday->~:# ps aux |grep gdb
ubuntu   1588276  0.0  0.1  16288  8844 pts/8    S+   21:58   0:00 python3 gdb_test.py
root     1588710  0.0  0.0   6476  2448 pts/9    S+   22:00   0:00 grep --color=auto gdb

然后使用 gdb python3 1588276 ,便可以attach到运行的进程上:

......
#10 0x000056527606a005 in _PyEval_EvalFrameDefault ()
No symbol table info available.
#11 0x00005652760813ac in _PyFunction_Vectorcall ()
No symbol table info available.
#12 0x000056527606a005 in _PyEval_EvalFrameDefault ()
No symbol table info available.
#13 0x00005652760813ac in _PyFunction_Vectorcall ()
No symbol table info available.
#14 0x000056527606a005 in _PyEval_EvalFrameDefault ()
No symbol table info available.
#15 0x0000565276066766 in ?? ()
......

直接使用bt full打出来的是上面的栈,(忽略符号表,重点是栈形式),这是基于C代码的角度

这个时候,可以手动加在python扩展, 部分发行版Linux的GDB集成了python扩展功能,那就无需手动导入了

(gdb) python
>import sys
>sys.path.insert(0,"/home/ubuntu/py-code")
>import libpython
>end
(gdb) py
py-bt               py-down             py-locals           py-up               python-interactive
py-bt-full          py-list             py-print            python        

sys.path.insert是把下载的libpython.py文件所在的文件夹加入系统路径中,如果已经放入系统路径,这一步就是多余的了

导入成功了,输入 py 然后tab,将看到下面几个命令,其含义和gdb中命令类似。

(gdb) py-bt
Traceback (most recent call first):
  (unable to read python frame information)
  (unable to read python frame information)
  (unable to read python frame information)

py-bt 是打印栈,但是如果报出上面的错误,无需担心,这是libpython版本和当前python解释器版本没对上的问题,因此重新下载对应版本的libpython.py文件即可

https://i-blog.csdnimg.cn/blog_migrate/0785e9d49b5cbef66e75adc6b01c0183.png#pic_center

如上图所示,选择对应的python版本即可,这里选择的应该是 3.10 版本

下面来看一下常见的命令效果:

py-bt打印栈的简略信息:

(gdb) py-bt
Traceback (most recent call first):
  File "/home/postgres/Connect_data.py", line 44, in deal_type_value
    data = (int(random.random() * 20000), int(random.random() * 1000000))
  File "/home/postgres/Connect_data.py", line 159, in <module>
    deal_type_value(f1, f2, start_time_sec)

py-bt-full打印栈的详细信息

(gdb) py-bt-full
#0 Frame 0xffffb85ed640, for file /home/postgres/Connect_data.py, line 44, in deal_type_value (f1=<_io.TextIOWrapper at remote 0xffffb8622860>, f2=<_io.TextIOWrapper at remote 0xffffb8622ee0>, now_time=1704118932, d_time=80532)
    data = (int(random.random() * 20000), int(random.random() * 1000000))
#5 Frame 0x2302d680, for file /home/postgres/Connect_data.py, line 159, in <module> ()
    deal_type_value

py-up和py-down上下移动栈的位置py-list 显示当前附近的源码:

(gdb) py-list 
  39    
  40    def deal_type_value(f1, f2, now_time):
  41        global sava_start_time
  42        global data_sum
  43        d_time = now_time - sava_start_time
 >44        data = (int(random.random() * 20000), int(random.random() * 1000000))
  45        data_sum["3s"].append(data)
  46        data_sum["10s"].append(data)
  47        if d_time % 3 == 0:
  48            succ, conc = sum_data(data_sum["3s"])
  49            f1.write(f"{now_time}, 1, {succ}, {conc}\n")

py-locals打印当前栈的局部变量信息

(gdb) py-locals 
f1 = <_io.TextIOWrapper at remote 0xffffb8622860>
f2 = <_io.TextIOWrapper at remote 0xffffb8622ee0>
now_time = 1704118932
d_time = 80532

py-print可以打印指定的变量信息:

(gdb) py-print now_time
local 'now_time' = 1704118932
2.1 如果打印信息失败
(gdb) py-bt
Traceback (most recent call first):
  (unable to read python frame information)
  (unable to read python frame information)
  (unable to read python frame information)
  (unable to read python frame information)

上述输出表明 GDB 无法读取 Python 堆栈帧信息。这可能会出现在几种情况下:

  1. GDB 没有正确加载 Python 插件 :需要确保在开始追踪之前已经通过 gdb 命令行加载了 Python 插件,比如:

    (gdb) python
    >>> import sys
    >>> sys.path.append('/path/to/python/source')
    >>> from Tools import gdb
    >>> end

    这里的 ‘/path/to/python/source’ 是 Python 源代码的路径。这个路径需要替换为实际的路径。

  2. Python 可执行文件没有包含调试符号 :要在 GDB 中调试 Python,需要一个包含调试符号的 Python 可执行文件。在构建 Python 时,需要使用 --with-pydebug 选项来包含这些符号。如果使用的 Python 没有包含这些符号,可能需要重新构建 Python。

  3. GDB 版本不兼容 :不是所有版本的 GDB 都能正确地处理 Python 堆栈帧。你可能需要尝试升级或降级 GDB 来解决这个问题。

  4. Python 代码正在执行非 Python 代码 :如果 Python 代码正在执行一个 C 扩展或者其他非 Python 代码,GDB 可能无法显示 Python 堆栈帧。在这种情况下,可能需要等待代码回到 Python 域,或者在 C 代码中设置断点。

3.安装pip+pystack

使用下面命令安装 pip(“ensurepip” is a module in Python that provides support for bootstrapping the pip installer into an existing Python installation or virtual environment)

pip -m ensurepip
pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple pystack-debugger
ps -ef | grep yams
pystack <python-pid>

https://i-blog.csdnimg.cn/blog_migrate/027004482f3a3ce3cf6063b863ab8f98.png#pic_center

Once Day

也信美人终作土,不堪幽梦太匆匆……

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~