我正在从python开发C扩展,我得到了一些segfault(在开发过程中不可避免...)。我正在寻找一种方法来显示segfault发生在哪一行代码(一个想法就像跟踪每一行代码一样),我该怎么做呢?
u7up0aaq1#
如果您使用的是linux,请在gdb下运行python。
gdb python (gdb) run /path/to/script.py ## wait for segfault ## (gdb) backtrace ## stack trace of the c code
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当然,我可能希望将跟踪输出写入一个文件。)
dkqlctbz3#
C扩展中的segfault通常是由于在创建一个新的对象引用时没有增加引用计数造成的。这使得很难追踪到segfault,因为segfault只在从对象中删除最后一个引用之后发生,而且通常只在分配其他对象时发生。你不会说到目前为止你已经写了多少C扩展代码,但是如果你刚刚开始,考虑一下你是否可以使用ctypes或者Cython。Ctypes可能不够灵活,不能满足你的需要,但是你应该能够用Cython链接到几乎任何C库,并且自动维护所有的引用计数。这并不总是足够的:如果Python对象和任何底层C对象具有不同的生存期,您仍然会遇到问题,但它确实大大简化了事情。
ffscu2ro4#
我来这里是为了寻找同样问题的解决方案,但是其他的答案对我都没有帮助。真正有帮助的是faulthandler,你可以用pip install在Python 2.7中安装它。faulthandler只在Python 3.3版本中被引入,该版本发布于2012年9月,这是在大多数其他答案被写出来之后。
faulthandler
pip install
juud5qan5#
gdb有一些未公开的python扩展。从Python源代码中获取Tools/gdb/libpython.py(它不包括在正常安装中)。将此放入sys.path然后道:
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堆栈。注意事项:
--with-python
gdb
libpython
$PATH
libpython.py
如果无法将libpython.py复制到sys.path,则可以将其位置添加到sys.path,如下所示:
(gdb) python >import sys >sys.path.append('/path/to/containing/dir/') >import libpython > >end
这在python dev docs、the fedora wiki和the python wiki中的记录有些不完整如果您有一个较旧的gdb或只是无法使其正常工作,Python源代码中还有一个gdbinit,您可以将其复制到~/.gdbinit中,从而添加一些类似的功能
~/.gdbinit
798qvoo86#
以下是另外3种选择:
**1:**在启用错误处理程序的情况下执行脚本:
python3 -X faulthandler your_script.py
**2:**在调试模式下执行脚本(pdb)
python3 -m pdb your_script.py
并使用continue命令执行脚本。gdb工具提供了最多的信息,但是它们都没有打印我的脚本中最后执行的行号。
**3:**我最后使用了pytest。为了让它正常工作,我将代码封装在一个前缀为test_的函数中,并执行如下脚本:
test_
pytest your_script.py
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
我真希望我能早点偶然发现这个答案:)
7条答案
按热度按时间u7up0aaq1#
如果您使用的是linux,请在gdb下运行python。
mwkjh3gx2#
下面是一种输出Python代码运行的每一行的文件名和行号的方法:
输出量:
(You当然,我可能希望将跟踪输出写入一个文件。)
dkqlctbz3#
C扩展中的segfault通常是由于在创建一个新的对象引用时没有增加引用计数造成的。这使得很难追踪到segfault,因为segfault只在从对象中删除最后一个引用之后发生,而且通常只在分配其他对象时发生。
你不会说到目前为止你已经写了多少C扩展代码,但是如果你刚刚开始,考虑一下你是否可以使用ctypes或者Cython。Ctypes可能不够灵活,不能满足你的需要,但是你应该能够用Cython链接到几乎任何C库,并且自动维护所有的引用计数。
这并不总是足够的:如果Python对象和任何底层C对象具有不同的生存期,您仍然会遇到问题,但它确实大大简化了事情。
ffscu2ro4#
我来这里是为了寻找同样问题的解决方案,但是其他的答案对我都没有帮助。真正有帮助的是
faulthandler
,你可以用pip install
在Python 2.7中安装它。faulthandler
只在Python 3.3版本中被引入,该版本发布于2012年9月,这是在大多数其他答案被写出来之后。juud5qan5#
gdb有一些未公开的python扩展。
从Python源代码中获取
Tools/gdb/libpython.py
(它不包括在正常安装中)。将此放入
sys.path
然后道:
正如您所看到的,我们现在可以看到与CPython调用链相对应的Python堆栈。
注意事项:
--with-python
进行编译gdb
嵌入了python(通过链接到libpython
),它不会在subshell中运行它。这意味着它可能不一定与$PATH
上的python版本匹配。gdb
链接的任何Python源代码版本匹配的任何Python源代码版本下载libpython.py
。sys.path
以匹配您正在调试的代码。如果无法将
libpython.py
复制到sys.path
,则可以将其位置添加到sys.path
,如下所示:这在python dev docs、the fedora wiki和the python wiki中的记录有些不完整
如果您有一个较旧的
gdb
或只是无法使其正常工作,Python源代码中还有一个gdbinit,您可以将其复制到~/.gdbinit
中,从而添加一些类似的功能798qvoo86#
以下是另外3种选择:
**1:**在启用错误处理程序的情况下执行脚本:
**2:**在调试模式下执行脚本(pdb)
并使用continue命令执行脚本。
gdb
工具提供了最多的信息,但是它们都没有打印我的脚本中最后执行的行号。**3:**我最后使用了pytest。为了让它正常工作,我将代码封装在一个前缀为
test_
的函数中,并执行如下脚本:roqulrg37#
Mark的答案很棒,如果你的机器路径上有lldb,而不是gdb(我的例子),Mark的答案是:
我真希望我能早点偶然发现这个答案:)