简体   繁体   English

python 跟踪分段错误

[英]python tracing a segmentation fault

I'm developing C extensions from python and I obtain some segfaults (inevitable during the development...).我正在从 python 开发 C 扩展,我得到了一些段错误(在开发过程中不可避免......)。

I'm searching for a way to display at which line of code the segfault happens (an idea is like tracing every single line of code), how can I do that?我正在寻找一种方法来显示发生段错误的代码行(一个想法就像跟踪每一行代码),我该怎么做?

If you are on linux, run python under gdb如果您使用的是 linux,请在 gdb 下运行 python

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

Here's a way to output the filename and line number of every line of Python your code runs:这是输出代码运行的每一行 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()

Output:输出:

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

(You'd probably want to write the trace output to a file, of course.) (当然,您可能希望将跟踪输出写入文件。)

Segfaults from C extensions are very frequently a result of not incrementing a reference count when you create a new reference to an object. C 扩展中的段错误经常是由于在创建对对象的新引用时没有增加引用计数的结果。 That makes them very hard to track down as the segfault occurs only after the last reference is removed from the object, and even then often only when some other object is being allocated.这使得它们很难追踪,因为段错误仅在从对象中删除最后一个引用后才发生,而且通常只有在分配其他对象时才会发生。

You don't say how much C extension code you have written so far, but if you're just starting out consider whether you can use either ctypes or Cython .您没有说到目前为止您编写了多少 C 扩展代码,但如果您刚刚开始,请考虑是否可以使用 ctypes 或Cython Ctypes may not be flexible enough for your needs, but you should be able to link to just about any C library with Cython and have all the reference counts maintained for you automatically. Ctypes 可能不够灵活,无法满足您的需求,但您应该能够使用 Cython 链接到几乎任何 C 库,并自动为您维护所有引用计数。

That isn't always sufficient: if your Python objects and any underlying C objects have different lifetimes you can still get problems, but it does simplify things considerably.这并不总是足够的:如果您的 Python 对象和任何底层 C 对象具有不同的生命周期,您仍然会遇到问题,但它确实大大简化了事情。

I came here looking for a solution to the same problem, and none of the other answers helped me.我来到这里寻找同一问题的解决方案,但其他答案都没有帮助我。 What did help was faulthandler , and you can install it in Python 2.7 just using pip install .有什么帮助是faulthandler ,你可以在 Python 2.7 中安装它,只需使用pip install

faulthandler was introduced to Python only in version 3.3, that was released in September 2012, which was after most other answers here were written. faulthandler仅在 3.3 版中引入 Python,该版本于 2012 年 9 月发布,这是在编写此处的大多数其他答案之后。

There are somewhat undocumented python extensions for gdb. gdb 有一些未公开的 python 扩展。

From the Python source grab Tools/gdb/libpython.py (it is not included in a normal install).从 Python 源获取Tools/gdb/libpython.py (它不包含在正常安装中)。

Put this in sys.path把它放在sys.path

Then:然后:

# 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):

As you can see we now have visibility into the Python stack corresponding with the CPython call chain.如您所见,我们现在可以查看与 CPython 调用链对应的 Python 堆栈。

Some caveats:一些注意事项:

  • Your version of gdb needs to be greater than 7 and it needs to have been compiled with --with-python您的 gdb 版本需要大于 7,并且需要使用--with-python进行编译
  • gdb embeds python (by linking to libpython ), it doesn't run it in a subshell. gdb嵌入了 python(通过链接到libpython ),它不会在子shell 中运行它。 This means that It may not necessarily match the version of python that is on $PATH .这意味着它可能不一定与$PATH上的 python 版本匹配。
  • You need to download libpython.py from whatever version of the Python source that matches whatever gdb is linked to.您需要从与gdb链接到的任何版本相匹配的任何 Python 源版本下载libpython.py
  • You may have to run gdb as root - if so you may need to set up sys.path to match that of the code that you are debugging.您可能必须以 root 身份运行 gdb - 如果是这样,您可能需要设置sys.path以匹配您正在调试的代码的路径。

If you cannot copy libpython.py into sys.path then you can add it's location to sys.path like this:如果您无法将libpython.py复制到sys.path那么您可以将其位置添加到sys.path如下所示:

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

This is somewhat poorly documented in the python dev docs , the fedora wiki and the python wiki这在python 开发文档fedora wikipython wiki 中的记录很少。

If you have an older gdb or just can't get this working there is also a gdbinit in the Python source that you can copy to ~/.gdbinit which add some similar functionality如果您有一个较旧的gdb或无法使其正常工作,那么 Python 源中还有一个gdbinit ,您可以将其复制到~/.gdbinit ,它添加了一些类似的功能

Mark's answer is awesome.马克的回答很棒。 If you happen to be on a machine that has lldb readily available in your path, and not gdb (which was my case), Mark's answer becomes:如果你碰巧在一台机器上,而 lldb 在你的路径中可用,而不是 gdb(我的情况),马克的回答变成:

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

I wish I would have stumbled on this answer earlier:)我希望我能早点偶然发现这个答案:)

Here are 3 more alternatives:这里还有 3 个替代方案:

1: Executing a script with faulthandler enabled: 1:在启用故障处理程序的情况下执行脚本:

python3 -X faulthandler your_script.py

2: Executing a script in debug mode ( pdb ) 2:在调试模式下执行脚本( pdb

python3 -m pdb your_script.py

and execute the script with the continue command.并使用continue命令执行脚本。

The gdb tool provided the most information, but none of them print the last executed line number in my script. gdb工具提供了最多的信息,但它们都没有在我的脚本中打印最后执行的行号。

3: I ended up using pytest . 3:我最终使用了pytest For this to work, I wrapped my code in a function prefixed with test_ and execute the script like this:为此,我将代码包装在一个以test_为前缀的函数中,并像这样执行脚本:

pytest your_script.py

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

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