恭喜你,当你来到这个帖子, 说明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包地址:
https://dl.fedoraproject.org/pub/epel/7Server/x86_64/debug/Packages/p/python36-debuginfo-3.6.3-7.el7.x86_64.rpm
这个包是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
保存完成
6. 测试
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时出现的坑
1. ./configure --with-python="/path/to/python3.6" 后面的路径是指向python3.6 可执行文件的, 并且要包含
例如/usr/bin/python3.6
2. ./configure --with-python="/path/to/python3.6" --prefix="destination path" 有时 --prefix 会检测不到 目前还没知道怎么解决