python跟踪分段错误
我正在从Python广告开发C扩展我得到一些segfaults(在开发过程中不可避免的…)。
我正在寻找一种方法来显示段错误发生在哪一行代码(一个想法就像跟踪每一行代码),我该怎么做?
以下是输出代码运行的每一行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
C扩展中的Segfaults通常是在您创build对对象的新引用时不增加引用计数的结果。 这使得他们非常难以追踪,因为只有在最后一个引用从对象中移除之后才会发生段错误,即使这样,通常也只有当其他对象被分配时。
到目前为止,你不知道你写了多lessC扩展代码,但是如果你刚开始考虑是否可以使用ctypes或者Cython 。 Ctypes可能不够灵活以满足您的需求,但您应该能够链接到任何使用Cython的C库,并自动为您保留所有引用计数。
这并不总是足够的:如果你的Python对象和任何底层的C对象具有不同的生命周期,你仍然可以得到问题,但它确实可以大大简化。
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
embeddedpython(通过链接到libpython
),它不会在子shell中运行它。 这意味着它可能不一定匹配$PATH
上的python版本。 - 您需要从与任何
gdb
链接的Python源代码版本下载libpython.py
。 - 你可能不得不以root身份运行gdb – 如果是这样,你可能需要设置
sys.path
来匹配你正在debugging的代码。
如果您不能将libpython.py
复制到sys.path
那么您可以将它的位置添加到sys.path
如下所示:
(gdb) python >import sys >sys.path.append('/path/to/containing/dir/') >import libpython > >end
这在python开发文档 , fedora wiki和python wiki中有一些不足之处
如果你有一个较老的gdb
或者只是不能得到这个工作, 那么在Python源代码中也有一个gdbinit ,你可以复制到~/.gdbinit
,它添加了一些类似的function