python - para - traceback cisco
Mostrar el seguimiento de pila de una aplicaciĆ³n Python en ejecuciĆ³n (24)
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 está ejecutando?
¿Algún tipo de apilamiento sobre la marcha?
Preguntas relacionadas:
Casi siempre trato con varios subprocesos y el subproceso principal no suele hacer mucho, así que lo más interesante es volcar todas las pilas (que es más parecido al 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)
Eche un vistazo al módulo de faulthandler
, nuevo en Python 3.3. Un backport de faulthandler
para uso en Python 2 está disponible en PyPI.
El módulo de traceback tiene algunas funciones agradables, entre ellas: print_stack:
import traceback
traceback.print_stack()
En Python 3, pdb instalará automáticamente un manejador 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 revisé 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 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 (<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.
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
-
dnf install gdb python-debuginfo
osudo apt-get install gdb python2.7-dbg
-
gdb python <pid of running process>
-
py-bt
También considere los info threads
y el thread apply all py-bt
.
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.
Introduje una herramienta que se une a 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
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 pdb.set_trace()
para pdb.set_trace()
inmediatamente en un indicador de pdb . (Consulte la bzrlib.breakin módulo bzrlib.breakin para conocer los detalles exactos). Con pdb no solo puede obtener el seguimiento de 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ñales. En Linux, puede adjuntar gdb al proceso y obtener un seguimiento de la pila 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 pila 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.
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 pila de un proceso 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
, cambiandoPyEval_EvalFrame
aPyEval_EvalFrameEx
Adjuntar gdb:
gdb -p PID
Obtener el seguimiento de pila de python:
pystack
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 usted podría hacer un servidor en otro hilo que puede obtener un seguimiento de pila en respuesta a un mensaje de algún tipo?
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í.
Obtener un seguimiento de la pila de un programa 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).
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 programa. 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 ver 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/
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. Puedes continuar con c
y romper de nuevo varias veces si lo pierdes y quieres volver a intentarlo.
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 programa con:
$ gdb -ex r --args python <programname>.py [arguments]
Esto le indica a gdb
que prepare python <programname>.py <arguments>
y lo r
.
Ahora, cuando programe bloqueos, 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 wiki.python.org/moin/DebuggingWithGdb y here .
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 programa se inicie (incluso podría pegarlo en site.py para que todos los programas de Python lo usen), y déjelo funcionar. 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 pase 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 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 publicarlo aquí, pero lo he agregado como una receta de libro de cocina de python .
Utilice el módulo de inspección.
Importar ayuda de inspección (inspect.stack) Ayuda en la pila de funciones en módulo inspeccionar:
stack (context = 1) Devuelve una lista de registros para la pila sobre el marco de la persona que llama.
Lo encuentro muy útil por cierto.
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 .
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)
pyringe es un depurador que puede interactuar ejecutando procesos de Python, imprimiendo trazas de la pila, 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.
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 la siguiente manera:
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 de manera similar a como se ejecutará el intérprete en modo interactivo, al igual que GDB
>>> import traceback
>>> def x():
>>> print traceback.extract_stack()
>>> x()
[(''<stdin>'', 1, ''<module>'', None), (''<stdin>'', 2, ''x'', None)]
También puede formatear bien la traza de pila, ver los docs .
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 pila enviando SIGUSR1
al proceso Python en ejecución.