Python отслеживает ошибку сегментации - PullRequest
46 голосов
/ 19 апреля 2010

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

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

Ответы [ 5 ]

66 голосов
/ 19 апреля 2010

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

gdb python
(gdb) run /path/to/script.py
## wait for segfault ##
(gdb) backtrace
## stack trace of the c code
38 голосов
/ 19 апреля 2010

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

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

17 голосов
/ 19 апреля 2010

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

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

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

6 голосов
/ 17 января 2018

Я пришел сюда в поисках решения той же проблемы, и ни один из других ответов не помог мне.Что помогло, так это faulthandler, и вы можете установить его в Python 2.7, просто используя pip install.

faulthandler был представлен в Python только в версии 3.3, выпущеннойСентябрь 2012 года, который был после того, как было написано большинство других ответов.

3 голосов
/ 24 июня 2015

Есть несколько недокументированных расширений 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 (f=f@entry=
    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), throwflag=throwflag@entry=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 встраивает python (путем ссылки на 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 для разработчиков , вики Fedora и вики Python

Если у вас более старый gdb или вы просто не можете заставить его работать, в исходном коде Python также есть gdbinit , который вы можете скопировать в ~/.gdbinit, который добавляет некоторые подобные функции

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...