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é
305
demandé sur Community 2008-09-25 12:06:06

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 .

294
répondu Brian 2014-12-10 16:05:19

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.

137
répondu spiv 2014-10-01 14:02:31

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)
63
répondu haridsv 2013-08-07 06:29:09

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

40
répondu Nickolay 2015-04-26 18:22:20
>>> 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.

33
répondu Torsten Marek 2008-09-25 09:32:24

le module traceback a quelques belles fonctions, parmi lesquelles: print_stack:

import traceback

traceback.print_stack()
26
répondu gulgi 2009-04-04 00:29:50

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 PyEval_EvalFrame en PyEval_EvalFrameEx [éditez: n'est plus nécessaire; le fichier lié a déjà ce changement en date du 2010-01-14]

  • joindre gdb: gdb -p PID

  • Obtenir le python trace de la pile: pystack

19
répondu Gunnlaugur Briem 2017-05-23 11:47:17

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 /

19
répondu haypo 2014-04-03 14:54:03

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

10
répondu Gustavo Rubio 2008-09-25 08:24:24

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)
10
répondu Konstantin Naryshkin 2017-05-23 10:31:14

regardez le module faulthandler , nouveau en Python 3.3. Un faulthandler backport pour une utilisation en Python 2 est disponible sur PyPI.

8
répondu Matt Joiner 2014-01-14 20:09:37

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.
6
répondu Tim Foster 2011-08-28 21:30:38

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 .

6
répondu anatoly techtonik 2013-06-24 08:08:48

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.

5
répondu Stefan 2016-02-03 12:46:04

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 .

3
répondu rcoup 2009-01-29 01:28:29

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

3
répondu Albert 2012-04-06 18:49:30

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.

2
répondu Dan Lecocq 2014-05-01 09:40:07

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.

1
répondu Armin Ronacher 2008-09-25 09:09:54

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.

1
répondu asmeurer 2013-04-27 00:54:57

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?

0
répondu Douglas Leeder 2008-09-25 09:06:39

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.

0
répondu Alison R. 2011-12-21 16:42:05

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.

0
répondu jtatum 2014-12-14 02:16:25

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
0
répondu Michal Čihař 2015-04-28 06:15:05

je suis dans le camp GDB avec les extensions python. Suivre https://wiki.python.org/moin/DebuggingWithGdb , qui signifie

  1. dnf install gdb python-debuginfo ou sudo apt-get install gdb python2.7-dbg
  2. gdb python <pid of running process>
  3. py-bt

aussi considérer info threads et thread apply all py-bt .

0
répondu user7610 2018-04-18 12:31:10