Отображение трассировки стека из запущенного приложения Python - PullRequest
320 голосов
/ 25 сентября 2008

У меня есть это приложение на Python, которое время от времени зависает, и я не могу найти где.

Есть ли какой-нибудь способ дать интерпретатору Python указание точного кода, который выполняется?

Какая-то трассировка стека на лету?

Похожие вопросы:

Ответы [ 25 ]

305 голосов
/ 25 сентября 2008

У меня есть модуль, который я использую для таких ситуаций, когда процесс будет работать долго, но иногда застревает по неизвестным и невоспроизводимым причинам. Это немного странно, и работает только на Unix (требуются сигналы):

import code, traceback, signal

def debug(sig, frame):
    """Interrupt running process, and provide a python prompt for
    interactive debugging."""
    d={'_frame':frame}         # Allow access to frame object.
    d.update(frame.f_globals)  # Unless shadowed by global
    d.update(frame.f_locals)

    i = code.InteractiveConsole(d)
    message  = "Signal received : entering python shell.\nTraceback:\n"
    message += ''.join(traceback.format_stack(frame))
    i.interact(message)

def listen():
    signal.signal(signal.SIGUSR1, debug)  # Register handler

Чтобы использовать, просто вызовите функцию listen () в какой-то момент, когда ваша программа запускается (вы можете даже вставить ее в site.py, чтобы все программы Python ее использовали), и позволить ей работать. В любой момент отправьте процессу сигнал SIGUSR1, используя kill или в python:

    os.kill(pid, signal.SIGUSR1)

Это приведет к тому, что программа перейдет на консоль python в точке, в которой она находится в данный момент, показывая вам трассировку стека и позволяя вам манипулировать переменными. Используйте control-d (EOF) для продолжения работы (хотя учтите, что вы, вероятно, прервете любой ввод-вывод и т. Д. В точке, о которой вы сигнализируете, так что это не является полностью незаметным.

У меня есть другой скрипт, который делает то же самое, за исключением того, что он связывается с запущенным процессом по каналу (для отладки фоновых процессов и т. Д.). Его немного велико, чтобы публиковать здесь, но я добавил его как рецепт питонской кулинарной книги .

143 голосов
/ 29 сентября 2008

Предложение об установке обработчика сигналов хорошее, и я им часто пользуюсь. Например, bzr по умолчанию устанавливает обработчик SIGQUIT, который вызывает pdb.set_trace(), чтобы немедленно перенаправить вас в приглашение pdb . (Точную информацию см. В источнике модуля bzrlib.breakin .) С помощью pdb вы можете не только получить текущую трассировку стека, но и проверить переменные и т. Д.

Однако иногда мне нужно отлаживать процесс, в котором у меня не было предвидения, чтобы установить обработчик сигналов. В linux вы можете присоединить к процессу gdb и получить трассировку стека python с помощью некоторых макросов gdb. Поместите http://svn.python.org/projects/python/trunk/Misc/gdbinit в ~/.gdbinit, затем:

  • Прикрепить gdb: gdb -p PID
  • Получить трассировку стека Python: pystack

К сожалению, это не совсем надежно, но работает большую часть времени.

Наконец, добавление strace часто может дать вам хорошее представление о том, что делает процесс.

69 голосов
/ 03 апреля 2010

Я почти всегда имею дело с несколькими потоками, и основной поток обычно мало что делает, поэтому самое интересное - сбросить все стеки (что больше похоже на дамп Java). Вот реализация, основанная на этом блоге :

import threading, sys, traceback

def dumpstacks(signal, frame):
    id2name = dict([(th.ident, th.name) for th in threading.enumerate()])
    code = []
    for threadId, stack in sys._current_frames().items():
        code.append("\n# Thread: %s(%d)" % (id2name.get(threadId,""), threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)
46 голосов
/ 26 апреля 2015

Получение трассировки стека неподготовленной программы на Python, работающей на стандартном питоне без отладочных символов может быть сделано с пиразитом . Для меня Ubuntu Trusty работал как шарм:

$ sudo pip install pyrasite
$ echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
$ sudo pyrasite 16262 dump_stacks.py # dumps stacks to stdout/stderr of the python program

(Шляпа к @Albert, ответ на который содержал указатель на это, среди других инструментов.)

34 голосов
/ 25 сентября 2008
>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

Вы также можете отформатировать трассировку стека, см. документы .

Редактировать : Чтобы смоделировать поведение Java, как предлагает @Douglas Leeder, добавьте:

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

к коду запуска в вашем приложении. Затем вы можете распечатать стек, отправив SIGUSR1 запущенному процессу Python.

27 голосов
/ 25 сентября 2008

Модуль traceback имеет несколько полезных функций, среди которых: print_stack:

import traceback

traceback.print_stack()
23 голосов
/ 27 апреля 2013

Вы можете попробовать модуль обработки ошибок . Установите его, используя pip install faulthandler и добавив:

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

в начале вашей программы. Затем отправьте SIGUSR1 вашему процессу (например: kill -USR1 42), чтобы отобразить трассировку Python всех потоков в стандартный вывод. Прочтите документацию , чтобы получить дополнительные параметры (например, войти в файл) и другие способы отображения трассировки.

Модуль теперь является частью Python 3.3. Для Python 2 см. http://faulthandler.readthedocs.org/

20 голосов
/ 06 марта 2009

Что мне действительно помогло, так это совет spiv (за который я бы проголосовал и прокомментировал бы, если бы у меня были очки репутации) для получения трассировки стека из неподготовленного процесса Python , За исключением того, что он не работал, пока я не изменил скрипт gdbinit . Итак:

  • скачайте http://svn.python.org/projects/python/trunk/Misc/gdbinit и поместите в ~/.gdbinit

  • отредактируйте его, изменив PyEval_EvalFrame на PyEval_EvalFrameEx [править: больше не нужно; связанный файл уже имеет это изменение по состоянию на 2010-01-14]

  • Прикрепите gdb: gdb -p PID

  • Получить трассировку стека Python: pystack

12 голосов
/ 31 марта 2011

Я бы добавил это как комментарий к ответу haridsv , но мне не хватает репутации, чтобы сделать это:

Некоторые из нас все еще застряли на версии Python старше 2.6 (требуется для Thread.ident), поэтому я получил код, работающий на Python 2.5 (хотя и без отображения имени потока), как таковой:

import traceback
import sys
def dumpstacks(signal, frame):
    code = []
    for threadId, stack in sys._current_frames().items():
            code.append("\n# Thread: %d" % (threadId))
        for filename, lineno, name, line in traceback.extract_stack(stack):
            code.append('File: "%s", line %d, in %s' % (filename, lineno, name))
            if line:
                code.append("  %s" % (line.strip()))
    print "\n".join(code)

import signal
signal.signal(signal.SIGQUIT, dumpstacks)
11 голосов
/ 25 сентября 2008

python -dv yourscript.py

Это заставит интерпретатор работать в режиме отладки и подскажет, что делает интерпретатор.

Если вы хотите интерактивно отлаживать код, вы должны запустить его следующим образом:

python -m pdb yourscript.py

Это говорит интерпретатору python запускать ваш скрипт с модулем "pdb", который является отладчиком python, если вы запустите его так, что интерпретатор будет выполняться в интерактивном режиме, во многом как GDB

...