Python отслеживает ошибку сегментации

Я разрабатываю C-расширения из python ad. Я получаю некоторые segfaults (неизбежные во время разработки...).

Я ищу способ отображения на какой строке кода происходит segfault (идея похожа на отслеживание каждой строки кода), как я могу это сделать?

Ответ 1

Здесь вы можете вывести имя файла и номер строки каждой строки 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

(Конечно, вы, вероятно, захотите записать вывод трассировки в файл.)

Ответ 2

Если вы используете linux, запустите python под gdb

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

Ответ 3

Segfaults из C-расширений очень часто являются результатом не увеличения количества ссылок при создании новой ссылки на объект. Это делает их очень трудными для отслеживания, поскольку segfault возникает только после удаления последней ссылки с объекта, и даже тогда часто только тогда, когда выделяется какой-либо другой объект.

Вы не говорите, сколько кода расширения C вы уже написали, но если вы только начинаете, подумайте, можете ли вы использовать ctypes или Cython. Ctypes может быть недостаточно гибким для ваших нужд, но вы должны иметь возможность ссылаться на любую библиотеку C с Cython и автоматически поддерживать все счетчики ссылок.

Это не всегда достаточно: если ваши объекты Python и любые базовые объекты C имеют разные сроки жизни, вы все равно можете получить проблемы, но это значительно упростит ситуацию.

Ответ 4

Есть несколько недокументированных расширений 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.

Некоторые оговорки:

  • Ваша версия gdb должна быть больше 7, и ее необходимо скомпилировать с помощью --with-python
  • gdb внедряет питон (путем ссылки на libpython), он не запускает его в подоболочке. Это означает, что он может не соответствовать версии python, которая находится на $PATH.
  • Вам нужно загрузить libpython.py из любой версии источника Python, которая соответствует любому gdb.
  • Возможно, вам придется запустить gdb как root - если вам, возможно, потребуется настроить 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, которые добавляют некоторые аналогичные функции