我正在開發來自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