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

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

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

Какой-то тип "на лету"?

Связанные вопросы:

Ответы

Ответ 1

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

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

Ответ 2

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

Ответ 3

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

Ответ 4

Получение трассировки стека неподготовленной программы 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

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

Ответ 5

>>> 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.

Ответ 6

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

import traceback

traceback.print_stack()

Ответ 7

Вы можете попробовать модуль 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/

Ответ 8

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

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

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

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

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

Ответ 9

python -dv yourscript.py

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

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

python -m pdb yourscript.py

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

Ответ 10

Я бы добавил это как комментарий к ответах 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

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

Ответ 12

В 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.

Ответ 13

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

Запустите свою программу с помощью

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

Это дает команду gdb подготовить python <programname>.py <arguments> и r un it.

Теперь, когда вы запускаете программу, перейдите в консоль gdb, нажмите Ctr+C и выполните:

(gdb) thread apply all py-list

Смотрите пример сеанса и более подробную информацию здесь и здесь.

Ответ 14

Я искал какое-то время для решения отладки моих потоков, и я нашел его здесь благодаря 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)

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

Ответ 15

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

Проект Summer of Code 2006 года рассмотрел возможность добавления удаленных отладочных функций в pydb в модуле mpdb.

Ответ 16

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

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

Ответ 17

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

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

Ответ 18

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

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

Ответ 19

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

from pudb import set_interrupt_handler; set_interrupt_handler()

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

Ответ 20

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

Ответ 21

используйте модуль проверки.

  

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

  

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

Я считаю это очень полезным.

Ответ 22

В Python 3 pdb автоматически установит обработчик сигнала при первом использовании c (onue (inue)) в отладчике. После этого нажатие Ctrl-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.

Ответ 23

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

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

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

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

Ответ 24

Я нахожусь в лагере 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.

Ответ 25

Как отладить любую функцию в консоли:

Создайте функцию, в которой вы используете pdb.set_trace(), затем функцию, которую вы хотите отладить.

>>> import pdb
>>> import my_function

>>> def f():
...     pdb.set_trace()
...     my_function()
... 

Затем вызовите созданную функцию:

>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb) 

Удачной отладки :)