У меня есть это приложение Python, которое время от времени зависает, и я не могу понять, где.
Есть ли способ сигнализировать интерпретатору Python, чтобы он показал вам точный запущенный код?
Какая-то трассировка стека на лету?
Связанные вопросы:
связанные wiki.python.org/moin/DebuggingWithGdb






python -dv yourscript.py
Это заставит интерпретатор работать в режиме отладки и даст вам представление о том, что делает интерпретатор.
Если вы хотите отладить код в интерактивном режиме, запустите его следующим образом:
python -m pdb yourscript.py
Это говорит интерпретатору Python запустить ваш скрипт с модулем «pdb», который является отладчиком Python, если вы запустите его так, что интерпретатор будет выполняться в интерактивном режиме, во многом как GDB
Это не отвечает на вопрос. Вопрос был об уже запущенном процессе.
В модуле проследить есть несколько хороших функций, среди которых: print_stack:
import traceback
traceback.print_stack()
Чтобы записать трассировку стека в файл, используйте: import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
+1 к @gulgi за его простой в использовании ответ. Некоторые другие ответы выглядели очень сложными для моей простой задачи получения трассировки стека вызовов из функции сценария.
>>> 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.
Это только напечатает обратную трассировку основного потока. Мне еще предстоит найти решение, позволяющее видеть следы для всех потоков. Фактически, кажется, что у python отсутствует API для извлечения стека из объекта Thread, хотя threading.enumerate () предоставляет доступ ко всем объектам Thread.
Это отлично работает на cygwin. Он печатает только три строки трассировки стека, но этого достаточно, чтобы понять
Я не знаю ничего похожего на ответ java на SIGQUIT, поэтому вам, возможно, придется встроить его в свое приложение. Может быть, вы могли бы создать сервер в другом потоке, который мог бы получать трассировку стека при ответе на какое-либо сообщение?
Невозможно подключиться к работающему процессу Python и получить разумные результаты. Что я делаю, если процессы блокируются, - это подключаю strace и пытаюсь понять, что именно происходит.
К сожалению, часто strace - это наблюдатель, который «исправляет» условия гонки, так что результат здесь тоже бесполезен.
Да, это правда. Очень жаль, что pdb не поддерживает подключение к запущенному процессу ...
Это неправда. См. Ответ "spiv" выше, в котором показано, как подключить gdb и получить трассировку стека Python.
Это не то же самое - эти макросы gdb ненадежны и не обеспечивают полной мощности / знакомого интерфейса pdb. Мне часто хочется, чтобы кто-нибудь написал небольшое приложение, которое использовало бы ptrace для внедрения некоторого байт-кода Python в работающий процесс Python и чтобы он выполнял 'import pdb; pdb.set_trace () ', возможно, также после временного перенаправления sys.stdin / stdout.
Это уже не так, см. Другие ответы, указывающие на пирог / пиразит.
У меня есть модуль, который я использую для подобных ситуаций - когда процесс будет работать долгое время, но иногда застревает по неизвестным и невоспроизводимым причинам. Это немного взломано и работает только в 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.
Спасибо! Это как раз то, что я искал. Может быть, вы также могли бы опубликовать этот скрипт с поддержкой канала на каком-нибудь сайте сниппетов Python?
Я разместил его на сайте кулинарной книги python - добавлена ссылка.
Мне нужно было добавить «импортную строку чтения», чтобы включить функции истории.
Отличный совет! Это также работает для отправки сигнала всем процессам, содержащим слово «mypythonapp»: pkill -SIGUSR1 -f mypythonapp
К сожалению, это не работает в mod_wsgi: code.google.com/p/modwsgi/wiki/…
Действительно отличный совет. Отлаживаю софт, который просто зависает, если я использую стандартный модуль pdb. У меня действительно заканчивались варианты, кроме print.
Это не работает в Windows! Нет в наличии signal.SIGUSR1.
Это работает на cygwin! Есть ли способ возобновить программу как есть из оболочки после того, как вы взломали ее таким образом?
Если приложение зависло, цикл интерпретатора Python может не работать для обработки сигнала. Используйте модуль faulthandler (и его backport, найденный в PyPI) для обработчика сигнала уровня C, который будет печатать стек Python, не требуя, чтобы цикл интерпретатора был отзывчивым.
Этот метод не работает в многопоточной программе. Сигнал не ловится, пробовал слушать разные сигналы (USR1, USR2, SIGALRM, SIGTERM), ни у кого не работает. Возможное объяснение - code.activestate.com/recipes/…, но это не дает возможности получить трассировку стека зависшей программы. Есть идеи?
Это более короткая версия, если вам нужен только stacktrace: signal.signal (signal.SIGUSR1, lambda sig, frame: traceback.print_stack ())
ValueError: сигнал работает только в основном потоке, делаем ли мы что-нибудь для многопоточного приложения в python
@BjornTipling Как это делает PDB?
Есть ли большие (или какие-либо) накладные расходы на прослушивание подобных сигналов?
Предложение установить обработчик сигналов - хорошее, и я часто им пользуюсь. Например, БЗР по умолчанию устанавливает обработчик SIGQUIT, который вызывает pdb.set_trace(), чтобы немедленно перенаправить вас в приглашение PDB. (Точные сведения см. В источнике модуля bzrlib.breakin.) С помощью pdb вы можете не только получить текущую трассировку стека (с помощью команды (w)here), но также проверить переменные и т. д.
Однако иногда мне нужно отладить процесс, в котором я не предусмотрительно установил обработчик сигналов. В Linux вы можете присоединить к процессу gdb и получить трассировку стека python с помощью некоторых макросов gdb. Поместите http://svn.python.org/projects/python/trunk/Misc/gdbinit в ~/.gdbinit, затем:
gdb -pPIDpystackК сожалению, это не совсем надежно, но в большинстве случаев работает.
Наконец, подключение strace часто может дать вам хорошее представление о том, что делает процесс.
Блестяще! Команда pystack иногда блокируется, но перед этим дает мне полную трассировку стека процесса в строках кода Python без какой-либо подготовки.
Незначительное обновление: этот метод gdb (и обновленный код) задокументирован в wiki.python.org/moin/DebuggingWithGdb. На этом фронте были некоторые разработки, которые задокументированы по этому URL-адресу, и, очевидно, gdb 7 имеет некоторую поддержку Python.
Насколько я могу судить, это действительно работает только в том случае, если у вас есть символы отладки, скомпилированные в ваш двоичный файл python - например: вы запускали свою программу с помощью python2-dbg (в Ubuntu это находится в отдельном пакете python-dbg). Без этих символов вы, похоже, не получите много полезной информации.
в моем случае это возвращает Unable to locate python frame для каждой команды
Поддержка gdb 7+ --with-python обеспечивается python-gdb.py. Подробнее здесь: chezsoi.org/lucas/blog/2014/11/07/en-gdb-python-macros
Стоит взглянуть на Pydb, «расширенную версию отладчика Python, основанную на наборе команд gdb». Он включает менеджеры сигналов, которые могут позаботиться о запуске отладчика при отправке указанного сигнала.
В проекте Summer of Code 2006 года рассматривалось добавление функций удаленной отладки в pydb в модуле mpdb.
Кажется, он прошел через две (1) перезаписи (2) Функция без добавления присоединенного PID, которую я искал ...
Что действительно помогло мне здесь, так это наконечник спива (за который я бы проголосовал и прокомментировал, если бы у меня были очки репутации) для получения трассировки стека из процесса неподготовленный Python. За исключением того, что это не сработало, пока я не модифицировал скрипт gdbinit. Так:
скачать http://svn.python.org/projects/python/trunk/Misc/gdbinit и поставить в ~/.gdbinit
отредактируйте его, изменив [редактировать: больше не требуется; связанный файл уже содержит это изменение по состоянию на 14 января 2010 г.]PyEval_EvalFrame на PyEval_EvalFrameEx
Прикрепите gdb: gdb -p PID
Получите трассировку стека python: pystack
Кажется, что в gdbinit по указанному URL-адресу уже есть предложенный вами патч. В моем случае, когда я набирал pystack, мой процессор просто зависал. Не знаю почему.
Нет, это не так - мне неясно, извините, потому что эта строка появляется в трех местах. Патч, с которым я связался, показывает, какой из них я изменил, когда увидел эту работу.
Как и ответ @spiv, для этого требуется, чтобы программа работала под python, скомпилированным с отладочными символами. В противном случае вы получите просто No symbol "co" in current context.
использовать модуль проверки.
import inspect help(inspect.stack) Help on function stack in module inspect:
стек (контекст = 1) Вернуть список записей для стека над кадром вызывающего.
Я нахожу это действительно очень полезным.
Я почти всегда имею дело с несколькими потоками, а основной поток, как правило, мало что делает, поэтому наиболее интересно выгружать все стеки (что больше похоже на дамп 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)
Я бы добавил это как комментарий к ответ 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)
В 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.
Похоже, есть программа pstack Debian / Ubuntu, которая делает то же самое.
Кажется, что он дает только обратную трассировку под Linux, а не трассировку Python с именем файла и номерами строк.
Взгляните на модуль faulthandler, новый в Python 3.3. faulthandler задний порт для использования в Python 2 доступен в PyPI.
В более позднем ответе @haypo это более подробно рассматривается. Я не уверен, как это обычно делается на SO, но мне кажется неправильным иметь два по существу повторяющихся ответа ...
Я собрал инструмент, который подключается к работающему процессу Python и вводит код для получения оболочки Python.
Смотрите здесь: https://github.com/albertz/pydbattach
Примечание: не очевидно, как это построить. Спасибо за ссылки, которые вы поместили в README: pyrasite работал отлично!
Некоторое время я искал решение для отладки своих потоков и нашел его здесь благодаря 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)
Для своих нужд я также фильтрую темы по имени.
Вы можете попробовать модуль обработки неисправностей. Установите его с помощью pip install faulthandler и добавьте:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
в начале вашей программы. Затем отправьте SIGUSR1 вашему процессу (например, kill -USR1 42), чтобы отобразить трассировку Python всех потоков в стандартный вывод. Прочтите документацию для дополнительных опций (например: войти в файл) и других способов отображения трассировки.
Модуль теперь является частью Python 3.3. Для Python 2 см. http://faulthandler.readthedocs.org/
Для этого вы можете использовать PuDB, отладчик Python с интерфейсом curses. Просто добавь
from pudb import set_interrupt_handler; set_interrupt_handler()
в свой код и используйте Ctrl-C, если хотите прервать работу. Вы можете продолжить с c и несколько раз прервать работу, если пропустите и захотите повторить попытку.
Когда вы используете указанную выше команду в django, не забудьте правильно запустить сервер, чтобы предотвратить сбои: «manage.py runserver --noreload --nothreading»
Если вы работаете в системе Linux, используйте преимущества gdb с расширениями отладки Python (могут быть в пакете python-dbg или python-debuginfo). Он также помогает с многопоточными приложениями, приложениями с графическим интерфейсом пользователя и модулями C.
Запустите вашу программу с помощью:
$ gdb -ex r --args python <programname>.py [arguments]
Это указывает gdb на подготовку python <programname>.py <arguments> и r к его запуску.
Теперь, когда программа зависает, переключитесь на консоль gdb, нажмите Ctr+C и выполните:
(gdb) thread apply all py-list
См. пример сеанса и дополнительную информацию здесь и здесь.
пирог - это отладчик, который может взаимодействовать с запущенными процессами Python, трассировками стека печати, переменными и т. д. Без какой-либо априорной настройки.
Несмотря на то, что в прошлом я часто использовал решение для обработки сигналов, все еще может быть сложно воспроизвести проблему в определенных средах.
Очевидно, он несовместим с некоторыми сборками gdb (например, с той, которую я установил на ubuntu): github.com/google/pyringe/issues/16, требующей перестройки вручную. Другой отладчик, pyrasite, отлично подействовал на меня.
В 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.
Получение трассировки стека программы 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, чей ответ содержал указатель на это, среди других инструментов.)
Это отлично сработало для меня, где dump_stacks.py был просто import traceback; traceback.print_stack().
traceback -l дает вам список предопределенных скриптов Python, которые вы можете использовать, и dump_stacks.py является одним из них. Если вы используете свое собственное имя (например, для записи трассировки стека в файл), может быть целесообразно использовать другое имя.
Важный совет: запустите apt-get install gdb python-dbg (или аналогичный) перед запуском pyrasite, иначе он автоматически выйдет из строя. В противном случае работает как шарм!
Последний выпуск пиразита был в 2012
Если вам нужно сделать это с помощью uWSGI, у него есть встроенный Трассировщик Python, и это просто вопрос включения его в конфигурации (номер привязан к имени для каждого рабочего):
py-tracebacker=/var/run/uwsgi/pytrace
Как только вы это сделаете, вы можете распечатать backtrace, просто подключившись к сокету:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
Я нахожусь в лагере GDB с расширениями python. Следуйте https://wiki.python.org/moin/DebuggingWithGdb, что означает
dnf install gdb python-debuginfo или sudo apt-get install gdb python2.7-dbggdb python <pid of running process>py-btТакже рассмотрите info threads и thread apply all py-bt.
нормально ли получать ответ типа Traceback (most recent call first): Python Exception <class 'gdb.error'> No frame is currently selected.: Error occurred in Python command: No frame is currently selected. при запуске py-bt в gdb?
неважно. это потому, что мое приложение работало как sudo. Мне тоже нужно было запустить gdb pyton <pid> как sudo.
Как отлаживать любую функцию в консоли:
Создать функцию, где вы используйте 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)
Удачной отладки :)
Это можно сделать с помощью отличного пи-шпион. Это профилировщик выборки для программ Python, поэтому его задача - подключиться к процессам Python и выбрать их стеки вызовов. Следовательно, py-spy dump --pid $SOME_PID - это все, что вам нужно для дампа стеков вызовов всех потоков в процессе $SOME_PID. Обычно ему требуются повышенные привилегии (для чтения памяти целевого процесса).
Вот пример того, как это выглядит для многопоточного приложения Python.
$ sudo py-spy dump --pid 31080
Process 31080: python3.7 -m chronologer -e production serve -u www-data -m
Python v3.7.1 (/usr/local/bin/python3.7)
Thread 0x7FEF5E410400 (active): "MainThread"
_wait (cherrypy/process/wspbus.py:370)
wait (cherrypy/process/wspbus.py:384)
block (cherrypy/process/wspbus.py:321)
start (cherrypy/daemon.py:72)
serve (chronologer/cli.py:27)
main (chronologer/cli.py:84)
<module> (chronologer/__main__.py:5)
_run_code (runpy.py:85)
_run_module_as_main (runpy.py:193)
Thread 0x7FEF55636700 (active): "_TimeoutMonitor"
run (cherrypy/process/plugins.py:518)
_bootstrap_inner (threading.py:917)
_bootstrap (threading.py:885)
Thread 0x7FEF54B35700 (active): "HTTPServer Thread-2"
accept (socket.py:212)
tick (cherrypy/wsgiserver/__init__.py:2075)
start (cherrypy/wsgiserver/__init__.py:2021)
_start_http_thread (cherrypy/process/servers.py:217)
run (threading.py:865)
_bootstrap_inner (threading.py:917)
_bootstrap (threading.py:885)
...
Thread 0x7FEF2BFFF700 (idle): "CP Server Thread-10"
wait (threading.py:296)
get (queue.py:170)
run (cherrypy/wsgiserver/__init__.py:1586)
_bootstrap_inner (threading.py:917)
_bootstrap (threading.py:885)
Это работает, но hypno из ответа @ kmaork производит трассировку Python, которая намного полезнее для выяснения того, где проблема.
В момент запуска кода вы можете вставить этот небольшой фрагмент, чтобы увидеть хорошо отформатированную трассировку напечатанного стека. Предполагается, что у вас есть папка logs в корневом каталоге вашего проекта.
# DEBUG: START DEBUG -->
import traceback
with open('logs/stack-trace.log', 'w') as file:
traceback.print_stack(file=file)
# DEBUG: END DEBUG --!
Вы можете использовать пакет гипно, например:
hypno <pid> "import traceback; traceback.print_stack()"
Это выведет трассировку стека в стандартный вывод программы.
В качестве альтернативы, если вы не хотите ничего выводить на стандартный вывод или у вас нет доступа к нему (например, демон), вы можете использовать пакет Madbg, который является отладчиком python, который позволяет вам подключаться к запущенному python и отлаживайте ее в своем текущем терминале. Он похож на pyrasite и pyringe, но новее, не требует GDB и использует IPython для отладчика (что означает цвета и автозаполнение).
Чтобы увидеть трассировку стека запущенной программы, вы можете запустить:
madbg attach <pid>
И в оболочке отладчика введите:
bt
Отказ от ответственности - я написал оба пакета
FTW! sudo $(which hypno) $(pgrep pytest) "import traceback; traceback.print_stack()" (Он не работает без sudo, несмотря на то, что оба процесса принадлежат одному пользователю.)
Рад слышать! Он может работать без sudo, если вы разрешите ptrace scope на своем компьютере, подробнее здесь: stackoverflow.com/q/19215177/2907819
связанные: stackoverflow.com/q/4163964/1449460