python

2018-06-01 centos 7.x 版本下用gdb 调试 python3.6.3 解释器 - 简书

文章暂存

systemime
2020-11-22
8 min

摘要.

恭喜你,当你来到这个帖子, 说明 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](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**

保存完成

  1. 测试

# 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

  1. ./configure --with-python="/path/to/python3.6"  --prefix="destination path" 有时 --prefix 会检测不到  目前还没知道怎么解决

    https://www.jianshu.com/p/1794c0fd94b6 https://www.jianshu.com/p/1794c0fd94b6

上次编辑于: 5/20/2021, 7:26:49 AM