Pregunta

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

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

¿Algún tipo de seguimiento de pila sobre la marcha?

Preguntas relacionadas:

¿Fue útil?

Solución

Tengo un módulo que uso para situaciones como esta, en las que un proceso se ejecutará durante mucho tiempo, pero a veces se atasca 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 usar, simplemente llame a la función listen () en algún momento cuando su programa se inicie (incluso podría pegarlo en site.py para que todos los programas de Python lo usen), y dejarlo correr. En cualquier momento, envíe al proceso una señal SIGUSR1, usando kill o en python:

    os.kill(pid, signal.SIGUSR1)

Esto hará que el programa se rompa a una consola de Python en el punto en el que se encuentra actualmente, mostrándole el seguimiento de la pila 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 no intrusivo.

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

Otros consejos

La sugerencia de instalar un controlador de señal es buena, y la uso mucho. Por ejemplo, bzr instala de manera predeterminada un controlador SIGQUIT que invoca pdb.set_trace() para colocarlo inmediatamente en un pdb . (Vea el bzrlib.breakin fuente del módulo para los detalles exactos). Con pdb no solo puede obtener el seguimiento de la pila 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 controlador de señal. En Linux, puede adjuntar gdb al proceso y obtener un seguimiento de la pila de Python con algunas macros gdb. Ponga http://svn.python.org/projects/python/trunk/Misc/ gdbinit en ~/.gdbinit, luego:

  • Adjuntar gdb: gdb -p PID
  • Obtenga el seguimiento de la pila de Python: pystack

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

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

Casi siempre estoy tratando con múltiples subprocesos y el subproceso principal generalmente no está haciendo mucho, así que lo más interesante es volcar todas las pilas (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)

Obtener un seguimiento de la pila de un programa de python no preparado , que se ejecuta en un stock de python sin símbolos de depuración se puede hacer con pyrasite . Me funcionó de maravilla 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 para @Albert, cuya respuesta contenía un puntero a esto, entre otras herramientas).

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

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

También puede formatear muy bien el seguimiento de la pila, consulte los documentos .

Editar : 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 pila enviando SIGUSR1 al proceso de Python en ejecución.

El módulo traceback tiene algunos buenos funciones, entre ellas: print_stack:

import traceback

traceback.print_stack()

Puedes probar el módulo de manejo de fallas.Instálalo usando pip install faulthandler y añadir:

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

al comienzo de su programa.Luego envíe SIGUSR1 a su proceso (ej: kill -USR1 42) para mostrar el rastreo de Python de todos los subprocesos hasta la salida estándar. Leer la documentación para más opciones (ej.: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 punta de spiv (que votaría y comentaría si tuviera los puntos de reputación) para obtener un seguimiento de la pila de un proceso de Python no preparado . Excepto que no funcionó hasta que modificó el script gdbinit . Entonces:

  • descargar http://svn.python.org/projects / python / trunk / Misc / gdbinit y colóquelo en ~/.gdbinit

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

  • Adjuntar gdb: gdb -p PID

  • Obtenga el seguimiento de la pila de Python: pystack

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

Algunos de nosotros todavía estamos atascados 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 que se muestre 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 dará un rastro de lo que está haciendo el intérprete.

Si desea depurar interactivamente el código, debe ejecutarlo así:

python -m pdb yourscript.py

Eso le dice al intérprete de Python que ejecute su script con el módulo " pdb " que es el depurador de python, si lo ejecuta así, el intérprete se ejecutará en modo interactivo, al igual que GDB

Eche un vistazo al faulthandler módulo, nuevo en Python 3.3. Un <=> backport para usar en Python 2 está disponible en PyPI.

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

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

Ejecute su programa con:

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

Esto instruye gdb preparar python <programname>.py <arguments> y runidad.

Ahora, cuando su programa se cuelgue, cambie a gdb consola, presione Ctrl+C y ejecutar:

(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 una versión ligeramente simplificada que emplea 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 los hilos por nombre.

Vale la pena mirar Pydb , " una versión ampliada del depurador Python basado libremente 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 Summer of Code 2006 analizó la adición de funciones de depuración remota a pydb en un módulo llamado mpdb .

Pirateé alguna herramienta que se conecta a un proceso de Python en ejecución e inyecta algo de código para obtener un shell de Python.

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

pyringe es un depurador que puede interactuar con procesos en ejecución de Python, imprimir rastros de pila, variables, etc. sin ninguna configuración a priori.

Aunque a menudo he usado la solución de controlador de señal en el pasado, aún 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 engancharse e intentar averiguar qué está sucediendo exactamente.

Desafortunadamente, a menudo extraño es el observador que " corrige " condiciones de carrera para que la salida también sea inútil allí.

Puede usar PuDB , un depurador de Python con una interfaz de maldiciones 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 e interrumpir nuevamente varias veces si se lo pierde y desea volver a intentarlo.

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

Crear función donde usa pdb.set_trace () , luego la función que desea depurar.

>>> import pdb
>>> import my_function

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

Entonces llame a la función creada:

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

Feliz depuración :)

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

use el módulo de inspección.

  
    
      

importación inspeccionar       ayuda (inspect.stack)       Ayuda sobre la pila de funciones en el módulo de inspección:

    
  

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

Me parece muy útil.

En Python 3, pdb instalará automáticamente un controlador de señal la primera vez que use c (ont (inue)) en el depurador. Presionando Control-C luego te dejará 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 de nuevo 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 pasa algún tiempo depurando Python. La interfaz es un poco obtusa, pero debería ser familiar para cualquiera que haya utilizado herramientas similares, como gdb.

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

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

Una vez que haya hecho esto, puede imprimir la traza inversa simplemente conectándose al zócalo:

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

Estoy en el campo de GDB con las extensiones de Python.Seguir https://wiki.python.org/moin/DebuggingWithGdb, lo que significa

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

Considere también info threads y thread apply all py-bt.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top