Question

J'ai cette application Python qui reste bloquée de temps en temps et je ne peux pas savoir où.

Existe-t-il un moyen de signaler à l'interprète Python de vous montrer le code exact en cours d'exécution?

Une sorte de stacktrace à la volée?

Questions connexes:

Était-ce utile?

La solution

J'ai un module que j'utilise dans des situations comme celle-ci: un processus fonctionnera longtemps, mais reste bloqué parfois pour des raisons inconnues et non reproductibles. C'est un peu hacky, et ne fonctionne que sous 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 l'utiliser, appelez simplement la fonction listen () à un moment donné au démarrage de votre programme (vous pouvez même le coller dans site.py pour que tous les programmes python l'utilisent) et le laisser fonctionner. A tout moment, envoyez au processus un signal SIGUSR1 à l’aide de kill ou en python:

    os.kill(pid, signal.SIGUSR1)

Cela fera que le programme se brisera sur une console python au point où il se trouve actuellement, vous montrant la trace de la pile et vous permettant de manipuler les variables. Utilisez control-d (EOF) pour continuer à fonctionner (mais notez que vous interromprez probablement les E / S, etc. au moment que vous signalez, afin que ce ne soit pas totalement non intrusif.

J'ai un autre script qui fait la même chose, sauf qu'il communique avec le processus en cours via un canal (pour permettre le débogage des processus en arrière-plan, etc.). C'est un peu gros de poster ici, mais je l'ai ajouté en tant que recette de livre de recettes python .

Autres conseils

La suggestion d'installer un gestionnaire de signal est une bonne idée, et je l'utilise beaucoup. Par exemple, bzr installe par défaut un gestionnaire SIGQUIT qui appelle pdb.set_trace() pour vous déposer immédiatement dans un pdb . (Voir le bzrlib.breakin la source du module pour les détails exacts.) Avec pdb, vous pouvez non seulement obtenir le tracé actuel de la pile, mais aussi inspecter les variables, etc.

Cependant, il est parfois nécessaire de déboguer un processus pour lequel je n'avais pas la clairvoyance d'installer le gestionnaire de signal. Sous Linux, vous pouvez associer gdb au processus et obtenir une trace de pile Python avec des macros gdb. Mettez http://svn.python.org/projects/python/trunk/Misc/ gdbinit dans ~/.gdbinit, puis:

  • Attachez gdb: gdb -p PID
  • Récupère la trace de la pile python: pystack

Ce n’est malheureusement pas totalement fiable, mais cela fonctionne la plupart du temps.

Enfin, attacher strace peut souvent vous donner une bonne idée du processus.

Je suis presque toujours confronté à plusieurs threads et le thread principal ne fait généralement pas grand-chose, donc le plus intéressant est de vider toutes les piles (ce qui ressemble plus au 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 python non préparé , s'exécutant dans un python standard sans symboles de débogage peut être effectué avec pyrasite . A fonctionné comme un charme pour moi dans 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 de chapeau à @Albert, dont la réponse contenait un pointeur, entre autres outils.)

>>> import traceback
>>> def x():
>>>    print traceback.extract_stack()

>>> x()
[('<stdin>', 1, '<module>', None), ('<stdin>', 2, 'x', None)]

Vous pouvez également mettre en forme la trace de pile, voir la docs .

Modifier : pour simuler le comportement de Java, comme suggéré par @Douglas Leeder, ajoutez ceci:

import signal
import traceback

signal.signal(signal.SIGUSR1, lambda sig, stack: traceback.print_stack(stack))

au code de démarrage dans votre application. Ensuite, vous pouvez imprimer la pile en envoyant SIGUSR1 le processus Python en cours d’exécution.

Le module traceback a quelques fonctionnalités intéressantes. fonctions, parmi lesquelles: print_stack:

import traceback

traceback.print_stack()

Vous pouvez essayer le module faulthandler . Installez-le avec pip install faulthandler et ajoutez:

import faulthandler, signal
faulthandler.register(signal.SIGUSR1)

au début de votre programme. Envoyez ensuite SIGUSR1 à votre processus (ex: kill -USR1 42) pour afficher le suivi Python de tous les threads sur la sortie standard. Lisez la documentation pour plus d'options (par exemple, enregistrer dans un fichier) et pour afficher le suivi.

Le module fait maintenant partie de Python 3.3. Pour Python 2, voir http://faulthandler.readthedocs.org/

Ce qui m'a vraiment aidé ici, c'est Le conseil de spiv (que je voterais et commenterais si j’avais les points de réputation) pour obtenir une trace de pile d’un processus Python non préparé . Sauf que cela n'a pas fonctionné jusqu'à ce que j'ai modifié le script gdbinit . Donc:

  • téléchargez http://svn.python.org/projects / python / trunk / Misc / gdbinit et mettez-le dans ~/.gdbinit

  • le modifier en remplaçant PyEval_EvalFrame par PyEval_EvalFrameEx [edit: n'est plus nécessaire; le fichier lié contient déjà cette modification à partir du 14/01/2010]

  • Attachez gdb: gdb -p PID

  • Obtenir la trace de la pile python: pystack

J'ajouterais ceci comme commentaire à haridsv réponse , mais la réputation me manque pour le faire:

Certains d'entre nous sont toujours bloqués sur une version de Python antérieure à la version 2.6 (requise pour Thread.ident). J'ai donc fait fonctionner le code dans Python 2.5 (bien que le nom du fil ne soit pas affiché) en tant que 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)

python -dv yourscript.py

Cela fera que l'interprète s'exécutera en mode débogage et vous donnera une trace de ce qu'il fait.

Si vous souhaitez déboguer le code de manière interactive, exécutez-le comme suit:

python -m pdb yourscript.py

Cela indique à l'interprète python d'exécuter votre script avec le module " pdb " qui est le débogueur python, si vous l’exécutez comme cela, l’interprète sera exécuté en mode interactif, un peu comme GDB

Consultez le module faulthandler , nouveau dans Python 3.3. Un <=> backport à utiliser dans Python 2 est disponible dans PyPI.

Sous Solaris, vous pouvez utiliser pstack (1) Aucune modification du 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 l’atout génial de gdb avec les extensions de débogage Python (peut être dans le package python-dbg ou python-debuginfo). Il est également utile pour les applications multithreads, les applications graphiques et les modules C.

Exécutez votre programme avec:

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

Ceci indique à python <programname>.py <arguments> de préparer r et <=> un it.

Maintenant, lorsque vous programmez des blocages, passez à la <=> console, appuyez sur Ctr + C et exécutez:

(gdb) thread apply all py-list

Voir exemple de session et plus d'informations ici et ici .

Je cherchais depuis longtemps une solution pour déboguer mes threads et je l’ai trouvée ici grâce à haridsv. J'utilise une version légèrement simplifiée en utilisant 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 également les threads par nom.

Cela vaut la peine de regarder Pydb , & "; une version développée du débogueur Python vaguement basé sur la commande gdb, définissez " ;. Il inclut des gestionnaires de signaux qui peuvent s’occuper de démarrer le débogueur lorsqu’un signal spécifié est envoyé.

Un projet Summer of Code de 2006 visait à ajouter des fonctionnalités de débogage à distance à pydb dans un module appelé mpdb .

J'ai piraté ensemble un outil qui s'attache à 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 pouvant interagir avec les processus Python en cours d'exécution, imprimer les traces de la pile, les variables, etc. sans configuration a priori.

Bien que j'aie souvent utilisé la solution de traitement de signal par le passé, il est toujours difficile de reproduire le problème dans certains environnements.

Il n’existe aucun moyen de s’inscrire à un processus python en cours d’exécution pour obtenir des résultats raisonnables. Ce que je fais si les processus se bloquent, c’est d’attaquer strace et de tenter de comprendre ce qui se passe exactement.

Malheureusement, strace est souvent l'observateur qui " corrige " conditions de course pour que la sortie soit 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 interrompre à nouveau plusieurs fois si vous le manquez et que vous souhaitez réessayer.

Comment déboguer une fonction dans la console :

Créer une fonction dans laquelle vous utilisez pdb.set_trace () , puis vous souhaitez déboguer.

>>> import pdb
>>> import my_function

>>> def f():
...     pdb.set_trace()
...     my_function()
... 

Appelez ensuite la fonction créée:

>>> f()
> <stdin>(3)f()
(Pdb) s
--Call--
> <stdin>(1)my_function()
(Pdb) 

Bon débogage:)

Je ne connais rien de similaire à réponse de Java à SIGQUIT , vous devrez peut-être l'intégrer à votre application. Peut-être pourriez-vous créer un serveur dans un autre thread pouvant obtenir un stacktrace en réponse à un message quelconque?

utilisez le module inspecter.

  
    
      

import inspecter       aide (inspect.stack)       Aide sur la pile de fonctions dans le module inspecter:

    
  

pile (contexte = 1)     Retourne une liste d'enregistrements pour la pile au-dessus du cadre de l'appelant.

Je trouve cela très utile en effet.

Dans Python 3, pdb installera automatiquement un gestionnaire de signaux la première fois que vous utilisez c (ont (inue)) dans le débogueur. Appuyez ensuite sur Ctrl-C pour vous y laisser tomber. En Python 2, voici un one-liner qui devrait fonctionner même dans des versions relativement anciennes (testé dans la version 2.7, mais j’ai vérifié l’origine du code Python à la version 2.4 et tout s’avérait bien):

import pdb, signal
signal.signal(signal.SIGINT, lambda sig, frame: pdb.Pdb().set_trace(frame))

pdb vaut la peine d’être appris si vous passez du temps à déboguer Python. L’interface est un peu obtuse mais devrait être familière à quiconque a utilisé des outils similaires, tels que gdb.

Si vous avez besoin de le faire avec uWSGI, Tracebacker Python intégré et il suffit de l'activer dans la configuration (le numéro est associé au nom de chaque travailleur):

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

Une fois que cela est fait, vous pouvez imprimer la trace en connectant simplement à la prise:

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

Je suis dans le camp GDB avec les extensions python. Suivez https://wiki.python.org/moin/DebuggingWithGdb , ce 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

Prenez également en compte info threads et thread apply all py-bt.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top