У мене є ця програма Python, яка час від часу застрягає, і я не можу дізнатися, де.
Чи є спосіб подати сигнал інтерпретатору Python, щоб показати точний код, який працює?
Якась нальотна стежка?
Пов’язані запитання:
У мене є ця програма 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
Для використання просто зателефонуйте на функцію liste () в якийсь момент, коли ваша програма запуститься (Ви можете навіть дотримуватися її в site.py, щоб усі програми python використовували її), і нехай вона працює. У будь-який момент надсилайте процесу сигнал SIGUSR1, використовуючи kill або в python:
os.kill(pid, signal.SIGUSR1)
Це призведе до того, що програма перерветься на консоль пітона в точці, в якій вона перебуває в даний момент, показавши вам слід стека та дозволить вам маніпулювати змінними. Використовуйте control-d (EOF) для продовження запуску (хоча зауважте, що ви, ймовірно, перерватимете будь-який ввід / вивід тощо в точці, в якій ви подаєте сигнал, тому це не зовсім нав'язливо.
У мене є ще один сценарій, який робить те саме, за винятком того, що він спілкується з запущеним процесом через трубу (щоб дозволити налагодження фонових процесів тощо). Це трохи велике розміщення тут, але я додав його як рецепт кухонної книги пітона .
faulthandler
модуль (і його репортаж, знайдений на PyPI) для обробника сигналу рівня C, який буде друкувати стек Python, не вимагаючи відповіді циклу перекладача.
Пропозиція встановити обробник сигналів - хороша, і я її багато використовую. Наприклад, bzr за замовчуванням встановлює обробник 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
pystack
На жаль, це не зовсім надійно, але працює більшу частину часу.
Нарешті, прикріплення strace
часто може дати вам гарне уявлення про те, що відбувається.
python-dbg
). Без цих символів ви, здається, не отримуєте багато корисної інформації.
Unable to locate python frame
до кожної команди
Я майже завжди маю справу з декількома потоками, і головний потік, як правило, не дуже багато, тому що найцікавіше - це скинути всі стеки (що більше нагадує дамп 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, відповідь якої містила вказівник на це, серед інших інструментів.)
dump_stacks.py
було простоimport traceback; traceback.print_stack()
traceback -l
дає список попередньо визначених сценаріїв python, якими ви можете скористатися, і dump_stacks.py
є одним з них. Якщо ви використовуєте свій власний (наприклад, для запису трасування стека у файл), можливо, було б розумно використовувати інше ім’я.
apt-get install gdb python-dbg
(або еквівалент) перед запуском піразиту, інакше він мовчки вийде з ладу. Інакше працює як шарм!
>>> 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.
Модуль traceback має кілька приємних функцій, серед яких: print_stack:
import traceback
traceback.print_stack()
import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
Ви можете спробувати модуль несправності . Встановіть його за допомогою pip install faulthandler
та додайте:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
на початку вашої програми. Потім відправте SIGUSR1 у ваш процес (наприклад:), щоб kill -USR1 42
відобразити прослідкування Python усіх потоків на стандартний вихід. Прочитайте документацію, щоб отримати додаткові параметри (наприклад: увійти у файл) та інші способи відображення прослідковування.
Тепер модуль є частиною Python 3.3. Для Python 2 див. Http://faulthandler.readthedocs.org/
Що мені справді допомогло тут - це підказка spiv (яку я би проголосував і прокоментував, якби мав бали репутації) за отримання сліду стека з непідготовленого процесу Python. За винятком того, що він не працював, поки я не змінив сценарій gdbinit . Тому:
скачайте http://svn.python.org/projects/python/trunk/Misc/gdbinit і вставте його~/.gdbinit
відредагуйте його, змінивши [редагувати: більше не потрібно; пов’язаний файл уже має цю зміну станом на 2010-01-14]PyEval_EvalFrame
наPyEval_EvalFrameEx
Прикріпити gdb: gdb -p PID
Отримати слід стека пітона: pystack
No symbol "co" in current context.
Я б додав це як коментар до відповіді 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 yourscript.py
Це змусить перекладача працювати в режимі налагодження і дати вам слід про те, що робить перекладач.
Якщо ви хочете інтерактивно відладжувати код, слід запустити його так:
python -m pdb yourscript.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.
pstack
яка робить те саме
Якщо ви на системі 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
Дивіться приклад сеансу та додаткову інформацію тут і тут .
Я деякий час шукав рішення для налагодження моїх ниток, і знайшов його тут завдяки 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". Він включає менеджерів сигналів, які можуть подбати про запуск налагоджувача при надсиланні заданого сигналу.
Проект Summer of Code 2006 розглядав додавання функцій віддаленої налагодження до pydb в модулі під назвою mpdb .
Я зламав разом якийсь інструмент, який приєднується до запущеного процесу Python і вводить якийсь код, щоб отримати оболонку Python.
Дивіться тут: https://github.com/albertz/pydbattach
pyrasite
працювали чудово!
Це можна зробити з відмінним пі-шпигуном . Це пробірник вибірки для програм 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)
пірсінг - це налагоджувач, який може взаємодіяти з запущеними процесами python, друкувати сліди стека, змінні тощо без будь-якої апріорної установки.
Хоча я часто використовував рішення обробника сигналів у минулому, все ще може бути важко відтворити проблему в певних умовах.
pyrasite
працював як шарм для мене.
Немає можливості підключитися до запущеного процесу пітону та отримати розумні результати. Що я роблю, якщо процеси замикаються, це підключення напруги і намагання з'ясувати, що саме відбувається.
На жаль, часто спостерігається напруга спостерігача, який "фіксує" умови перегонів, так що вихід також є марним.
Для цього можна використовувати PuDB , налагоджувач Python з інтерфейсом curses. Просто додайте
from pudb import set_interrupt_handler; set_interrupt_handler()
у свій код і використовуйте Ctrl-C, коли ви хочете зламати. Ви можете продовжити c
і перервати ще раз кілька разів, якщо пропустите це і захочете спробувати ще раз.
Я в таборі GDB з розширеннями python. Дотримуйтесь https://wiki.python.org/moin/DebuggingWithGdb , що означає
dnf install gdb python-debuginfo
або sudo apt-get install gdb python2.7-dbg
gdb 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>
як судо.
Як налагодити будь-яку функцію в консолі :
Створіть функцію, де ви використовуєте 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 , тому вам, можливо, доведеться вбудувати це у свою програму. Можливо, ви могли б зробити сервер в іншому потоці, який може отримати стек-трек у відповідь на якесь повідомлення?
У 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
Зробивши це, ви можете надрукувати зворотній шлях, просто підключившись до сокета:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
У місці запуску коду ви можете вставити цей невеликий фрагмент, щоб побачити добре відформатований слід друкованого стека. Це передбачає, що logs
у кореневому каталозі вашого проекту є папка, що викликається .
# DEBUG: START DEBUG -->
import traceback
with open('logs/stack-trace.log', 'w') as file:
traceback.print_stack(file=file)
# DEBUG: END DEBUG --!