摘要.
恭喜你,当你来到这个帖子, 说明 pdb 的调试功能已经开始满足不了你。
Gdb 在 7.0+ 版本上已经支持 Python 程序的调试, 但是支持不代表说你系统自带的 gdb 已经可以顺利调试 python 程序,这还需要一些配置才能正确读取 python 的符号 和 额外的命令行脚本。
当运行 python 程序出现 core dump 的情况下,这时候是程序员最麻烦的时候, 看见 segmentation fault 就好比告诉你今晚你要加班的事实。众所周知, 目前安装的 python 是由 CPython 解释的。这个 CPython 解释器是用 C 语言实现的 。 所以在使用 gdb 中读取的 core 文件都是在 C 层面上的调试,看到一大串的 PyObject 的指针会让人无从下手。
俗话说的好, 工欲兴其事,必先利其器。 在参考了https://fedoraproject.org/wiki/Features/EasierPythonDebugging 和 http://podoliaka.org/2016/04/10/debugging-cpython-gdb/ 的帖子以后发现, 现在大部分帖子都是在 ubuntu 系统安装 pythonx.x-dgb 包实现对 python2.X 版本的调试,却很少帖子是针对 python3 的 centos 调试。 为了解决调试 python3 代码, 花了不少时间找资源和询问大腿方法。 这里特意感谢 Morgan 大腿的鼎力支持!
# 1. 条件说明
先明确一点, 目前来说调试 python 的条件比较苛刻, 只针对特定版本的 python 版本 和 centos 系统。
对于 centos6.x 系统, 目前已经开发出的 debuginfo 包是针对 python3.4 版本
对于 centos7.x 系统, 目前针对 python3.4 和 python3.6 版本
如果各位看官能在别的 RHEL 源里找到别的版本,请及时联系我 TAT
debuginfo 包地址:
这个包是 debug 符号, 针对 python3.6.3 版本
这些 debug 符号被 CPython 的脚本 用作分析 PyEval_EvalFrameEx frames(一个 frame 代表的函数调用和上下文环境, 例如 函数内部局部变量,cpu 寄存器),并且映射到应用层层面的 python 函数中。
# 2. 安装 Python3.6.3
**sudo yum -y groupinstall development ** ## 安装 python3.6 必备依赖
sudo yum -y install zlib-devel ## 安装 zlib 是为了防止后面 make 和 make install 出错
wget http://www.python.org/ftp/python/3.6.3/Python-3.6.3.tgz
tar -xzvf Python-3.6.3.tgz
cd Python-3.6.3
**./comfigure --prefix='/usr/local' **
## 这样安装的可执行文件 python3.6 位于 /usr/local/bin/python3.6
## 安装后的文件分布 /usr/local + /bin /lib /include /share
**make **
sudo make install
中间出错的话 观察报告是否缺少什么依赖的包 直接用 yum install 解决
# 3. 安装 epel 源
sudo yum -y install epel-release ## -y 表示自动选择 yes
**sudo yum makecache **
# 4. 指向 epel 源文件
**sudo vim /etc/yum.repos.d/epel.repo **
## 在这个文件的末尾添加下面的 epel 源 (一般源地址都带 repodata 文件夹)
[base-epel]
name=EPEL
baseurl=https://dl.fedoraproject.org/pub/epel/7Server/x86_64
gpgcheck=0
enabled=1
## 保存文件后退出 vim
sudo vim /etc/yum.repos.d/epel-debug.repo
[base-debuginfo-epel]
name=EPEL-DebugInfo
baseurl=https://dl.fedoraproject.org/pub/epel/7Server/x86_64/debug/
gpgcheck=0
enabled=1
# 4. 安装 debuginfo 包
yum install python36-debug && debuginfo-install python36-debug-3.6.3-7.el7.x86_64
# 5. 加载额外的 python scripts, 使 gdb 提供如 py-bt 的功能
到这一步, 帖主现在都不是很清楚 gdb 的 auto-load 功能是怎么设置的。所以用了别的方法导入。
该脚本在不同的系统有不同的名字: 如 pythonx.x-gdb.py or libpython.py
对于 centos 系统, 主要使用 libpython.py
首先明确一点,该文件适用于 Python2.x 和 Python 3.x 语法。
有一些帖子提及到 gdb 会绑定一个 python 版本解释器, 在 gdb 里输入
(gdb) py -V
或者
(gdb) python
>import sys
>print(sys.version)
>end
可以查询 gdb 当前绑定的 python 解释器版本
我个人的观点是, 无论 gdb 绑定的是什么版本, 只要能读取 libpython.py 即可。
所以没有必要把 gdb 卸载, 然后再手动编译安装 gdb(默认的 gdb 是绑定操作系统自带的 python, centos6 安装的是 Python2.6 版本 centos7 安装的是 Python2.7) 。
步骤如下:
A. 在你的系统中
** cd / && sudo find . -name "*libpython*"**
在这里会找到几个类似的文件:
** ../Tools/gdb/libpython.py**
** ../libpython3.6m.so-1.0-3.6.3-7.el7.x86_64.debug-gdb.py**
个人比较偏向于用下面的脚本,因为下面的脚本是由 debuginfo 包生成的
B. vim ~/.gdbinit
** source /path/to/libpython/py**
举例:
** source ../libpython3.6m.so-1.0-3.6.3-7.el7.x86_64.debug-gdb.py**
保存完成
- 测试
# A . 先写一个会产生段错误的代码: a.py
import sys
sys.setrecursionlimit(5000000)
def a():
** return a()** ## 无限递归使程序栈空间内存不够
a()
# B. 设置产生 core 文件
ulimit -c unlimited
ulimit -a ## 查看 core file size 大小
# C. 调用 debug 版本的解释器 运行 a.py 文件
python3.6-debug a.py
命令行出现
root @ localadmain: Segmentation Fault
# D. 调用 Gdb 调试 python3.6-debug 解释器
gdb python3.6-debug core.XXXXX
(gdb)py-bt
就可以看到经过符号翻译和转换的函数调用栈了。
未调用前:
这里是普通的 frame 0 调用栈, 可以看到 f = <error reading variable: cannot access memory at address)
这里的函数调用栈仍然在 C 代码中解释, 还没在 Python application 层面解释符号
**(gdb) py-bt **
调用 py-bt 时可以发现已经解释, Python 捕获到一个异常, 是 memory error
这里再次感谢 Morgan 大神的鼎力帮助!
# Appendix:
最后附上手动安装 gdb 时出现的坑
- ./configure --with-python="/path/to/python3.6" 后面的路径是指向 python3.6 可执行文件的, 并且要包含
例如 / usr/bin/python3.6
./configure --with-python="/path/to/python3.6" --prefix="destination path" 有时 --prefix 会检测不到 目前还没知道怎么解决
https://www.jianshu.com/p/1794c0fd94b6 https://www.jianshu.com/p/1794c0fd94b6