2010-04-18 113 views
39

我正在开发来自python广告的C扩展我获得了一些segfaults(在开发过程中不可避免的...)。python跟踪分段错误

我正在寻找一种方法来显示段错误发生在哪一行代码(一个想法就像跟踪每一行代码),我该怎么做?

回答

32

这里有一个方法来输出的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 

(你可能想要写跟踪输出当然是)。

+0

这是否适用于C扩展? – 2015-04-22 14:25:54

+1

@MadPhysicist:它不会打印你C代码的行号,如果这就是你的意思。 :-)它将打印调用到C代码中的Python代码的行号。 – RichieHindle 2015-04-22 14:30:10

+0

这就是我的意思。我发现原来的问题很有趣,因为我遇到了同样的问题。 segfault原来是因为我的C代码插入一个NULL元素到PyList_Object中。当我试图迭代列表时,它表现在Python方面。不确定在这种情况下python调试器会有多大帮助。 – 2015-04-22 15:37:46

54

如果你在linux上,在gdb下运行python

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

如果你已经有了一个核心文件,你可以使用'gdb python core'(或者调用核心文件)。如果你在OSX上,核心转储(默认不生成;参见'ulimit -c')存储在目录'/ cores'中。 – 2010-04-19 12:53:03

+0

我真的希望这是我的第一个答案,因为阅读所有其他人花了相当多的时间我宁愿花我的错误。 – sage 2016-04-08 04:10:58

+0

如果在运行像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

15

C扩展中的Segfaults很常见是在您创建对对象的新引用时不增加引用计数的结果。这使得他们非常难以追踪,因为只有在从对象中移除最后一个引用之后才发生段错误,并且即使这样,通常也只有在分配了其他对象时才会发生段错误。

你没有说你到目前为止写了多少C扩展代码,但如果你刚开始考虑是否可以使用ctypes或Cython。 Ctypes可能不够灵活以满足您的需求,但您应该能够链接到任何使用Cython的C库,并自动为您保留所有引用计数。这并不总是足够的:如果你的Python对象和任何底层C对象具有不同的生命周期,你仍然可以得到问题,但它确实可以大大简化事情。

+2

另外,将NULL放在它们不属于的地方。 – 2015-04-22 15:39:26

3

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的堆栈。

一些注意事项:

  • 你的GDB的版本必须大于7,它需要一直与--with-python
  • gdb编译嵌入的Python(通过链接到libpython),它不运行它在一个子shell中。这意味着它可能不一定匹配$PATH上的python版本。
  • 您需要从任何版本的Python源代码下载libpython.py,该源代码与gdb链接的任何版本匹配。
  • 您可能需要以root身份运行gdb - 如果是这样的话,您可能需要设置sys.path以与您正在调试的代码相匹配。

如果您不能复制libpython.pysys.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或者只是不能得到这个工作还有一个gdbinit在Python源代码,你可以复制到~/.gdbinit w这些添加了一些类似的功能

0

我来这里寻找解决同一个问题,没有其他答案帮助我。有帮助的是faulthandler,你可以使用pip install将它安装在Python 2.7中。

faulthandler仅在2012年9月发布的3.3版本中引入了Python,这是在编写大多数其他答案之后才发布的。