2010-04-18 143 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,這是在編寫大多數其他答案之後才發佈的。