Отображение трассировки стека из запущенного приложения Python

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

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

своего рода на лету stacktrace?

вопросы:

24 ответов


у меня есть модуль, который я использую для таких ситуаций - где процесс будет работать в течение длительного времени, но иногда застревает по неизвестным и невоспроизводимым причинам. Его немного хаки, и работает только на 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 или in python:

    os.kill(pid, signal.SIGUSR1)

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

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


предложение установить обработчик сигналов является хорошим, и я использую его много. Например, bzr по умолчанию устанавливается обработчик SIGQUIT, который вызывает pdb.set_trace() чтобы немедленно бросить тебя в pdb запрос. (См. bzrlib.брейкин!--8--> источник модуля для точных деталей.) С pdb вы можете не только получить текущую трассировку стека, но и проверить переменные и т. д.

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

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

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

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


я почти всегда имею дело с несколькими потоками, и основной поток, как правило, не делает много, поэтому самое интересное-сбросить все стеки (что больше похоже на дамп 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)

получение трассировки стека неприготовленные программа python, работающая на складе python без отладочных символов можно сделать pyrasite. Работал как шарм для меня в 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

(Hat tip to @Albert, ответ которого содержал указатель на это, среди других инструментов.)


>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

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

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

редактировать: чтобы имитировать поведение Java, как предложил @Douglas Leeder, добавьте это:

import signal
import traceback

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

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


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

import traceback

traceback.print_stack()

что действительно помогло мне вот это фарцовщика подсказка (который я бы проголосовал и прокомментировал, если бы у меня были очки репутации) для получения трассировки стека из неприготовленные процесс Python. Только это не сработало, пока я ... --10-->изменен скрипт gdbinit. Итак:

  • скачать http://svn.python.org/projects/python/trunk/Misc/gdbinit и положить его в ~/.gdbinit

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

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

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


вы можете попробовать модуль faulthandler. Установите его с помощью pip install faulthandler и добавить:

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

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

модуль в Python 3.3. Для Python 2 см. http://faulthandler.readthedocs.org/


python-dv yourscript.py

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

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

python - m pdb yourscript.py

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


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

некоторые из нас все еще застряли на версии Python старше 2.6 (требуется для резьбы.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)

посмотри faulthandler модуль, новый в Python 3.3. А faulthandler backport для использования в Python 2 доступен на PyPI.


на Solaris вы можете использовать pstack (1) никаких изменений в коде python не требуется. например.

# pstack 16000 | grep : | head
16000: /usr/bin/python2.6 /usr/lib/pkg.depotd --cfg svc:/application/pkg/serv
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:282 (_wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:295 (wait) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/process/wspbus.py:242 (block) ]
[ /usr/lib/python2.6/vendor-packages/cherrypy/_init_.py:249 (quickstart) ]
[ /usr/lib/pkg.depotd:890 (<module>) ]
[ /usr/lib/python2.6/threading.py:256 (wait) ]
[ /usr/lib/python2.6/Queue.py:177 (get) ]
[ /usr/lib/python2.6/vendor-packages/pkg/server/depot.py:2142 (run) ]
[ /usr/lib/python2.6/threading.py:477 (run)
etc.

если вы находитесь в системе Linux, используйте удивительность gdb с расширениями отладки Python (может быть в python-dbg или python-debuginfo пакет). Он также помогает с многопоточными приложениями, приложениями GUI и модулями C.

запустите программу с помощью:

$ gdb -ex r --args python <programname>.py [arguments]

таким образом gdb для подготовки python <programname>.py <arguments> и run it.

теперь, когда программа зависает, переключает в


Я некоторое время искал решение для отладки моих потоков, и я нашел его здесь благодаря haridsv. Я использую слегка упрощенную версию, использующую traceback.print_stack():

import sys, traceback, signal
import threading
import os

def dumpstacks(signal, frame):
  id2name = dict((th.ident, th.name) for th in threading.enumerate())
  for threadId, stack in sys._current_frames().items():
    print(id2name[threadId])
    traceback.print_stack(f=stack)

signal.signal(signal.SIGQUIT, dumpstacks)

os.killpg(os.getpgid(0), signal.SIGQUIT)

для моих нужд я также фильтрую потоки по имени.


стоит Pydb, "Расширенная версия отладчика Python, свободно основанная на наборе команд gdb". Он включает в себя менеджеры сигналов, которые могут позаботиться о запуске отладчика при отправке указанного сигнала.

летом 2006 года проект кода посмотрел на добавление функций удаленной отладки в pydb в модуле под названием mpdb.


я взломал какой-то инструмент, который присоединяется к запущенному процессу Python и вводит некоторый код, чтобы получить оболочку Python.

смотрите здесь: https://github.com/albertz/pydbattach


pyringe - отладчик, который может взаимодействовать с запущенными процессами python, трассировками стека печати, переменными и т. д. без какой-либо априорной установки.

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


нет способа подключиться к запущенному процессу python и получить разумные результаты. Что я делаю, если процессы запираются, это подключаю strace и пытаюсь выяснить, что именно происходит.

к сожалению, часто strace является наблюдателем, который" исправляет " условия гонки, так что выход там тоже бесполезен.


можно использовать PuDB, отладчик Python с интерфейсом curses для этого. Просто добавьте

from pudb import set_interrupt_handler; set_interrupt_handler()

к вашему коду и используйте Ctrl-C, когда вы хотите сломать. Вы можете продолжить с c и перерыв снова несколько раз, если вы пропустите его и хотите попробовать еще раз.


Я не знаю ничего похожего на ответ java на SIGQUIT, поэтому вам, возможно, придется встроить его в свое приложение. Может быть, вы могли бы сделать сервер в другом потоке, который может получить stacktrace в ответ на какое-то сообщение?


используйте модуль inspect.

импорт проверить помогите (осмотрите.стек) Справка по стеку функций в модуле inspect:

стек(контекст=1) Возвращает список записей для стека над фреймом вызывающего объекта.

Я нахожу это очень полезным.


в Python 3 pdb автоматически установит обработчик сигнала при первом использовании c(ont (inue)) в отладчике. После нажатия Control-C вы снова вернетесь туда. В Python 2 Вот однострочный, который должен работать даже в относительно старых версиях (протестирован в 2.7, но я проверил источник Python до 2.4, и он выглядел нормально):

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

pdb стоит изучить, если вы тратите какое-то время на отладку Python. Интерфейс немного тупой, но должен быть знакомым для всех, кто использовал аналогичные инструменты, такие как gdb.


в случае, если вам нужно сделать это с uWSGI, он имеет Python Tracebacker встроенный, и это просто вопрос включения его в конфигурации (номер прикреплен к имени для каждого работника):

py-tracebacker=/var/run/uwsgi/pytrace

после того, как вы сделали это, вы можете распечатать backtrace просто подключившись к сокету:

uwsgi --connect-and-read /var/run/uwsgi/pytrace1

Я нахожусь в лагере GDB с расширениями python. Следуйте заhttps://wiki.python.org/moin/DebuggingWithGdb, что означает

  1. dnf install gdb python-debuginfo или sudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

также считают info threads и thread apply all py-bt.