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

StackOverflow https://stackoverflow.com/questions/132058

Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

Другие советы

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

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

  • Прикрепите БДБ: 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 без отладочных символов можно сделать с пиразит.Для меня это сработало в 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, чей ответ содержал указатель на это, среди других инструментов.)

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

А Выслеживать модуль имеет несколько приятных функций, среди них:print_stack:

import traceback

traceback.print_stack()

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

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

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

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

Что мне действительно помогло, так это Совет Спива (за что я бы проголосовал и прокомментировал, если бы у меня были очки репутации) за получение трассировки стека из неподготовленный Питоновый процесс.Вот только это не сработало, пока я изменил скрипт gdbinit.Так:

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

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

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

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

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

python -dv вашscript.py

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

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

python -m pdb вашscript.py

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

Взгляните на faulthandler модуль, новый в Python 3.3.А faulthandler резервный порт для использования в 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 упаковка).Это также помогает с многопоточными приложениями, приложениями с графическим интерфейсом и модулями C.

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

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

Это поручает gdb подготовить python <programname>.py <arguments> и rединица.

Теперь, когда ваша программа зависает, переключитесь в gdb консоль, нажмите Ctrl+C и выполните:

(gdb) thread apply all py-list

Видеть пример сеанса и дополнительная информация здесь и здесь.

Я долго искал решение для отладки своих потоков и нашел его здесь благодаря haridsv.Я использую слегка упрощенную версию, использующую функцию трассировки.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)

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

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

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

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

Глянь сюда: https://github.com/albertz/pydbattach

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

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

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

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

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

from pudb import set_interrupt_handler; set_interrupt_handler()

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

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

Создать функцию где ты используйте 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) 

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

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

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

Импорт проверки справки (Inspect.Stack) Справка в стеке функций в модуле. Проверка:

Stack (context = 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 встроенный и его достаточно включить в конфигурации (к имени каждого воркера прикреплён номер):

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

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

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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top