在 Python 中使用调试器捕获段错误

Catching segfault with debugger in Python

我想调试一个经常卡死的Python程序

基本上,我的程序 运行 是一个接受 SOAP 请求的 spyne 服务器。我的程序是多线程的,有时我用来访问它的客户端会超时。

我已经尝试了几个调试器,如 PUDB、PDB、WINPDB、PYSTUCK,但我无法从它们中捕获任何异常,事实上它们也恰好被卡住了(CTRL+C 不起作用... )

我取得的最好成绩是使用以下命令从 GDB 获得的:

 gdb -ex r --args python myscript.py

GDB 设法捕获了异常,但没有显示任何有用的信息:

 Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffd7fff700 (LWP 22573)]
0x000000000057656d in PyEval_EvalCodeEx ()
(gdb) info threads 
  Id   Target Id         Frame 
* 15   Thread 0x7fffd7fff700 (LWP 22573) "python" 0x000000000057656d in PyEval_EvalCodeEx ()
  7    Thread 0x7fffecc2c700 (LWP 22277) "python" 0x00007ffff6998653 in select () at ../sysdeps/unix/syscall-template.S:82
  6    Thread 0x7fffed42d700 (LWP 22276) "python" 0x00007ffff6998653 in select () at ../sysdeps/unix/syscall-template.S:82
  5    Thread 0x7fffedc2e700 (LWP 22271) "python" sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:86
  4    Thread 0x7fffee42f700 (LWP 22270) "python" sem_wait () at ../nptl/sysdeps/unix/sysv/linux/x86_64/sem_wait.S:86
  3    Thread 0x7fffef9e8700 (LWP 22261) "python" 0x00007ffff6993933 in __GI___poll (fds=<optimized out>, nfds=<optimized out>, 
    timeout=<optimized out>) at ../sysdeps/unix/sysv/linux/poll.c:87
  2    Thread 0x7ffff613f700 (LWP 21988) "python" 0x00007ffff7bcc04d in accept () at ../sysdeps/unix/syscall-template.S:82
  1    Thread 0x7ffff7fd6700 (LWP 20970) "python" 0x00007ffff6998653 in select () at ../sysdeps/unix/syscall-template.S:82
(gdb) bt
#0  0x000000000057656d in PyEval_EvalCodeEx ()
#1  0x0000000000577ab0 in function_call.15039 ()
#2  0x00000000004d91b6 in PyObject_Call ()
#3  0x000000000054d8a5 in PyEval_EvalFrameEx ()
#4  0x000000000054c272 in PyEval_EvalFrameEx ()
#5  0x000000000054c272 in PyEval_EvalFrameEx ()
#6  0x000000000054c272 in PyEval_EvalFrameEx ()
#7  0x000000000054c272 in PyEval_EvalFrameEx ()
#8  0x000000000054c272 in PyEval_EvalFrameEx ()

我已经安装了软件包 python2.7-dbg 来启用命令 "py-bt" 但它不是更有用:

(gdb) py-bt 
#7 (unable to read python frame information)
#8 (unable to read python frame information)
#16 (unable to read python frame information)
#17 (unable to read python frame information)
#18 (unable to read python frame information)
#27 (unable to read python frame information)
#28 (unable to read python frame information)
#32 (unable to read python frame information)
#33 (unable to read python frame information)
#34 (unable to read python frame information)

我在某处读到这是因为 Python 没有调试符号然后我尝试了以下

 gdb -ex r --args python-dbg myscript.py

但它也不起作用,我什至无法 运行 程序,我有几个错误:

ImportError: /usr/lib/python2.7/dist-packages/lxml/etree.so: undefined symbol: Py_InitModule4_64
ImportError: /usr/lib/python2.7/dist-packages/apt_pkg.so: undefined symbol: Py_InitModule4_64

我运行别无选择....

关于我的程序的详细信息: Python: Python 2.7 OS: Ubuntu 12.04 服务器端框架:Spyne(前 SoapLib) 我还在我的程序中使用了 Pyro,这可能是造成这一切的原因。不过我已经在 Pyro 上禁用了多线程

你试过内置的gdb模块吗?我的意思是 python -m pdb myscript.py。最重要的是,您可以导入 gdb 并对一些断点进行硬编码。

我已经通过 运行

获得了更好的回溯
gdb -ex r --args python-dbg myscript.py

我已经通过使用 python-dbg 重新编译包 lxml 解决了符号问题(参见上文)。我在这样做时遇到了一些麻烦,但它最终按照以下步骤工作:

pip install lxml --download-cache myDir
# for newer pip, use : pip install lxml --download myDir --no-use-wheel
cd myDir
tar -xvf lxml-4.2.1.tar.gz
cd lxml-4.2.1
sudo apt-get install libxslt-dev
sudo apt-get install gcc
sudo apt-get install python-dev
sudo apt-get install python-dbg
sudo python-dbg setup.py install

以下post帮助很大: http://hustoknow.blogspot.fr/2013/06/why-your-python-program-cant-start-when.html

现在我只需要了解回溯:-)

我使用标准库中的 python 的 faulthandler 很难找到致命的 errors/segfaults。它将创建一个回溯(最近调用优先)显示当 python 进行转储时正在执行的代码行。

例如:

import faulthandler


with open("fault_handler.log", "w") as fobj:
    faulthandler.enable(fobj)
    your_function_to_debug()

它比标准的 python 回溯更受限制,但至少可以为您指明正确的方向。来自文档:

  • Only ASCII is supported. The backslashreplace error handler is used on encoding.

  • Each string is limited to 500 characters.

  • Only the filename, the function name and the line number are displayed. (no source code)

  • It is limited to 100 frames and 100 threads.

  • The order is reversed: the most recent call is shown first.