C语言 跟踪分段错误的python

klr1opcd  于 2022-12-02  发布在  Python
关注(0)|答案(7)|浏览(153)

我正在从python开发C扩展,我得到了一些segfault(在开发过程中不可避免...)。
我正在寻找一种方法来显示segfault发生在哪一行代码(一个想法就像跟踪每一行代码一样),我该怎么做呢?

u7up0aaq

u7up0aaq1#

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

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

mwkjh3gx2#

下面是一种输出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

(You当然,我可能希望将跟踪输出写入一个文件。)

dkqlctbz

dkqlctbz3#

C扩展中的segfault通常是由于在创建一个新的对象引用时没有增加引用计数造成的。这使得很难追踪到segfault,因为segfault只在从对象中删除最后一个引用之后发生,而且通常只在分配其他对象时发生。
你不会说到目前为止你已经写了多少C扩展代码,但是如果你刚刚开始,考虑一下你是否可以使用ctypes或者Cython。Ctypes可能不够灵活,不能满足你的需要,但是你应该能够用Cython链接到几乎任何C库,并且自动维护所有的引用计数。
这并不总是足够的:如果Python对象和任何底层C对象具有不同的生存期,您仍然会遇到问题,但它确实大大简化了事情。

ffscu2ro

ffscu2ro4#

我来这里是为了寻找同样问题的解决方案,但是其他的答案对我都没有帮助。真正有帮助的是faulthandler,你可以用pip install在Python 2.7中安装它。
faulthandler只在Python 3.3版本中被引入,该版本发布于2012年9月,这是在大多数其他答案被写出来之后。

juud5qan

juud5qan5#

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),它不会在subshell中运行它。这意味着它可能不一定与$PATH上的python版本匹配。
  • 您需要从与gdb链接的任何Python源代码版本匹配的任何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 dev docsthe fedora wikithe python wiki中的记录有些不完整
如果您有一个较旧的gdb或只是无法使其正常工作,Python源代码中还有一个gdbinit,您可以将其复制到~/.gdbinit中,从而添加一些类似的功能

798qvoo8

798qvoo86#

以下是另外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
roqulrg3

roqulrg37#

Mark的答案很棒,如果你的机器路径上有lldb,而不是gdb(我的例子),Mark的答案是:

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

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

相关问题