Я разрабатываю C-расширения из python ad. Я получаю некоторые segfaults (неизбежные во время разработки...).
Я ищу способ отображения на какой строке кода происходит segfault (идея похожа на отслеживание каждой строки кода), как я могу это сделать?
Я разрабатываю C-расширения из python ad. Я получаю некоторые segfaults (неизбежные во время разработки...).
Я ищу способ отображения на какой строке кода происходит segfault (идея похожа на отслеживание каждой строки кода), как я могу это сделать?
Здесь вы можете вывести имя файла и номер строки каждой строки 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, запустите python под gdb
gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code
Segfaults из C-расширений очень часто являются результатом не увеличения количества ссылок при создании новой ссылки на объект. Это делает их очень трудными для отслеживания, поскольку segfault возникает только после удаления последней ссылки с объекта, и даже тогда часто только тогда, когда выделяется какой-либо другой объект.
Вы не говорите, сколько кода расширения C вы уже написали, но если вы только начинаете, подумайте, можете ли вы использовать ctypes или Cython. Ctypes может быть недостаточно гибким для ваших нужд, но вы должны иметь возможность ссылаться на любую библиотеку C с Cython и автоматически поддерживать все счетчики ссылок.
Это не всегда достаточно: если ваши объекты Python и любые базовые объекты C имеют разные сроки жизни, вы все равно можете получить проблемы, но это значительно упростит ситуацию.
Есть несколько недокументированных расширений python для gdb.
Из источника источника 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):
Как вы можете видеть, теперь мы видим видимость в стеке Python, соответствующем цепочке вызовов CPython.
Некоторые оговорки:
--with-python
gdb
внедряет питон (путем ссылки на libpython
), он не запускает его в подоболочке. Это означает, что он может не соответствовать версии python, которая находится на $PATH
.libpython.py
из любой версии источника Python, которая соответствует любому 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, федерала wiki и вики python
Если у вас более старый gdb
или просто не получается это работать, в источнике Python также есть gdbinit, вы можете скопировать на ~/.gdbinit
, которые добавляют некоторые аналогичные функции