Wie werden Goroutine-Stacktraces ausgegeben?

Antworten:

101

Verwenden Sie from, um den Stack-Trace für die aktuelle Goroutine zu drucken .PrintStack()runtime/debug

PrintStack druckt den von Stack zurückgegebenen Stack-Trace als Standardfehler.

Beispielsweise:

import(
   "runtime/debug"
)
...    
debug.PrintStack()

Um den Stack-Trace für alle Goroutinen zu drucken, verwenden Sie Lookupund WriteTovon runtime/pprof.

func Lookup(name string) *Profile
// Lookup returns the profile with the given name,
// or nil if no such profile exists.

func (p *Profile) WriteTo(w io.Writer, debug int) error
// WriteTo writes a pprof-formatted snapshot of the profile to w.
// If a write to w returns an error, WriteTo returns that error.
// Otherwise, WriteTo returns nil.

Jedes Profil hat einen eindeutigen Namen. Einige Profile sind vordefiniert:

goroutine - Stack-Traces aller aktuellen Goroutines-
Heaps - eine Stichprobe aller Heap-Zuordnungen
threadcreate - Stack-Traces, die zur Erstellung neuer
Block- OS-Threads führten - Stack-Traces, die zum Blockieren von Synchronisationsprimitiven führten

Beispielsweise:

pprof.Lookup("goroutine").WriteTo(os.Stdout, 1)
Internet
quelle
1
Druckt es die Stapelspur aller Goroutinen?
Es sollte, ruft es Stack. "Stack gibt eine formatierte Stack-Ablaufverfolgung der Goroutine zurück, die sie aufruft. Für jede Routine enthält sie die Quellzeileninformationen und den PC-Wert und versucht dann, für Go-Funktionen die aufrufende Funktion oder Methode und den Text der Zeile zu ermitteln, die die enthält Aufruf."
Internet
1
Es wird leider nur der aktuelle Goroutine-Stack-Trace gedruckt.
4
@HowardGuo Ich habe ein Beispiel mit runtime / pprof hinzugefügt, um alle Stack-Traces zu sichern.
Internet
1
Ich denke, dies gibt nur die aktuell ausgeführte Goroutine jedes Threads aus, nicht alle Goroutinen, z. play.golang.org/p/0hVB0_LMdm
rogerdpack
39

Für das runtime/pprofin der Antwort von Intermernet erwähnte Paket gibt es ein HTTP-Frontend . Importieren Sie das Paket net / http / pprof , um einen HTTP-Handler zu registrieren für /debug/pprof:

import _ "net/http/pprof"
import _ "net/http"

Starten Sie einen HTTP-Listener, falls Sie noch keinen haben:

go func() {
    log.Println(http.ListenAndServe("localhost:6060", nil))
}()

Zeigen Sie dann mit einem Browser auf http://localhost:6060/debug/pprofein Menü oder http://localhost:6060/debug/pprof/goroutine?debug=2einen vollständigen Goroutine-Stack-Dump.

Es gibt noch andere lustige Dinge, die Sie auf diese Weise über Ihren laufenden Code lernen können. Beispiele und weitere Details finden Sie im Blog-Beitrag: http://blog.golang.org/profiling-go-programs

lnmx
quelle
Ich habe es laufen lassen, es zeigt nur die Goroutinen, die ausgeführt wurden, soweit ich sehe. Gibt es eine Möglichkeit, alle "Methoden" zu sehen, die nach dem Start von main.go ausgeführt werden?
Lukas Lukac
38

So ahmen Sie das Java-Verhalten von Stack-Dump auf SIGQUIT nach, lassen das Programm jedoch weiterhin laufen:

go func() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGQUIT)
    buf := make([]byte, 1<<20)
    for {
        <-sigs
        stacklen := runtime.Stack(buf, true)
        log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
    }
}()
Bryan
quelle
4
Ich denke, das ist es, wonach der Autor wirklich gesucht hat, um nachzuahmen, was Java tut, wenn Sie ein Kill-QUIT senden. Eine kleine Änderung, die ich vornehmen musste, war, die erste Zeile der for () - Schleife in "<- sigs" zu ändern. Mit anderen Worten, verwerfen Sie das Signal einfach, nachdem Sie darauf gewartet haben. In neueren Versionen von Go können Sie eine Variable nicht deklarieren, ohne sie später zu verwenden.
George Armhold
@Bryan, sind Sie bereit, dies unter BSD oder anderen zulässigeren Bedingungen zusätzlich zu dem von StackOverflow geforderten CC-BY-SA 3.0 zu lizenzieren?
Charles Duffy
1
@ CharlesDuffy Sie können fast das gleiche hier unter Apache Lizenz finden: github.com/weaveworks/weave/blob/…
Bryan
36

Ähnlich wie bei Java kann SIGQUIT verwendet werden, um einen Stack-Trace eines Go-Programms und seiner Goroutinen zu drucken.
Ein wesentlicher Unterschied besteht jedoch darin, dass das Senden von SIGQUIT an Java-Programme diese standardmäßig nicht beendet, während Go-Programme beendet werden.

Dieser Ansatz erfordert keine Codeänderung, um eine Stapelverfolgung aller Goroutinen vorhandener Programme zu drucken.

Die Umgebungsvariable GOTRACEBACK ( siehe Dokumentation des Laufzeitpakets ) steuert die Menge der generierten Ausgabe. Um beispielsweise alle Goroutinen einzuschließen, setzen Sie GOTRACEBACK = all.

Das Drucken des Stack-Trace wird durch eine unerwartete Laufzeitbedingung (nicht behandeltes Signal) ausgelöst, die ursprünglich in diesem Commit dokumentiert wurde und seit mindestens Go 1.1 verfügbar ist.


Wenn das Ändern des Quellcodes eine Option ist, lesen Sie alternativ andere Antworten.


Beachten Sie, dass in einem Linux-Terminal SIGQUIT bequem mit der Tastenkombination Ctrl+ gesendet werden kann \.

Rodolfo Carvalho
quelle
5
Beim Durchsuchen der Dokumente habe ich keine Erwähnungen von SIGQUIT gefunden, sondern von SIGABRT. Aus meinen eigenen Tests (mit go 1.7) ging das letztere auch über das erstere.
Soltysh
3
Dies sollte die beste Antwort sein.
Steven Soroka
Die Dokumente beziehen sich auf "Wenn ein Go-Programm aufgrund einer nicht wiederhergestellten Panik oder einer unerwarteten Laufzeitbedingung fehlschlägt". Ein nicht erfasstes Signal (SIGQUIT usw.) ist eines der letzteren. Warum habe ich SIGQUIT erwähnt? Weil das OP seine Liebe zur Verwendung von SIGQUIT mit Java zum Ausdruck bringt und diese Antwort die Ähnlichkeit betont. Formulieren Sie die Antwort neu, um sie klarer zu machen.
Rodolfo Carvalho
26

Sie können runtime.Stack verwenden , um den Stack-Trace aller Goroutinen abzurufen:

buf := make([]byte, 1<<16)
runtime.Stack(buf, true)
fmt.Printf("%s", buf)

Aus der Dokumentation:

func Stack(buf []byte, all bool) int

Stack formatiert einen Stack-Trace der aufrufenden Goroutine in buf und gibt die Anzahl der in buf geschriebenen Bytes zurück. Wenn alles wahr ist, stapeln die Stapelformate die Spuren aller anderen Goroutinen nach der Spur für die aktuelle Goroutine in Buf.

Robert Kajic
quelle
Dies beinhaltet Backtraces von allen Goroutinen, schön!
Rogerdpack
Ist dies das Format, auf das sich eine nicht wiederhergestellte Panik beschränkt?
Ztyx
2
Vergessen Sie nicht, einen String (buf) hinzuzufügen, sonst drucken Sie die Rohbytes dort.
Koda
1
Vielleicht mache ich etwas falsch oder vielleicht hat sich die Funktionalität geändert, aber das bringt mir nichts außer einem leeren Byte-Slice?
17xande
1
@koda gibt es keine Notwendigkeit zu tun string(buf)hier, fmt.Printf("%s", buf)und fmt.Printf("%s", string(buf))tut genau dasselbe (siehe Dokumentation für fmtPaket); Der einzige Unterschied besteht darin, dass die stringVersion die Bytes von bufunnötig
kopiert
19

Drücken Sie STRG + \

(Wenn Sie es in einem Terminal ausführen und nur Ihr Programm beenden und die Go-Routinen usw. sichern möchten)

Ich fand diese Frage auf der Suche nach der Tastenfolge. Ich wollte nur schnell und einfach feststellen, ob mein Programm undicht ist.

Øyvind Skaar
quelle
11

Auf * NIX-Systemen (einschließlich OSX) wird ein Signalabbruch gesendet SIGABRT:

pkill -SIGABRT program_name

Kyle Kloepper
quelle
Anscheinend wird das Senden von SIGQUIT an einen Java-Prozess nicht wie bei SIGABRT beendet .
Dave C
Ich fand, dass dies die einfachste und am besten passende Lösung für die ursprüngliche Frage ist. Oft benötigen Sie sofort einen Stacktrace, ohne Ihren Code zu ändern.
Jotrocken
5

Es ist erforderlich, die von zurückgegebene Länge runtime.Stack()zu verwenden, um zu vermeiden, dass nach der Stapelverfolgung ein paar leere Zeilen gedruckt werden. Die folgende Wiederherstellungsfunktion druckt eine gut formatierte Ablaufverfolgung:

if r := recover(); r != nil {
    log.Printf("Internal error: %v", r))
    buf := make([]byte, 1<<16)
    stackSize := runtime.Stack(buf, true)
    log.Printf("%s\n", string(buf[0:stackSize]))
}
David Tootill
quelle
Es gibt keine runtime.Trace; runtime.Stack wurde bereits vor anderthalb Jahren erwähnt .
Dave C
Ich habe das noch nie gesehen; Auf welcher Plattform laufen Sie?
Bryan
Was hast du nicht gesehen? Der Code sollte auf allen Plattformen ausgeführt werden. Ich habe es unter Windows 7, Ubuntu 14.04 und Mac getestet.
David Tootill
Nie leere Zeilen gesehen.
Bryan
4

Drücken Sie standardmäßig die ^\Tasten ( STRG + \ ), um die Stapelspuren aller Goroutinen zu sichern.


Andernfalls können Sie für eine genauere Steuerung verwenden panic. Der einfache Weg ab Go 1.6+ :

go func() {
    s := make(chan os.Signal, 1)
    signal.Notify(s, syscall.SIGQUIT)
    <-s
    panic("give me the stack")
}()

Führen Sie dann Ihr Programm folgendermaßen aus:

# Press ^\ to dump the stack traces of all the user-created goroutines
$ GOTRACEBACK=all go run main.go

Wenn Sie auch drucken möchten, gehen Sie zur Laufzeit Goroutinen:

$ GOTRACEBACK=system go run main.go

Hier sind alle GOTRACEBACK-Optionen:

  • GOTRACEBACK=none lässt die Goroutine-Stapelspuren vollständig weg.
  • GOTRACEBACK=single (Standardeinstellung) verhält sich wie oben beschrieben.
  • GOTRACEBACK=all Fügt Stapelspuren für alle vom Benutzer erstellten Goroutinen hinzu.
  • GOTRACEBACK=system ist wie "alle", fügt jedoch Stapelrahmen für Laufzeitfunktionen hinzu und zeigt Goroutinen an, die intern von der Laufzeit erstellt wurden.
  • GOTRACEBACK=crashist wie "System", stürzt jedoch betriebssystemspezifisch ab, anstatt zu beenden. Auf Unix-Systemen wird der Absturz beispielsweise ausgelöst SIGABRT, um einen Core-Dump auszulösen.

Hier ist die Dokumentation

Die Variable GOTRACEBACK steuert die Ausgabemenge, die generiert wird, wenn ein Go-Programm aufgrund einer nicht wiederhergestellten Panik oder einer unerwarteten Laufzeitbedingung fehlschlägt.

Standardmäßig druckt ein Fehler eine Stapelverfolgung für die aktuelle Goroutine, wobei Funktionen innerhalb des Laufzeitsystems entfernt werden, und wird dann mit dem Exit-Code 2 beendet. Der Fehler druckt Stapelverfolgungen für alle Goroutinen, wenn keine aktuelle Goroutine vorhanden ist oder der Fehler vorliegt intern zur Laufzeit.

Aus historischen Gründen sind die GOTRACEBACK-Einstellungen 0, 1 und 2 Synonyme für none, all bzw. system.

Die SetTraceback-Funktion des Laufzeit- / Debug-Pakets ermöglicht das Erhöhen der Ausgabemenge zur Laufzeit, kann jedoch die Menge nicht unter die von der Umgebungsvariablen angegebene Menge reduzieren. Siehe https://golang.org/pkg/runtime/debug/#SetTraceback .

Inanc Gumus
quelle