Go Home Page
Die Programmiersprache Go

Diagnostics — Deutsche Übersetzung

Das Original:
https://golang.org/doc/diagnostics.html
Version of November 20, 2020 (go1.16)
Diese Übersetzung:
https://bitloeffel.de/DOC/golang/diagnostics_20210225_de.html
Stand: 25.02.2021
© 2020-21 Hans-Werner Heinzen @ Bitloeffel.de
Die Nutzung dieses Werks ist unter den Bedingungen der "Creative Commons Attribution 3.0"-Lizenz erlaubt.
Für Fachbegriffe und ähnliches gibt es hier noch eine Wörterliste.

Diagnostik

Einführung

Das Go-Ökosystem bietet eine breite Palette von Schnittstellen und Werkzeugen zum Erkennen von Logik- und Durchsatzproblemen in Go-Programmen. Dieser Artikel zählt die verfügbaren Werkzeuge auf und hilft Go-Anwendern bei der Wahl des richtigen für Ihre jeweiliges Problem.

Diagnosewerkzeuge kann man in folgende Kategorien einteilen:

Hinweis: Einzelne Diagnosewerkzeuge können sich gegenseitig stören. Zum Beispiel verzerrt die genaue Analyse der Speichernutzung die Analyse der Prozessornutzung; und die Analyse des Blockierverhalten von Goroutinen beeinflusst das Nachverfolgen des Ablaufmanagers (scheduler). Benutzen Sie die Werkzeuge getrennt voneinander, dann bekommen Sie genauere Ergebnisse.

Profilmessung

Profilmessung (profiling) ist nützlich zum Auffinden teurer oder oft gerufener Kodeabschnitte. Die Go-Laufzeitumgebung liefert Profildaten in einem Format, wie es vom Visualisierungswerkzeug pprof erwartet wird. Profildaten können beim Testen mit go test oder mithilfe von Start- und Zielmarken aus dem Paket net/http/pprof gesammelt werden. Profildaten müssen zuerst gesammelt werden, und anschließend müssen mit dem Werkzeug pprof die häufigsten Kodepfade gefiltert und angezeigt werden.

Das Paket runtime/pprof bietet dafür vordefinierte Profile:

Welche Profilmessungen für Go-Programme gibt es außerdem noch?

Unter Linux kann man die perf-Tools zur Profilmessung von Go-Programmen benutzen. Perf kann Profile messen und cgo/SWIG-Kode von Kernelkode unterscheiden, kann also Einblicke in Performanzengpässe zwischen Programm- und Kernelkode gewähren. Unter macOS kann die Instrumentierungssammlung Instruments benutzt werden.

Kann ich Profilmessung im Produktionsbetrieb einsetzen?

Ja, das ist gefahrlos möglich — allerdings bringen einige der Profile (z.B. das CPU-Profil) Kosten mit sich. Sie sollten sich auf Performanzverluste einstellen. Die Performanzeinbuße kann abgeschätzt werden, indem man den zusätzlichen Aufwand für den Profiler misst, bevor man ihn in Produktion einsetzt.

Vielleicht wollen Sie bei Ihren produktiven Diensten regelmäßig das Profil vermessen. Ist es ein System mit vielen Kopien eines einzigen Prozesses, dann sind Sie auf der sicheren Seite, wenn Sie periodisch zufällig eine Kopie auswählen. Wählen Sie also einen produktiven Prozess, messen Sie dann alle Y Sekunden jeweils X Sekunden lang, sichern Sie die Ergebnisse für späteres Visualisieren und Analysieren; und das wiederholen Sie dann regelmäßig. Die Ergebnisse können manuell oder auch automatisiert auf ein Problem hin untersucht werden. Das Sammeln verschiedener Arten von Profildaten kann sich gegenseitig stören, weshalb empfohlen wird, Daten getrennt und nacheinander zu sammeln.

Wie visualisiert man Profildaten am besten?

Unsere Go-Werkzeuge können Profildaten als Text, Graph oder callgrind-Visualisierung darstellen, und zwar mithilfe von go tool pprof. Lesen Sie dazu "Profiling Go programs".


Liste der teuersten Funktionsaufrufe in Textform.


Darstellung der teuersten Funktionsaufrufe als Graph.

Die HTML-Darstellung zeigt die teuren Quellkodeabschnitte Zeile für Zeile auf einer HTML-Seite. Im folgenden Beispiel wurden 530ms in der Funktion runtime.concatstrings verbracht; die Kosten jeder Zeile werden angezeigt.


Darstellung der teuersten Funktionsaufrufe als HTML-Seite.

Eine andere Art, Profildaten anzuzeigen ist die in Form von Flammendiagrammen. Man kann sich dort auf einer bestimmten Abstammungslinie bewegen und die Ansicht von Kodeabschnitten vergößern und verkleinern. Das Original-pprof unterstützt Flammendiagramme.


Mit Flammendiagrammen lassen sich die teuersten Kodepfade aufspüren.

Ist mit den vordefinierten Profilen schon Schluss?

Nein, zusätzlich zu dem, was die Laufzeitumgebung bereitstellt, können benutzerdefinierte Profile mit pprof.Profile erzeugt werden; das Auswerten geschieht dann wieder mit den genannten Werkzeugen.

Kann ich die Profildatenbearbeiter (/debug/pprof/...) auf einen anderen Pfad und einen anderen Port ansetzen?

Ja. Das Paket net/http/pprof meldet seine Bearbeiter (handler) beim Standard-Multiplexer (mux) an, doch Sie können sie auch selbst anmelden mithilfe der vom Paket exportierten "Handler"-Funktionen.

Zum Beispiel wird im folgendem Kode der Bearbeiter pprof.Profile für /custom_debug_path/profile den Port :7777 bedienen:

package main

import (
    "log"
    "net/http"
    "net/http/pprof"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/custom_debug_path/profile", pprof.Profile)
    log.Fatal(http.ListenAndServe(":7777", mux))
}

Ablaufverfolgung

Ablaufverfolgung (tracing) ist eine Art der Instrumentierung von Kode für die Analyse von Latenzzeiten über eine Kette von Funktionsaufrufen hinweg. Go bietet das Paket golang.org/x/net/trace als minimales Werkzeug für die Hintergrundverarbeitung (backend) je Go-Knoten, und stellt eine minimale Bibliothek zum Instrumentieren des Kodes zu Verfügung sowie eine einfache Steuerung. Go hat außerdem noch einen Ablaufverfolger, der Laufzeitereignisse in einem Zeitintervall aufspürt.

Ablaufverfolgung ermöglicht uns,

In einem monolitischen System ist es relativ einfach, Diagnosedaten von den Programmbausteinen zu erhalten. Alle Teile arbeiten innerhalb eines Prozesses und melden ihre Protokolldaten, Fehler und andere Diagnosedaten an eine gemeinsame Ressource. Ist aber Ihr System erst einmal über den einen Prozess hinausgewachsen, wird also zu einem verteilten System, dann wird es auch schwerer, einen Aufruf vom Web-Server im Vordergrund zu allen Hintergrundprozessen zu verfolgen, bis endlich die Antwort an den Nutzer zurückgeht. Hier kommt die Verteilte Ablaufverfolgung zum Einsatz.

Verteilte Ablaufverfolgung ist eine Art der Instrumentierung von Kode für die Analyse von Latenzzeiten über die gesamte Laufzeit einer Benutzeranfrage. Wenn das System ein verteiltes ist und die gewöhnlichen Werkzeuge fürs Profilmessen und Entwanzen nicht mehr Schritt halten, werden Sie wohl für die Durchsatzanalyse Ihrer Benutzeranfragen und RPCs (remote procedure call) verteilte Werkzeuge benötigen.

Verteilte Ablaufverfolgung ermöglicht uns,

Das Go-Ökosystem bietet verschiedene verteilte Ablaufverfolgungs-Bibliotheken je Tracing-System als auch solche, die von der Art des Hintergrundsystems unabhängig sind.

Ist es möglich, irgendwie automatisch alle Funktionsaufrufe abzufangen und Protokolldaten zu erzeugen?

Das ist in Go nicht vorgesehen; Sie müssen Ihren Kode manuell instrumentieren, um Bereiche mit Anfangs- und Endemarken sowie mit Anmerkungen zu versehen.

Wie soll ich Ablauf-Kopfdaten in Go-Bibliotheken weitergeben?

Bezeichner (trace identifiers) und Markierungen (trace tags) fürs Ablaufprotokoll können Sie mithilfe von context.Context weitergeben. Bisher gibt es in der Industrie weder einen Standard für einen Ablaufschlüssel (trace key) noch eine allgemein übliche Darstellung für Ablauf-Kopfdaten (trace headers). Jeder, der Werkzeuge für die Ablaufverfolgung bereitstellt, ist auch für die Weitergabe innerhalb seiner Go-Bibliothek verantwortlich

Welche Low-Level-Ereignisse aus der Standardbibliothek oder der Laufzeitumgebung kann man noch protokollieren?

In der Standardbibliothek und der Laufzeitumgebung wurden einige Schnittstellen für das Melden von Low-Level-Ereignissen erweitert. Zum Beispiel stellt httptrace.ClientTrace eine Schnittstelle zum Verfolgen von Low-Level-Ereignissen während der Laufzeit einer abgehenden Anfrage zur Verfügung. Man bemüht sich fortwährend, weitere Low-Level-Ereignisse der Laufzeitumgebung für die Ablaufverfolgung bereitzustellen sowie Go-Anwendern zu erlauben, ihre eigenen Ereignisse zu definieren und aufzuzeichnen.

Entwanzen

Entwanzen (debugging) heißt der Prozess, bei dem die Ursache für ein schlechten Programmverhalten festgestellt wird. Debugger helfen uns, den Programmablauf und dessen aktuellen Zustand zu verstehen. Es gibt mehrere Arten des Entwanzens; hier konzentrieren wir uns auf das Verknüpfen eines Debuggers mit einem Programm sowie das Arbeiten mit einem Speicherauszug (core dump).

Für Go werden vor allem folgende Debugger verwendet:

Wie gut kommen Debugger mit Go-Programmen zurecht?

Der Compiler gc führt Optimierungen durch, etwa Funktionseinbettung (inlining) und Variablenpufferung (registerization). Das erschwert das Entwanzen mit dem Debugger. Man bemüht sich fortwährend, die Qualität von DWARF-Informationen zu verbessern, die für optimierte Binärdateien genutzt werden. Bis dahin empfehlen wir, den zum Entwanzen bestimmten Kode ohne Optimierungen umzuwandeln. Folgendes Kommando fertigt ein Paket ohne Optimierungen durch den Compiler:

$ go build -gcflags=all="-N -l"

Als Teil der genannten Verbesserungen führte Go 1.10 einen neuen Schalter für den Compiler ein: -dwarflocationlists. Dieser Schalter veranlasst den Compiler, optimierten Binärdateien eine Liste von Speicherorten hinzuzufügen. Folgendes Kommando fertigt ein Paket mit Optimierungen und mit einer DWARF-Liste von Speicherorten:

$ go build -gcflags="-dwarflocationlists=true"

Welche Benutzerschnittstelle wird für Debugger empfohlen?

Zwar bieten sowohl Delve als auch GDB Zeilenkommandos (CLI) an, aber die meisten Integrierten Umgebungen (IDEs) haben eigene Schnittstellen fürs Entwanzen.

Ist es möglich Go-Programme nach dem Absturz (postmortem) zu untersuchen?

Eine Speicherauszugsdatei (core dump file) ist eine Datei, die den Speicherauszug sowie den Status eines laufenden Prozesses enthält. Sie wird zum Einen gebraucht, um ein Programm nach dem Absturz zu untersuchen, und zum Anderen, um seinen Zustand während der Ausführung zu verstehen. Für beide Anwendungen ist der Speicherauszug eine nützliche Hilfe. Es ist möglich, sich Speicherauszüge von Go-Programmen zu greifen und diese dann mit Delve oder GDB zu untersuchen; core dump debugging bietet eine Schritt-für-Schritt-Anleitung.

Laufzeitzustände und Laufzeitereignisse

Die Laufzeitumgebung liefert uns Statistikdaten und meldet interne Ereignisse, sodass Go-Anwender Performanz- und Nutzungsprobleme auf dieser Ebene beurteilen können.

man kann mit diesen Statistikdaten die allgemeine Güte und Performanz eines Go-Programms besser verstehen. Es folgen einige oft benutzte Statistik- und Zustandsdaten:

Ablaufverfolger

Go bringt einen Ablaufverfolger mit, der eine breite Palette von Laufzeitereignissen einfängt. Zeitplanung (scheduling), Systemaufrufe, Speicherbereinigung und andere Ereignisse werden durch das Laufzeitsystem gesammelt und steht zur Visualisierung mit dem Go-Werkzeug trace zur Verfügung. Dieser Ablaufverfolger dient der Aufdeckung von Latenz- und Nutzungsproblemen. Sie können untersuchen, wie gut die CPU genutzt wird, und wann Netzwerk- oder Systemaufrufe Grund für Bevorzugung (preemption) von Goroutinen ist.

Der Ablaufverfolger hilft,

Allerdings ist dieses Werkzeug nicht besonders gut, um kritische Kodeabschnitte zu finden, etwa solche, die Speicher oder Prozessor übermäßig belasten. Nutzen Sie dafür stattdessen Werkzeuge der Profilmessung.

Hier zeigt die Visualisierung durch go tool trace, dass die Ausführung zunächst gut gestartet ist, dann aber nur noch seriell arbeitet. Das legt nahe, dass Konkurrenz um eine Sperre für eine gemeinsam genutzte Ressource aufgetreten ist, wodurch ein Flaschenhals entstand.

Schauen Sie sich unter go tool trace an, wie man Ablaufdaten sammelt und analysiert.

GODEBUG

Die Laufzeitumgebung emittiert Ereignisse und weitere Informationen, wenn die Umgebungsvariable GODEBUG entsprechend gesetzt ist:

Die Umgebungsvariable GODEBUG kann auch benutzt werden, um Erweiterungen des Befehlssatzes in der Standardbibliothek und der Laufzeitumgebung zu deaktivieren.