Affichage de la trace de la pile d'une application Python en cours D'exécution
j'ai cette application Python qui est coincée de temps en temps et je ne peux pas trouver où.
y a-t-il un moyen de faire signe à l'interpréteur Python pour vous montrer le code exact qui s'exécute?
une sorte de stacktrace à la volée?
questions connexes:
- Imprimer la pile d'appel en cours à partir d'une méthode dans le code Python
- vérifier ce qu'un processus en cours d'exécution est en train de faire: print stack trace d'un programme Python Non instrumenté
24 réponses
j'ai le module que j'utilise pour des situations comme celle - ci-où un processus va fonctionner pendant une longue période mais se bloque parfois pour des raisons inconnues et irréproductibles. Son un peu hacky, et ne fonctionne que sur unix (nécessite des signaux):
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
pour utiliser, il suffit d'appeler la fonction listen() à un moment donné quand votre programme démarre (vous pouvez même le coller dans site.py pour que tous les programmes python l'utilisent), et le laisser fonctionner. À tout moment, envoyer un signal SIGUSR1 au processus, en utilisant kill, ou en python:
os.kill(pid, signal.SIGUSR1)
cela provoquera le programme à se casser sur une console python au point où il est actuellement, vous montrant la trace de la pile, et vous permettant de manipuler les variables. Utilisez le contrôle - d (EOF) pour continuer à courir (notez cependant que vous allez probablement interrompre toute entrée/sortie etc au point que vous signalez, donc ce n'est pas complètement non-intrusif.
j'ai un autre script qui fait la même chose, sauf qu'il communique avec l'exécution de processus à travers un tuyau (pour permettre le débogage des processus de fond etc). C'est un peu grand pour poster ici, mais je l'ai ajouté comme un Python cookbook recette .
la suggestion d'installer un gestionnaire de signal est bonne, et je l'utilise beaucoup. Par exemple, bzr installe par défaut un gestionnaire SIGQUIT qui invoque pdb.set_trace()
pour vous déposer immédiatement dans une invite pdb . (Voir le bzrlib.breakin source du module pour les détails exacts.) Avec pdb vous pouvez non seulement obtenir la trace courante de la pile, mais aussi inspecter les variables, etc.
cependant, parfois J'ai besoin de déboguer un processus que je n'ai pas eu la bonne idée d'installer le gestionnaire de signal. Sous linux, vous pouvez attacher gdb au processus et obtenir une trace de la pile python avec quelques macros gdb. Mettre http://svn.python.org/projects/python/trunk/Misc/gdbinit in ~/.gdbinit
, puis:
- joindre gdb:
gdb -p
PID
- Obtenir le python trace de la pile:
pystack
Il n'est pas totalement fiable, malheureusement, mais il fonctionne la plupart du temps.
enfin, joindre strace
peut souvent vous donner une bonne idée de ce qu'un processus fait.
j'ai presque toujours affaire à plusieurs threads et le thread principal ne fait généralement pas beaucoup, donc ce qui est le plus intéressant est de vider toutes les piles (ce qui est plus comme le dump de Java). Voici une implémentation basée sur ce 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)
obtenir une trace de pile d'un programme non préparé Python, fonctionnant dans un programme Python stock sans débogage des symboles peut être fait avec pyrasite . Travaillé comme un charme pour moi sur 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
(astuce Chapeau à @Albert, dont la réponse contenait un pointeur de cela, parmi d'autres outils.)
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]
vous pouvez aussi joliment formater la trace de pile, voir le docs .
Edit : pour simuler le comportement de Java, comme suggéré par @Douglas Leeder, ajouter ceci:
import signal
import traceback
signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))
au code de démarrage de votre application. Ensuite, vous pouvez imprimer la pile en envoyant SIGUSR1
au processus Python en cours d'exécution.
ce qui m'a vraiment aidé ici est spiv's tip (sur lequel je voterais et commenterais si j'avais les points de réputation) pour obtenir une trace de pile à partir d'un non préparé processus Python. Sauf qu'il n'a pas fonctionné jusqu'à ce que je modifié le script gdbinit . So:
-
télécharger http://svn.python.org/projects/python/trunk/Misc/gdbinit et mettez-le dans
~/.gdbinit
-
éditez-le, en changeant[éditez: n'est plus nécessaire; le fichier lié a déjà ce changement en date du 2010-01-14]PyEval_EvalFrame
enPyEval_EvalFrameEx
-
joindre gdb:
gdb -p PID
-
Obtenir le python trace de la pile:
pystack
vous pouvez essayer le module faulthandler . L'installer en utilisant pip install faulthandler
et ajouter:
import faulthandler, signal
faulthandler.register(signal.SIGUSR1)
au début de votre programme. Ensuite, envoyez SIGUSR1 à votre processus (ex: kill -USR1 42
) pour afficher le traceback Python de tous les threads à la sortie standard. Lire la documentation pour plus d'options (ex: log dans un fichier) et d'autres façons d'afficher le traceback.
le module fait maintenant partie de Python 3.3. Pour Python 2, voir http://faulthandler.readthedocs.org /
python -dv yourscript.py
qui permettra à l'interpréteur de s'exécuter en mode de débogage et de vous donner une trace de ce qu'il fait.
si vous voulez déboguer le code de façon interactive, vous devez l'exécuter comme suit:
python-m pdb yourscript.py
qui dit à l'interpréteur python d'exécuter votre script avec le module "pdb" qui est le python débogueur, si vous l'exécutez, comme l'interprète sera exécuté en mode interactif, un peu comme GDB
je voudrais ajouter ceci comme un commentaire à réponse de haridsv , mais je manque la réputation de le faire:
certains d'entre nous sont encore coincés sur une version de Python plus ancienne que 2.6 (requis pour le Thread.ident), donc j'ai eu le code fonctionnant en python 2.5 (bien que sans que le nom du thread soit affiché) comme tel:
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)
regardez le module faulthandler
, nouveau en Python 3.3. Un faulthandler
backport pour une utilisation en Python 2 est disponible sur PyPI.
sur Solaris, vous pouvez utiliser pstack(1) Aucune modification au code python n'est nécessaire. par exemple.
# 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.
si vous êtes sur un système Linux, utilisez la qualité gdb
avec les extensions de débogage Python (peut être dans le paquet python-dbg
ou python-debuginfo
). Il aide également avec les applications multithreaded, les applications GUI et les modules C.
exécutez votre programme avec:
$ gdb -ex r --args python <programname>.py [arguments]
la présente instruction gdb
de préparer python <programname>.py <arguments>
et r
un it.
maintenant quand vous programmez pends, basculez dans gdb
console, appuyez sur Ctr+C et exécutez:
(gdb) thread apply all py-list
Voir exemple de session et plus d'infos ici et ici .
je cherchais depuis un moment une solution pour déboguer mon fils et je l'ai trouvé ici, grâce à haridsv. J'utilise une version légèrement simplifiée en utilisant le 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)
pour mes besoins Je filtre aussi les threads par leur nom.
il vaut la peine de regarder Pydb ,"une version étendue du débogueur Python vaguement basée sur la commande gdb set". Il inclut des gestionnaires de signaux qui peuvent prendre soin de démarrer le débogueur lorsqu'un signal spécifié est envoyé.
un projet de L'été 2006 a examiné l'ajout de fonctions de débogage à distance à pydb dans un module appelé mpdb .
j'ai hacké un outil qui se fixe dans un processus Python en cours d'exécution et injecte du code pour obtenir un shell Python.
voir ici: https://github.com/albertz/pydbattach
pyringe est un débogueur qui peut interagir avec les processus Python en cours d'exécution, les traces de la pile d'impression, les variables, etc. sans configuration a priori.
bien que j'aie souvent utilisé la solution du gestionnaire de signaux dans le passé, il peut encore être difficile de reproduire le problème dans certains environnements.
il n'y a aucun moyen de se connecter à un processus Python en cours d'exécution et d'obtenir des résultats raisonnables. Ce que je fais si les processus se bloquent, c'est d'accrocher strace et d'essayer de comprendre ce qui se passe exactement.
Malheureusement souvent strace est l'observateur qui "corrige" conditions de course, de sorte que la sortie est inutile là aussi.
vous pouvez utiliser PuDB , un débogueur Python avec une interface curses pour le faire. Il suffit d'ajouter
from pudb import set_interrupt_handler; set_interrupt_handler()
à votre code et utilisez Ctrl-C lorsque vous voulez casser. Vous pouvez continuer avec c
et rompre à nouveau plusieurs fois si vous le manquez et que vous voulez essayer à nouveau.
Je ne sais rien de similaire à réponse de java à SIGQUIT , donc vous pourriez avoir à le construire dans votre application. Peut-être que vous pourriez faire un serveur dans un autre thread qui peut obtenir un stacktrace sur la réponse à un message d'une sorte?
utiliser le module inspecter.
importer inspecter de l'aide(inspection.pile) Aide sur la pile de fonction dans le module inspecter:
pile(context=1) Retourner une liste des enregistrements pour la pile au-dessus du cadre de l'appelant.
je trouve cela très utile en effet.
en Python 3, pdb installera automatiquement un gestionnaire de signal la première fois que vous utilisez c(ont(inue)) dans le débogueur. En appuyant sur Contrôle-C, vous tomberez à l'eau. En Python 2, Voici un one-liner qui devrait fonctionner même dans les versions relativement anciennes (testé en 2.7 mais j'ai vérifié les sources Python jusqu'en 2.4 et ça avait l'air correct):
import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))
pdb vaut la peine d'apprendre si vous passez un peu de temps à déboguer Python. L'interface est un peu obtus, mais devrait être familier à toute personne qui a utilisé des outils similaires, tels que gdb.
dans le cas où vous avez besoin de faire cela avec uWSGI, il a Python Tracebacker intégré et il est juste question de l'activer dans la configuration (numéro est attaché au nom pour chaque travailleur):
py-tracebacker=/var/run/uwsgi/pytrace
une fois que vous avez fait ceci, vous pouvez imprimer la rétrotrace simplement en se connectant à la socket:
uwsgi --connect-and-read /var/run/uwsgi/pytrace1
je suis dans le camp GDB avec les extensions python. Suivre https://wiki.python.org/moin/DebuggingWithGdb , qui signifie
-
dnf install gdb python-debuginfo
ousudo apt-get install gdb python2.7-dbg
-
gdb python <pid of running process>
-
py-bt
aussi considérer info threads
et thread apply all py-bt
.