Mostrar el seguimiento de stack de una aplicación Python en ejecución

Tengo esta aplicación de Python que se atasca de vez en cuando y no puedo averiguar dónde.

¿Hay alguna forma de indicar al intérprete de Python que le muestre el código exacto que se está ejecutando?

¿Algún tipo de astackmiento sobre la marcha?

Preguntas relacionadas:

  • Imprima la stack de llamadas actual desde un método en código Python
  • Compruebe lo que está haciendo un proceso en ejecución: imprima el seguimiento de la stack de un progtwig Python no documentado

    Tengo un módulo que utilizo para situaciones como esta, donde un proceso se ejecutará durante mucho tiempo, pero a veces se bloquea por razones desconocidas e irreproducibles. Es un poco hacky, y solo funciona en Unix (requiere señales):

    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 

    Para usarlo, simplemente llame a la función listen () en algún momento cuando su progtwig se inicie (incluso podría pegarlo en site.py para que todos los progtwigs de Python lo usen), y deje que se ejecute. En cualquier momento, envíe al proceso una señal SIGUSR1, utilizando kill o en python:

      os.kill(pid, signal.SIGUSR1) 

    Esto hará que el progtwig pase a una consola de python en el punto en el que se encuentra actualmente, mostrándole el seguimiento de la stack y permitiéndole manipular las variables. Use control-d (EOF) para continuar ejecutándose (aunque tenga en cuenta que probablemente interrumpirá cualquier E / S, etc. en el punto que indique, por lo que no es totalmente intrusivo.

    Tengo otro script que hace lo mismo, excepto que se comunica con el proceso en ejecución a través de un conducto (para permitir la depuración de procesos en segundo plano, etc.). Es un poco grande publicar aquí, pero lo he agregado como una receta de un libro de cocina de python .

    La sugerencia de instalar un manejador de señales es buena, y la uso mucho. Por ejemplo, bzr instala de forma predeterminada un controlador SIGQUIT que invoca a pdb.set_trace() para pdb.set_trace() inmediatamente en un indicador de pdb . (Consulte la fuente del módulo bzrlib.breakin para conocer los detalles exactos). Con pdb no solo puede obtener el seguimiento de stack actual sino también inspeccionar variables, etc.

    Sin embargo, a veces necesito depurar un proceso en el que no tuve la previsión de instalar el manejador de señales. En Linux, puede adjuntar gdb al proceso y obtener un seguimiento de la stack de Python con algunas macros de gdb. Coloque http://svn.python.org/projects/python/trunk/Misc/gdbinit en ~/.gdbinit , luego:

    • Adjuntar gdb: gdb -p PID
    • Obtener el seguimiento de la stack de python: pystack

    Desafortunadamente, no es totalmente confiable, pero funciona la mayor parte del tiempo.

    Finalmente, adjuntar strace menudo puede darle una buena idea de lo que está haciendo un proceso.

    Casi siempre trato con varios subprocesos y el subproceso principal no suele hacer mucho, así que lo más interesante es volcar todas las stacks (que es más como el volcado de Java). Aquí hay una implementación basada en este 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) 

    La obtención de un seguimiento de la stack de un progtwig de Python no preparado , ejecutándose en un python común sin símbolos de depuración se puede hacer con pyrasite . Trabajó como un encanto para mí en 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 

    (Sugerencia de sombrero a @Albert, cuya respuesta contenía un indicador de esto, entre otras herramientas).

     >>> import traceback >>> def x(): >>> print traceback.extract_stack() >>> x() [('', 1, '', None), ('', 2, 'x', None)] 

    También puede dar un buen formato a la traza de la stack, ver los documentos .

    Edición : para simular el comportamiento de Java, como lo sugiere @Douglas Leeder, agregue esto:

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

    al código de inicio en su aplicación. Luego, puede imprimir la stack enviando SIGUSR1 al proceso de Python en ejecución.

    El módulo de rastreo tiene algunas funciones agradables, entre ellas: print_stack:

     import traceback traceback.print_stack() 

    Puedes probar el módulo faulthandler . pip install faulthandler usando pip install faulthandler y agrega:

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

    Al comienzo de tu progtwig. Luego envíe SIGUSR1 a su proceso (por ejemplo, kill -USR1 42 ) para mostrar el seguimiento de Python de todos los subprocesos a la salida estándar. Lea la documentación para obtener más opciones (por ejemplo, iniciar sesión en un archivo) y otras formas de mostrar el rastreo.

    El módulo ahora es parte de Python 3.3. Para Python 2, consulte http://faulthandler.readthedocs.org/

    Lo que realmente me ayudó aquí es la sugerencia de spiv (que votaría y comentaría si tuviera los puntos de reputación) para obtener un seguimiento de la stack de un proceso de Python no preparado . Excepto que no funcionó hasta que modifiqué el script gdbinit . Asi que:

    • descargue http://svn.python.org/projects/python/trunk/Misc/gdbinit y póngalo en ~/.gdbinit

    • PyEval_EvalFrame , cambiando PyEval_EvalFrame a PyEval_EvalFrameEx [editar: ya no es necesario; el archivo vinculado ya tiene este cambio a partir del 2010-01-14]

    • Adjuntar gdb: gdb -p PID

    • Obtener el seguimiento de la stack de python: pystack

    Agregaría esto como un comentario a la respuesta de haridsv , pero me falta la reputación para hacerlo:

    Algunos de nosotros todavía estamos atrapados en una versión de Python anterior a 2.6 (requerida para Thread.ident), así que obtuve el código funcionando en Python 2.5 (aunque sin el nombre del hilo) como tal:

     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

    Eso hará que el intérprete se ejecute en modo de depuración y le dé un seguimiento de lo que está haciendo el intérprete.

    Si desea depurar el código de forma interactiva, debe ejecutarlo de esta manera:

    python -m pdb yourscript.py

    Eso le indica al intérprete de python que ejecute su script con el módulo “pdb”, que es el depurador de python, si lo ejecuta de manera similar a como se ejecutará el intérprete en modo interactivo, al igual que GDB

    Eche un vistazo al módulo faulthandler , nuevo en Python 3.3. Un backport de faulthandler para uso en Python 2 está disponible en PyPI.

    En Solaris, puede usar pstack (1) No es necesario realizar cambios en el código de Python. p.ej.

     # 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 () ] [ /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 está en un sistema Linux, use la genialidad de gdb con las extensiones de depuración de Python (puede estar en el paquete python-dbg o python-debuginfo ). También ayuda con aplicaciones multiproceso, aplicaciones GUI y módulos C.

    Ejecute su progtwig con:

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

    Esto le indica a gdb que prepare python .py y lo r .

    Ahora, cuando programe lockings, cambie a la consola gdb , presione Ctr + C y ejecute:

     (gdb) thread apply all py-list 

    Ver sesión de ejemplo y más información aquí y aquí .

    Estuve buscando por un tiempo una solución para depurar mis hilos y la encontré aquí gracias a haridsv. Utilizo la versión ligeramente simplificada empleando el 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) 

    Para mis necesidades también filtro hilos por nombre.

    Vale la pena ver Pydb , “una versión expandida del depurador de Python basada en el conjunto de comandos gdb”. Incluye gestores de señales que pueden encargarse de iniciar el depurador cuando se envía una señal específica.

    Un proyecto de 2006 Summer of Code consideró agregar funciones de depuración remota a pydb en un módulo llamado mpdb .

    Hacké una herramienta que se adjunta en un proceso Python en ejecución e inyecta algo de código para obtener un shell de Python.

    Consulte aquí: https://github.com/albertz/pydbattach

    pyringe es un depurador que puede interactuar ejecutando procesos de Python, imprimiendo trazas de la stack, variables, etc. sin ninguna configuración a priori.

    Si bien en el pasado he usado con frecuencia la solución del manejador de señales, todavía puede ser difícil reproducir el problema en ciertos entornos.

    No hay forma de conectarse a un proceso de Python en ejecución y obtener resultados razonables. Lo que hago si los procesos se bloquean es conectar la estrategia y tratar de averiguar qué está sucediendo exactamente.

    Desafortunadamente, a menudo strace es el observador que “arregla” las condiciones de carrera para que la salida también sea inútil allí.

    Puedes usar PuDB , un depurador de Python con una interfaz de curses para hacer esto. Solo agrega

     from pudb import set_interrupt_handler; set_interrupt_handler() 

    a su código y use Ctrl-C cuando quiera romper. Puede continuar con c y romper de nuevo varias veces si lo pierde y desea volver a intentarlo.

    Cómo depurar cualquier función en la consola :

    Cree una función donde use pdb.set_trace () , luego la función que desea depurar.

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

    Entonces llama a la función creada:

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

    Feliz depuración 🙂

    No conozco nada similar a la respuesta de java a SIGQUIT , por lo que es posible que tenga que integrarlo en su aplicación. ¿Tal vez podría hacer un servidor en otro hilo que pueda obtener un seguimiento de stack en respuesta a un mensaje de algún tipo?

    Utilice el módulo de inspección.

    Importar ayuda de inspección (inspect.stack) Ayuda en la stack de funciones en módulo inspeccionar:

    stack (context = 1) Devuelve una lista de registros para la stack sobre el marco de la persona que llama.

    Lo encuentro muy útil por cierto.

    En Python 3, pdb instalará automáticamente un controlador de señales la primera vez que use c (ont (inue)) en el depurador. Presionando Control-C luego lo dejará caer de nuevo allí. En Python 2, aquí hay una línea que debería funcionar incluso en versiones relativamente antiguas (probado en 2.7, pero verifiqué la fuente de Python a 2.4 y se veía bien):

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

    Vale la pena aprender pdb si dedicas mucho tiempo a depurar Python. La interfaz es un poco obtusa pero debería ser familiar para cualquiera que haya usado herramientas similares, como gdb.

    En caso de que necesite hacer esto con uWSGI, tiene Python Tracebacker integrado y solo es necesario habilitarlo en la configuración (el número está adjunto al nombre de cada trabajador):

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

    Una vez que haya hecho esto, puede imprimir la traza de retroceso simplemente conectándose al socket:

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

    Estoy en el campamento de GDB con las extensiones de python. Siga https://wiki.python.org/moin/DebuggingWithGdb , que significa

    1. dnf install gdb python-debuginfo o sudo apt-get install gdb python2.7-dbg
    2. gdb python
    3. py-bt

    También considere los info threads y el thread apply all py-bt .