我正在开发来自python广告的C扩展我获得了一些segfaults(在开发过程中不可避免的...)。python跟踪分段错误
我正在寻找一种方法来显示段错误发生在哪一行代码(一个想法就像跟踪每一行代码),我该怎么做?
我正在开发来自python广告的C扩展我获得了一些segfaults(在开发过程中不可避免的...)。python跟踪分段错误
我正在寻找一种方法来显示段错误发生在哪一行代码(一个想法就像跟踪每一行代码),我该怎么做?
这里有一个方法来输出的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
(你可能想要写跟踪输出当然是)。
如果你在linux上,在gdb下运行python
gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code
如果你已经有了一个核心文件,你可以使用'gdb python core'(或者调用核心文件)。如果你在OSX上,核心转储(默认不生成;参见'ulimit -c')存储在目录'/ cores'中。 – 2010-04-19 12:53:03
我真的希望这是我的第一个答案,因为阅读所有其他人花了相当多的时间我宁愿花我的错误。 – sage 2016-04-08 04:10:58
如果在运行像python -m unittest my.module.tests.mytest这样的Python单元测试时遇到段错误,那么'-m'开关会混淆'gdb'。这样调用'--args'选项:'gdb --args python -m unittest my.module.tests.mytest' – 2017-04-05 10:08:58
C扩展中的Segfaults很常见是在您创建对对象的新引用时不增加引用计数的结果。这使得他们非常难以追踪,因为只有在从对象中移除最后一个引用之后才发生段错误,并且即使这样,通常也只有在分配了其他对象时才会发生段错误。
你没有说你到目前为止写了多少C扩展代码,但如果你刚开始考虑是否可以使用ctypes或Cython。 Ctypes可能不够灵活以满足您的需求,但您应该能够链接到任何使用Cython的C库,并自动为您保留所有引用计数。这并不总是足够的:如果你的Python对象和任何底层C对象具有不同的生命周期,你仍然可以得到问题,但它确实可以大大简化事情。
另外,将NULL放在它们不属于的地方。 – 2015-04-22 15:39:26
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 ([email protected]=
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), [email protected]=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
编译嵌入的Python(通过链接到libpython
),它不运行它在一个子shell中。这意味着它可能不一定匹配$PATH
上的python版本。libpython.py
,该源代码与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 docs,the fedora wiki和the python wiki
这在一定程度上不良记录。如果你有旧的gdb
或者只是不能得到这个工作还有一个gdbinit在Python源代码,你可以复制到~/.gdbinit
w这些添加了一些类似的功能
我来这里寻找解决同一个问题,没有其他答案帮助我。有帮助的是faulthandler
,你可以使用pip install
将它安装在Python 2.7中。
faulthandler
仅在2012年9月发布的3.3版本中引入了Python,这是在编写大多数其他答案之后才发布的。
这是否适用于C扩展? – 2015-04-22 14:25:54
@MadPhysicist:它不会打印你C代码的行号,如果这就是你的意思。 :-)它将打印调用到C代码中的Python代码的行号。 – RichieHindle 2015-04-22 14:30:10
这就是我的意思。我发现原来的问题很有趣,因为我遇到了同样的问题。 segfault原来是因为我的C代码插入一个NULL元素到PyList_Object中。当我试图迭代列表时,它表现在Python方面。不确定在这种情况下python调试器会有多大帮助。 – 2015-04-22 15:37:46