it-swarm.com.de

Anzeigen der Stack-Ablaufverfolgung aus einer laufenden Python-Anwendung

Ich habe diese Python-Anwendung, die von Zeit zu Zeit hängen bleibt und ich kann nicht herausfinden, wo.

Gibt es eine Möglichkeit, dem Python-Interpreter den genauen Code anzuzeigen, der gerade ausgeführt wird? 

Eine Art fliegender Stacktrace?

Verwandte Fragen:

313
Seb

Ich habe ein Modul, das ich für Situationen wie diese verwende - wo ein Prozess für lange Zeit läuft, aber manchmal aus unbekannten und nicht reproduzierbaren Gründen hängen bleibt. Es ist etwas hackig und funktioniert nur unter Unix (erfordert Signale):

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

Um zu verwenden, rufen Sie einfach die listen () - Funktion zu einem Zeitpunkt auf, zu dem Ihr Programm startet (Sie könnten es sogar in site.py stecken, damit alle Python-Programme es verwenden) und es laufen lassen. Senden Sie dem Prozess zu jeder Zeit ein SIGUSR1-Signal mit kill oder in Python:

    os.kill(pid, signal.SIGUSR1)

Dies führt dazu, dass das Programm an dem Punkt, an dem es sich gerade befindet, zu einer Python-Konsole aufbricht, die Stack-Ablaufverfolgung angezeigt wird und Sie die Variablen bearbeiten können. Verwenden Sie control-d (EOF), um die Ausführung fortzusetzen. Beachten Sie jedoch, dass Sie wahrscheinlich alle E/A usw. an der Stelle unterbrechen, an der Sie das Signal erhalten haben.

Ich habe ein anderes Skript, das dasselbe tut, außer dass es mit dem laufenden Prozess über eine Pipe kommuniziert (um das Debuggen von Hintergrundprozessen usw. zu ermöglichen). Es ist ein bisschen groß, hier zu posten, aber ich habe es als Python-Kochbuch-Rezept hinzugefügt.

298
Brian

Der Vorschlag, einen Signal-Handler zu installieren, ist gut und ich benutze ihn viel. Beispielsweise installiert bzr standardmäßig einen SIGQUIT-Handler, der pdb.set_trace() aufruft, um Sie sofort in eine pdb Prompt-Anweisung abzulegen. (Genaue Informationen finden Sie in der Quelle des Moduls bzrlib.breakin .) Mit pdb können Sie nicht nur den aktuellen Stack-Trace abrufen, sondern auch Variablen prüfen usw. 

Manchmal muss ich jedoch einen Prozess debuggen, in dem ich nicht die Weitsicht hatte, um den Signal-Handler zu installieren. Unter Linux können Sie gdb an den Prozess anschließen und eine Python-Stack-Ablaufverfolgung mit einigen gdb-Makros erhalten. Setzen Sie http://svn.python.org/projects/python/trunk/Misc/gdbinit in ~/.gdbinit und dann:

  • Fügen Sie gdb hinzu: gdb -pPID
  • Rufen Sie den Python-Stack-Trace ab: pystack

Es ist leider nicht ganz zuverlässig, funktioniert aber meistens.

Durch das Anhängen von strace erhalten Sie oft eine gute Vorstellung davon, was ein Prozess tut.

139
spiv

Ich beschäftige mich fast immer mit mehreren Threads und Main-Threads machen im Allgemeinen nicht viel. Daher ist es am interessantesten, alle Stapel zu entleeren (was eher dem Javas-Dump ähnelt). Hier ist eine Implementierung basierend auf diesem 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)
65
haridsv

Ein Stack-Trace eines unvorbereiteten Python-Programms abrufen, das in einem Standard-Python ausgeführt wird, ohne Symbole zu debuggen, kann mit pyrasite erfolgen. Ich habe in Ubuntu Trusty für mich wie ein Zauber gearbeitet:

$ 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

(Hat Tipp an @Albert, dessen Antwort unter anderem einen Zeiger darauf enthält.)

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

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

Sie können den Stack-Trace auch schön formatieren, siehe docs .

Edit: Um Javas Verhalten zu simulieren, wie von @Douglas Leeder vorgeschlagen, fügen Sie Folgendes hinzu:

import signal
import traceback

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

auf den Startcode in Ihrer Anwendung. Dann können Sie den Stapel drucken, indem Sie SIGUSR1 an den laufenden Python-Prozess senden.

33
Torsten Marek

Das traceback Modul hat einige Nice-Funktionen, darunter: print_stack:

import traceback

traceback.print_stack()
26
gulgi

Sie können das Modul faulthandler ausprobieren. Installiere es mit pip install faulthandler und füge hinzu:

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

am Anfang Ihres Programms. Senden Sie dann SIGUSR1 an Ihren Prozess (Beispiel: kill -USR1 42), um den Python-Traceback aller Threads in der Standardausgabe anzuzeigen. Lesen Sie die Dokumentation für weitere Optionen (zB: In eine Datei einloggen) und andere Möglichkeiten, um das Traceback anzuzeigen.

Das Modul ist jetzt Teil von Python 3.3. Für Python 2 siehe http://faulthandler.readthedocs.org/

20
haypo

Was mir hier wirklich geholfen hat, ist spivs Tipp (das ich abstimmen und kommentieren würde, wenn ich die Reputationspunkte hätte), um aus einem unvorbereiteten Python-Prozess eine Stack-Spur zu bekommen. Nur dass es nicht funktioniert hat, bis ich das gdbinit-Skript geändert habe . So:

  • download http://svn.python.org/projects/python/trunk/Misc/gdbinit und füge es in ~/.gdbinit ein

  • bearbeiten Sie es und ändern Sie PyEval_EvalFrame in PyEval_EvalFrameEx. [edit: nicht mehr benötigt; Die verknüpfte Datei hat diese Änderung bereits am 14.01.2010.]

  • Gdb anhängen: gdb -p PID

  • Rufen Sie den Python-Stack-Trace ab: pystack

19

python -dv yourscript.py

Dadurch wird der Interpreter im Debug-Modus ausgeführt und zeigt Ihnen an, was der Interpreter macht.

Wenn Sie den Code interaktiv debuggen möchten, führen Sie ihn folgendermaßen aus:

python -m pdb yourscript.py

Dadurch wird der Python-Interpreter angewiesen, das Skript mit dem Modul "pdb" auszuführen, das der Python-Debugger ist. Wenn Sie es so ausführen, wird der Interpreter im interaktiven Modus ausgeführt, ähnlich wie bei GDB

10
Gustavo Rubio

Ich würde dies als Kommentar zu haridsvs Antwort hinzufügen, aber mir fehlt der Ruf, dies zu tun:

Einige von uns sind immer noch bei einer älteren Python-Version als 2.6 steckengeblieben (erforderlich für Thread.ident), daher habe ich den Code in Python 2.5 (obwohl der Thread-Name nicht angezeigt wurde) als solchen verwendet:

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

Schauen Sie sich das Modul faulthandler an, das in Python 3.3 neu ist. Ein faulthandler backport zur Verwendung in Python 2 ist auf PyPI verfügbar.

8
Matt Joiner

Unter Solaris können Sie pstack verwenden (1) Es sind keine Änderungen am Python-Code erforderlich. z.B.

# 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
Tim Foster

Wenn Sie auf einem Linux-System arbeiten, verwenden Sie die Attraktivität von gdb mit Python-Debug-Erweiterungen (kann sich in python-dbg oder python-debuginfo befinden). Es hilft auch bei Multithread-Anwendungen, GUI-Anwendungen und C-Modulen.

Führen Sie Ihr Programm mit:

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

Dies weist gdb an, python <programname>.py <arguments> und r darauf vorzubereiten.

Wechseln Sie beim Programmieren in die Konsole gdb und drücken Sie Ctr+C und führe aus:

(gdb) thread apply all py-list

Siehe Beispielsitzung und weitere Informationen hier und hier .

6

Ich suchte eine Zeit lang nach einer Lösung, um meine Threads zu debuggen, und dank haridsv hier gefunden. Ich verwende eine etwas vereinfachte Version mit 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)

Für meine Bedürfnisse filtere ich auch Threads nach Namen.

5
Stefan

Ich habe ein Werkzeug zusammengehackt, das an einen laufenden Python-Prozess angehängt wird und etwas Code für eine Python-Shell einfügt.

Siehe hier: https://github.com/albertz/pydbattach

3
Albert

Es lohnt sich einen Blick auf Pydb , "eine erweiterte Version des Python-Debuggers, die lose auf dem Befehlssatz gdb basiert". Dazu gehören Signalmanager, die den Start des Debuggers beim Senden eines angegebenen Signals übernehmen können.

In einem Summer of Code-Projekt von 2006 wurde das Hinzufügen von Remote-Debugging-Funktionen zu pydb in einem Modul mit dem Namen mpdb untersucht. 

3
rcoup

pyringe ist ein Debugger, der mit laufenden Python-Prozessen, Druck-Stack-Traces, Variablen usw. ohne vorherige Einrichtung interagieren kann.

Obwohl ich die Signalhandler-Lösung in der Vergangenheit häufig verwendet habe, kann es in bestimmten Umgebungen immer noch schwierig sein, das Problem zu reproduzieren.

2
Dan Lecocq

Es gibt keine Möglichkeit, sich in einen laufenden Python-Prozess einzufügen und vernünftige Ergebnisse zu erzielen. Was ich mache, wenn Prozesse blockieren, ist es, sich einzuhaken und herauszufinden, was genau passiert.

Leider ist strace oft der Beobachter, der die Rennbedingungen "fixiert", so dass die Ausgabe auch dort unbrauchbar ist.

1
Armin Ronacher

Sie können dazu PuDB verwenden, einen Python-Debugger mit einer Curses-Schnittstelle. Einfach hinzufügen 

from pudb import set_interrupt_handler; set_interrupt_handler()

zu Ihrem Code und verwenden Sie Strg-C, wenn Sie brechen möchten. Sie können mit c fortfahren und mehrmals brechen, wenn Sie es vermissen und es erneut versuchen möchten. 

1
asmeurer

So debuggen Sie eine Funktion in Console :

Funktion erstellen wo Sie verwenden Sie pdb.set_trace () , dann Funktion, die Sie debuggen möchten.

>>> import pdb
>>> import my_function

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

Dann rufen Sie die erstellte Funktion auf:

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

Viel Spaß beim Debuggen :)

1
jakvb

Ich bin mit den Python-Erweiterungen im GDB-Lager. Folgen Sie https://wiki.python.org/moin/DebuggingWithGdb , was bedeutet

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

Berücksichtigen Sie auch info threads und thread apply all py-bt.

0
user7610

verwenden Sie das Inspect-Modul.

import inspect help (inspect.stack) Hilfe zum Funktionsstack im Modul inspect:

stapel (Kontext = 1) Gibt eine Liste der Datensätze für den Stapel über dem Rahmen des Anrufers zurück.

Ich finde es sehr hilfreich.

0
Bhaskar

Für den Fall, dass Sie dies mit uWSGI tun müssen, ist Python Tracebacker eingebaut und es muss lediglich in der Konfiguration aktiviert werden (Nummer ist an den Namen jedes Arbeiters angehängt):

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

Wenn Sie dies getan haben, können Sie die Rückverfolgung einfach ausdrucken, indem Sie eine Verbindung zum Socket herstellen:

uwsgi --connect-and-read /var/run/uwsgi/pytrace1
0
Michal Čihař

Ich kenne keine ähnliche Antwort wie Javas Antwort auf SIGQUIT , daher müssen Sie sie möglicherweise in Ihre Anwendung integrieren. Vielleicht könnten Sie einen Server in einem anderen Thread erstellen, der eine Stacktrace-Antwort auf eine beliebige Nachricht erhält?

0
Douglas Leeder

In Python 3 installiert pdb automatisch einen Signal-Handler, wenn Sie c(ont(inue)) zum ersten Mal im Debugger verwenden. Wenn Sie danach die Strg-C-Taste drücken, werden Sie sofort wieder dort angezeigt. In Python 2 ist hier ein Einzeiler, der auch in relativ alten Versionen funktionieren sollte (getestet in 2.7, aber ich habe die Python-Quelle auf 2.4 zurückgesetzt und es sah okay aus):

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

pdb lohnt sich, wenn Sie Zeit damit verbringen, Python zu debuggen. Die Schnittstelle ist etwas stumpf, sollte aber jedem bekannt sein, der ähnliche Tools wie gdb verwendet hat.

0
jtatum