Ho questa applicazione Python che si blocca di tanto in tanto e non riesco a scoprire dove.
C'è un modo per segnalare l'interprete Python per mostrarti il codice esatto in esecuzione?
Una specie di stacktrace al volo?
Domande correlate:
Ho questa applicazione Python che si blocca di tanto in tanto e non riesco a scoprire dove.
C'è un modo per segnalare l'interprete Python per mostrarti il codice esatto in esecuzione?
Una specie di stacktrace al volo?
Domande correlate:
Risposte:
Ho un modulo che uso per situazioni come questa - in cui un processo sarà in esecuzione per molto tempo ma a volte si blocca per motivi sconosciuti e irriproducibili. È un po 'confuso, e funziona solo su unix (richiede segnali):
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
Per usare, basta chiamare la funzione hear () ad un certo punto all'avvio del programma (potresti persino attaccarlo in site.py per farlo usare da tutti i programmi Python) e lasciarlo funzionare. In qualsiasi momento, invia al processo un segnale SIGUSR1, usando kill, o in python:
os.kill(pid, signal.SIGUSR1)
Ciò causerà il passaggio del programma a una console Python nel punto in cui si trova attualmente, mostrando la traccia dello stack e permettendoti di manipolare le variabili. Utilizzare control-d (EOF) per continuare a funzionare (sebbene si noti che probabilmente si interromperà qualsiasi I / O ecc. Nel punto in cui si segnala, quindi non è completamente non invadente.
Ho un altro script che fa la stessa cosa, tranne per il fatto che comunica con il processo in esecuzione attraverso una pipe (per consentire il debug di processi in background, ecc.). È un po 'grande pubblicare qui, ma l'ho aggiunto come una ricetta del ricettario in pitone .
faulthandler
modulo (e il suo backport trovato su PyPI) per un gestore di segnale di livello C che stamperà lo stack Python senza richiedere che il loop dell'interprete sia reattivo.
Il suggerimento di installare un gestore di segnale è buono e lo uso molto. Ad esempio, per impostazione predefinita bzr installa un gestore SIGQUIT che invoca pdb.set_trace()
per rilasciarti immediatamente in un prompt pdb . (Vedi l' origine del modulo bzrlib.breakin per i dettagli esatti.) Con pdb puoi non solo ottenere la traccia dello stack corrente ma anche ispezionare le variabili, ecc.
Tuttavia, a volte ho bisogno di eseguire il debug di un processo in cui non avevo la lungimiranza per installare il gestore del segnale. Su Linux, puoi collegare gdb al processo e ottenere una traccia dello stack Python con alcune macro gdb. Mettere http://svn.python.org/projects/python/trunk/Misc/gdbinit in ~/.gdbinit
poi:
gdb -p
PID
pystack
Purtroppo non è totalmente affidabile, ma funziona quasi sempre.
Infine, il collegamento strace
può spesso darti una buona idea di cosa sta facendo un processo.
python-dbg
). Senza quei simboli, sembra che tu non ottenga molte informazioni utili.
Unable to locate python frame
ad ogni comando
Ho quasi sempre a che fare con più thread e generalmente il thread principale non sta facendo molto, quindi la cosa più interessante è scaricare tutti gli stack (che è più simile al dump di Java). Ecco un'implementazione basata su questo blog :
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)
Ottenere una traccia dello stack di un programma Python non preparato , eseguendo un Python di serie senza simboli di debug può essere fatto con pyrasite . Ha funzionato come un incantesimo per me su 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
(Hat tip to @Albert, la cui risposta conteneva un puntatore a questo, tra gli altri strumenti.)
dump_stacks.py
era semplicementeimport traceback; traceback.print_stack()
traceback -l
ti dà un elenco di script Python predefiniti che puoi usare ed dump_stacks.py
è uno di questi. Se stai usando il tuo (ad esempio per scrivere la traccia dello stack in un file), potrebbe essere saggio usare un nome diverso.
apt-get install gdb python-dbg
Suggerimento importante: esegui (o equivalente) prima di eseguire il pirasite, altrimenti fallirà silenziosamente. Funziona come un fascino altrimenti!
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
Puoi anche formattare correttamente la traccia dello stack, vedi i documenti .
Modifica : per simulare il comportamento di Java, come suggerito da @Douglas Leeder, aggiungi questo:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
al codice di avvio nell'applicazione. Quindi è possibile stampare lo stack inviando SIGUSR1
al processo Python in esecuzione.
Il modulo traceback ha alcune belle funzioni, tra cui: print_stack:
import traceback
traceback.print_stack()
import traceback; f = open('/tmp/stack-trace.log', 'w') traceback.print_stack(file=f) f.close()
Puoi provare il modulo del gestore dei guasti . Installalo usando pip install faulthandler
e aggiungi:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
all'inizio del programma. Quindi inviare SIGUSR1 al processo (es:) per kill -USR1 42
visualizzare il traceback Python di tutti i thread nell'output standard. Leggi la documentazione per ulteriori opzioni (es: accedi a un file) e altri modi per visualizzare il traceback.
Il modulo fa ora parte di Python 3.3. Per Python 2, vedere http://faulthandler.readthedocs.org/
Ciò che mi ha davvero aiutato qui è il suggerimento di spiv (che voterei e commenterei se avessi i punti reputazione) per ottenere una traccia dello stack da un processo Python non preparato . Solo che non ha funzionato fino a quando non ho modificato lo script gdbinit . Così:
scaricare http://svn.python.org/projects/python/trunk/Misc/gdbinit e inserirlo~/.gdbinit
modificarlo, cambiando [modifica: non più necessario; il file collegato ha già questa modifica dal 14/01/2010]PyEval_EvalFrame
inPyEval_EvalFrameEx
Allega gdb: gdb -p PID
Ottieni la traccia dello stack di Python: pystack
No symbol "co" in current context.
Vorrei aggiungere questo come commento alla risposta di Haridsv , ma non ho la reputazione di farlo:
Alcuni di noi sono ancora bloccati su una versione di Python precedente alla 2.6 (richiesto per Thread.ident), quindi ho fatto funzionare il codice in Python 2.5 (anche se senza il nome del thread visualizzato) come tale:
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
Ciò farà funzionare l'interprete in modalità debug e ti darà una traccia di ciò che l'interprete sta facendo.
Se si desidera eseguire il debug interattivo del codice, è necessario eseguirlo in questo modo:
python -m pdb yourscript.py
Questo dice all'interprete Python di eseguire il tuo script con il modulo "pdb" che è il debugger di Python, se lo esegui in questo modo l'interprete verrà eseguito in modalità interattiva, proprio come GDB
Dai un'occhiata al faulthandler
modulo, nuovo in Python 3.3. Un faulthandler
backport per l'uso in Python 2 è disponibile su PyPI.
Su Solaris, è possibile utilizzare pstack (1) Non sono necessarie modifiche al codice Python. per esempio.
# 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
che fa la stessa cosa
Se usi un sistema Linux, usa la bellezza di gdb
con le estensioni di debug di Python (può essere dentro python-dbg
o python-debuginfo
pacchetto). Aiuta anche con applicazioni multithread, applicazioni GUI e moduli C.
Esegui il tuo programma con:
$ gdb -ex r --args python <programname>.py [arguments]
Questo indica gdb
di prepararlo python <programname>.py <arguments>
e r
disfarlo.
Ora quando il programma si blocca, passa alla gdb
console, premi Ctr+Ced esegui:
(gdb) thread apply all py-list
Vedi sessione di esempio e maggiori informazioni qui e qui .
Stavo cercando da tempo una soluzione per eseguire il debug dei miei thread e l'ho trovata qui grazie a haridsv. Uso una versione leggermente semplificata utilizzando 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)
Per le mie esigenze, filtro anche i thread per nome.
Vale la pena guardare Pydb , "una versione espansa del debugger Python liberamente basata sul set di comandi gdb". Include i gestori di segnali che possono occuparsi dell'avvio del debugger quando viene inviato un segnale specificato.
Un progetto di Summer of Code del 2006 ha esaminato l'aggiunta di funzionalità di debug remoto a pydb in un modulo chiamato mpdb .
Ho hackerato insieme uno strumento che si collega a un processo Python in esecuzione e inietta del codice per ottenere una shell Python.
Vedi qui: https://github.com/albertz/pydbattach
pyrasite
funzionato perfettamente!
Può essere fatto con un'eccellente spia . È un profiler di campionamento per i programmi Python , quindi il suo compito è quello di collegarsi a processi Python e campionare i loro stack di chiamate. Quindi, py-spy dump --pid $SOME_PID
è tutto ciò che devi fare per scaricare stack di chiamate di tutti i thread nel file$SOME_PID
processo. In genere necessita di privilegi di escalation (per leggere la memoria del processo di destinazione).
Ecco un esempio di come appare per un'applicazione Python con thread.
$ 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)
pyringe è un debugger che può interagire con l'esecuzione di processi Python, tracce dello stack di stampa, variabili, ecc. senza alcuna configurazione a priori.
Anche se in passato ho usato spesso la soluzione di gestione dei segnali, può spesso essere difficile riprodurre il problema in determinati ambienti.
pyrasite
funzionato come un incantesimo per me.
Non c'è modo di collegarsi a un processo Python in esecuzione e ottenere risultati ragionevoli. Quello che faccio se i processi si bloccano è l'aggancio della striscia e il tentativo di capire cosa sta succedendo esattamente.
Sfortunatamente spesso lo strace è l'osservatore che "corregge" le condizioni della razza in modo che l'output sia inutile anche lì.
Per fare questo puoi usare PuDB , un debugger Python con un'interfaccia curses. Aggiungi
from pudb import set_interrupt_handler; set_interrupt_handler()
al tuo codice e usa Ctrl-C quando vuoi rompere. Puoi continuare c
e rompere più volte se lo perdi e vuoi riprovare.
Sono nel campo GDB con le estensioni di Python. Segui https://wiki.python.org/moin/DebuggingWithGdb , che significa
dnf install gdb python-debuginfo
o sudo apt-get install gdb python2.7-dbg
gdb python <pid of running process>
py-bt
Considera anche info threads
e 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.
quando si esegue py-bt
in gdb
?
sudo
. dovevo anche correre gdb pyton <pid>
come sudo.
Come eseguire il debug di qualsiasi funzione nella console :
Crea la funzione in cui usi pdb.set_trace () , quindi la funzione che desideri eseguire il debug.
>>> import pdb
>>> import my_function
>>> def f():
... pdb.set_trace()
... my_function()
...
Quindi chiama la funzione creata:
>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb)
Buon debug :)
Non conosco nulla di simile alla risposta di Java a SIGQUIT , quindi potresti doverlo integrare nella tua applicazione. Forse potresti creare un server in un altro thread in grado di ottenere uno stacktrace in risposta a un messaggio di qualche tipo?
In Python 3, pdb installerà automaticamente un gestore di segnale la prima volta che usi c (ont (inue)) nel debugger. Premendo successivamente Control-C, ti riporterà di nuovo lì. In Python 2, ecco un one-liner che dovrebbe funzionare anche in versioni relativamente vecchie (testato in 2.7 ma ho ricontrollato la sorgente Python su 2.4 e sembrava a posto):
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
Vale la pena imparare pdb se passi del tempo a eseguire il debug di Python. L'interfaccia è un po 'ottusa ma dovrebbe essere familiare a chiunque abbia usato strumenti simili, come gdb.
Nel caso in cui sia necessario farlo con uWSGI, ha Python Tracebacker integrato ed è solo questione di abilitarlo nella configurazione (il numero è associato al nome per ciascun lavoratore):
py-tracebacker=/var/run/uwsgi/pytrace
Una volta fatto questo, è possibile stampare backtrace semplicemente collegandosi al socket:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
Nel punto in cui viene eseguito il codice, è possibile inserire questo piccolo frammento per visualizzare una traccia dello stack stampata ben formattata. Presuppone che tu abbia una cartella chiamata logs
nella directory principale del tuo progetto.
# DEBUG: START DEBUG -->
import traceback
with open('logs/stack-trace.log', 'w') as file:
traceback.print_stack(file=file)
# DEBUG: END DEBUG --!