繁体   English   中英

python 跟踪分段错误

[英]python tracing a segmentation fault

我正在从 python 开发 C 扩展,我得到了一些段错误(在开发过程中不可避免......)。

我正在寻找一种方法来显示发生段错误的代码行(一个想法就像跟踪每一行代码),我该怎么做?

如果您使用的是 linux,请在 gdb 下运行 python

gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code

这是输出代码运行的每一行 Python 的文件名和行号的方法:

import sys

def trace(frame, event, arg):
    print("%s, %s:%d" % (event, frame.f_code.co_filename, frame.f_lineno))
    return trace

def test():
    print("Line 8")
    print("Line 9")

sys.settrace(trace)
test()

输出:

call, test.py:7
line, test.py:8
Line 8
line, test.py:9
Line 9
return, test.py:9

(当然,您可能希望将跟踪输出写入文件。)

C 扩展中的段错误经常是由于在创建对对象的新引用时没有增加引用计数的结果。 这使得它们很难追踪,因为段错误仅在从对象中删除最后一个引用后才发生,而且通常只有在分配其他对象时才会发生。

您没有说到目前为止您编写了多少 C 扩展代码,但如果您刚刚开始,请考虑是否可以使用 ctypes 或Cython Ctypes 可能不够灵活,无法满足您的需求,但您应该能够使用 Cython 链接到几乎任何 C 库,并自动为您维护所有引用计数。

这并不总是足够的:如果您的 Python 对象和任何底层 C 对象具有不同的生命周期,您仍然会遇到问题,但它确实大大简化了事情。

我来到这里寻找同一问题的解决方案,但其他答案都没有帮助我。 有什么帮助是faulthandler ,你可以在 Python 2.7 中安装它,只需使用pip install

faulthandler仅在 3.3 版中引入 Python,该版本于 2012 年 9 月发布,这是在编写此处的大多数其他答案之后。

gdb 有一些未公开的 python 扩展。

从 Python 源获取Tools/gdb/libpython.py (它不包含在正常安装中)。

把它放在sys.path

然后:

# gdb /gps/python2.7_x64/bin/python coredump
...
Core was generated by `/usr/bin/python script.py'.
Program terminated with signal 11, Segmentation fault.
#0  call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
...
(gdb) python
>import libpython
>
>end
(gdb) bt
#0  call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037
#1  PyEval_EvalFrameEx (f=f@entry=
    Frame 0x7f9084d20ad0, 
    for file /usr/lib/python2.7/site-packages/librabbitmq/__init__.py, line 220, 
    in drain_events (self=<Connection(channels={1: <Channel(channel_id=1, connection=<...>, is_open=True, connect_timeout=4, _default_channel=<....(truncated), throwflag=throwflag@entry=0) at Python/ceval.c:2681
...
(gdb) py-list
 218            else:
 219                timeout = float(timeout)
>220            self._basic_recv(timeout)
 221
 222        def channel(self, channel_id=None):

如您所见,我们现在可以查看与 CPython 调用链对应的 Python 堆栈。

一些注意事项:

  • 您的 gdb 版本需要大于 7,并且需要使用--with-python进行编译
  • gdb嵌入了 python(通过链接到libpython ),它不会在子shell 中运行它。 这意味着它可能不一定与$PATH上的 python 版本匹配。
  • 您需要从与gdb链接到的任何版本相匹配的任何 Python 源版本下载libpython.py
  • 您可能必须以 root 身份运行 gdb - 如果是这样,您可能需要设置sys.path以匹配您正在调试的代码的路径。

如果您无法将libpython.py复制到sys.path那么您可以将其位置添加到sys.path如下所示:

(gdb) python
>import sys
>sys.path.append('/path/to/containing/dir/')
>import libpython
>
>end

这在python 开发文档fedora wikipython wiki 中的记录很少。

如果您有一个较旧的gdb或无法使其正常工作,那么 Python 源中还有一个gdbinit ,您可以将其复制到~/.gdbinit ,它添加了一些类似的功能

马克的回答很棒。 如果你碰巧在一台机器上,而 lldb 在你的路径中可用,而不是 gdb(我的情况),马克的回答变成:

lldb python
(lldb) process launch -- /path/to/script.py
## wait for segfault ##
(lldb) bt
## stack trace of the c code

我希望我能早点偶然发现这个答案:)

这里还有 3 个替代方案:

1:在启用故障处理程序的情况下执行脚本:

python3 -X faulthandler your_script.py

2:在调试模式下执行脚本( pdb

python3 -m pdb your_script.py

并使用continue命令执行脚本。

gdb工具提供了最多的信息,但它们都没有在我的脚本中打印最后执行的行号。

3:我最终使用了pytest 为此,我将代码包装在一个以test_为前缀的函数中,并像这样执行脚本:

pytest your_script.py

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM