Показано слід стека від запущеного додатку Python


340

У мене є ця програма Python, яка час від часу застрягає, і я не можу дізнатися, де.

Чи є спосіб подати сигнал інтерпретатору Python, щоб показати точний код, який працює?

Якась нальотна стежка?

Пов’язані запитання:



Відповіді:


315

У мене є модуль, який я використовую в таких ситуаціях - де процес триватиме довгий час, але іноді застрягає з незрозумілих та невідтворюваних причин. Це трохи хакі, і працює тільки на 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

Для використання просто зателефонуйте на функцію liste () в якийсь момент, коли ваша програма запуститься (Ви можете навіть дотримуватися її в site.py, щоб усі програми python використовували її), і нехай вона працює. У будь-який момент надсилайте процесу сигнал SIGUSR1, використовуючи kill або в python:

    os.kill(pid, signal.SIGUSR1)

Це призведе до того, що програма перерветься на консоль пітона в точці, в якій вона перебуває в даний момент, показавши вам слід стека та дозволить вам маніпулювати змінними. Використовуйте control-d (EOF) для продовження запуску (хоча зауважте, що ви, ймовірно, перерватимете будь-який ввід / вивід тощо в точці, в якій ви подаєте сигнал, тому це не зовсім нав'язливо.

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


1
Дякую! Це саме те, що я шукав. Можливо, ви також можете розмістити цей скрипт із підтримкою труб на якомусь фрагменті сайту Python?
Себ

2
Зараз я опублікував це на сайті кулінарної програми python - додано посилання.
Брайан

1
Мені потрібно було додати "імпортування читальної лінії", щоб увімкнути функції історії.
miracle2k

2
Чудова порада! Це також працює для надсилання сигналу до всіх процесів, що містять слово "mypythonapp": pkill -SIGUSR1 -f mypythonapp
Олександр

10
Якщо програма застрягла, цикл інтерпретатора Python, можливо, не зможе запуститися для обробки сигналу. Використовуйте faulthandlerмодуль (і його репортаж, знайдений на PyPI) для обробника сигналу рівня C, який буде друкувати стек Python, не вимагаючи відповіді циклу перекладача.
gps

146

Пропозиція встановити обробник сигналів - хороша, і я її багато використовую. Наприклад, 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
  • Отримати слід стека пітона: pystack

На жаль, це не зовсім надійно, але працює більшу частину часу.

Нарешті, прикріплення straceчасто може дати вам гарне уявлення про те, що відбувається.


2
Блискуче! Команда pystack іноді блокується, але перш ніж це зробити, це дає мені повний слід стека процесу в рядках коду python, не потребуючи жодної підготовки.
мудскоп

26
Незначне оновлення: ця техніка gdb (та оновлений код) задокументована на wiki.python.org/moin/DebuggingWithGdb На цьому фронті була якась розробка, задокументована за цією URL-адресою, і, мабуть, gdb 7 має деяку підтримку Python.
Нельсон

7
Наскільки я можу сказати, це дійсно працює, якщо у вас є символи налагодження, зібрані у ваш бінарний пітон - наприклад: ви запустили свою програму з python2-dbg (для Ubuntu це в окремому пакеті python-dbg). Без цих символів ви, здається, не отримуєте багато корисної інформації.
drevicko

1
у моєму випадку це повернення Unable to locate python frameдо кожної команди
seriyPS

6
gdb 7+ - із підтримкою python надається python-gdb.py. Детальніше тут: chezsoi.org/lucas/blog/2014/11/07/en-gdb-python-macros
Лукас Кімон

71

Я майже завжди маю справу з декількома потоками, і головний потік, як правило, не дуже багато, тому що найцікавіше - це скинути всі стеки (що більше нагадує дамп 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)

53

Отримати слід стека непідготовленої програми 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, відповідь якої містила вказівник на це, серед інших інструментів.)


5
Для мене це спрацювало чудово, де dump_stacks.pyбуло простоimport traceback; traceback.print_stack()
Джон Леман

2
traceback -lдає список попередньо визначених сценаріїв python, якими ви можете скористатися, і dump_stacks.pyє одним з них. Якщо ви використовуєте свій власний (наприклад, для запису трасування стека у файл), можливо, було б розумно використовувати інше ім’я.
johndodo

12
Важлива порада: запустіть apt-get install gdb python-dbg(або еквівалент) перед запуском піразиту, інакше він мовчки вийде з ладу. Інакше працює як шарм!
johndodo

Останній випуск піразиту був у 2012 році
Борис

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


2
Це дозволило б надрукувати лише зворотний бік основної нитки. Я ще не знаходжу рішення для бачення слідів для всіх потоків. Насправді, пітону, здається, не вистачає API для отримання стека з об’єкта Thread, хоча threading.enumerate () надає доступ до всіх об'єктів Thread.
haridsv

Це чудово працює на cygwin. Він видає лише три рядки сліду стека, але цього достатньо, щоб отримати підказку
slashdottir

28

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

import traceback

traceback.print_stack()

1
Щоб виписати слід стека до файлу, використовуйте: import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
GuruM

1
+1 до @gulgi за просту відповідь. Деякі з інших відповідей виглядали дуже складними для мого простого завдання - отримати слід стека виклику з функції сценарію.
ГуруМ

24

Ви можете спробувати модуль несправності . Встановіть його за допомогою 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

Що мені справді допомогло тут - це підказка spiv (яку я би проголосував і прокоментував, якби мав бали репутації) за отримання сліду стека з непідготовленого процесу Python. За винятком того, що він не працював, поки я не змінив сценарій gdbinit . Тому:

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

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

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

  • Отримати слід стека пітона: pystack


Здається, gdbinit за вказаною URL-адресою вже має запропонований патч. У моєму випадку, коли я набрав pystack, мій процесор просто висів. Не знаю чому.
Джессі Глік

2
Ні, це не так - мені було незрозуміло, вибачте, тому що ця лінія з'являється у трьох місцях. Патч, з яким я пов’язаний, показує, який я змінив, побачивши цю роботу.
Gunnlaugur Briem

2
Як і у відповіді @ spiv, для цього програма потребує запуску під python, складений символами налагодження. Інакше ви просто отримаєтеNo symbol "co" in current context.
Миколай

12

Я б додав це як коментар до відповіді 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

python -dv yourscript.py

Це змусить перекладача працювати в режимі налагодження і дати вам слід про те, що робить перекладач.

Якщо ви хочете інтерактивно відладжувати код, слід запустити його так:

python -m pdb yourscript.py

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


Це не дає відповіді на запитання. Питання стосувалося вже запущеного процесу.
dbn

11

Погляньте на faulthandlerновий модуль в Python 3.3. faulthandlerПортировать для використання в Python 2 є на PyPI.


2
Більш детальна відповідь @haypo висвітлює це більш докладно. Я не впевнений, як це звичайно вирішується на ЗО, але почувається неправильним, якщо два принципово повторювані відповіді ...
Ніколай,

7

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

2
Здається, є програма Debian / Ubuntu, pstackяка робить те саме
Rory

1
Здається, це дає лише backtrace під Linux, а не прослідкування Python з іменем файлу та номерами рядків.
ogrisel

6

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

Запустіть свою програму за допомогою:

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

Це вказує gdbпідготувати python <programname>.py <arguments>іr розв'язати його.

Тепер, коли ви програмуєте висить, переключіться на gdbконсоль, натисніть Ctr+Cта виконайте:

(gdb) thread apply all py-list

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


6

Я деякий час шукав рішення для налагодження моїх ниток, і знайшов його тут завдяки 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)

Для моїх потреб я також фільтрую теми за назвою.


3

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

Проект Summer of Code 2006 розглядав додавання функцій віддаленої налагодження до pydb в модулі під назвою mpdb .


Здається, це пройшло через два ( 1 ) переписування ( 2 ), не додаючи функцію прикріплення ПІД, яку я шукав ...
Nickolay

3

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

Дивіться тут: https://github.com/albertz/pydbattach


1
Зауважте: не очевидно, як це зробити. Дякуємо за посилання, які ви додали до README, хоча: pyrasiteпрацювали чудово!
Миколай

3

Це можна зробити з відмінним пі-шпигуном . Це пробірник вибірки для програм 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)  

2

пірсінг - це налагоджувач, який може взаємодіяти з запущеними процесами python, друкувати сліди стека, змінні тощо без будь-якої апріорної установки.

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


3
Мабуть, це несумісно з певними збірками gdb (наприклад, тією, яку я встановив на ubuntu): github.com/google/pyringe/isissue/16 , що потребує відновлення вручну. Ще один налагоджувач, pyrasiteпрацював як шарм для мене.
Миколай

1

Немає можливості підключитися до запущеного процесу пітону та отримати розумні результати. Що я роблю, якщо процеси замикаються, це підключення напруги і намагання з'ясувати, що саме відбувається.

На жаль, часто спостерігається напруга спостерігача, який "фіксує" умови перегонів, так що вихід також є марним.


1
Так, це правда. Прикро, хоча tddbd не підтримує приєднання до запущеного процесу ...
Bartosz Radaczyński,

Це не правда. Дивіться відповідь "spiv" вище, де показано, як підключити gdb та отримати трасування стека Python.
Ендрю Кук

Це не те саме - ці макроси gdb не є надійними і не забезпечують повну потужність / звичний інтерфейс pdb. Я часто бажаю, щоб хтось написав невелике додаток, яке використовувало б ptrace для введення деякого байтового коду Python в запущений процес Python і дозволити йому виконати імпорт pdb; pdb.set_trace () ', можливо також після тимчасового перенаправлення sys.stdin / stdout.
Маріус Гедмінас

Це більше не відповідає дійсності, дивіться інші відповіді, що вказують на пірінг / піразит.
Миколай

1

Для цього можна використовувати PuDB , налагоджувач Python з інтерфейсом curses. Просто додайте

from pudb import set_interrupt_handler; set_interrupt_handler()

у свій код і використовуйте Ctrl-C, коли ви хочете зламати. Ви можете продовжити cі перервати ще раз кілька разів, якщо пропустите це і захочете спробувати ще раз.


Якщо ви використовуєте вищевказану команду в django, не забувайте правильно запустити сервер, щоб запобігти збоям: "Manag.py runserver --noreload --nothreading"
потар

1

Я в таборі 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.


це нормально , щоб отримати відповідь , як 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?
криволіс

1
не звертай уваги. це тому, що мій додаток працює як sudo. мені також потрібно було бігати gdb pyton <pid>як судо.
криволіс

1

Як налагодити будь-яку функцію в консолі :

Створіть функцію, де ви використовуєте 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) 

Щаслива налагодження :)


0

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


0

використовувати модуль перевірки.

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

стек (контекст = 1) Повернення списку записів для стека над кадром абонента.

Я вважаю це дуже корисним.


0

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


0

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

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

Зробивши це, ви можете надрукувати зворотній шлях, просто підключившись до сокета:

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

0

У місці запуску коду ви можете вставити цей невеликий фрагмент, щоб побачити добре відформатований слід друкованого стека. Це передбачає, що logsу кореневому каталозі вашого проекту є папка, що викликається .

# DEBUG: START DEBUG -->
import traceback

with open('logs/stack-trace.log', 'w') as file:
    traceback.print_stack(file=file)
# DEBUG: END DEBUG --!
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.