Dev Life
Vorstellung eines plattformübergreifenden Debuggers für Go
Wir verwenden Go für einen Großteil unserer Serverentwicklung hier bei Mailgun, und es ist großartig. Da ich allerdings von Python komme, habe ich eine Sache wirklich vermisst:
Ich konnte diese kleine Zeile immer dann einfügen, wenn ich nicht verstand, was im Code passierte, um genau zu sehen, was vor sich ging. Aber in Go? Nicht wirklich. Als ich dieses Projekt im Januar begann, versagte gdb bei jedem Programm, bei dem ich es ausprobierte. delve funktionierte nicht unter OS X und das Debuggen mit Print-Anweisungen war zu langsam und eingeschränkt. Was bleibt einem da anderes übrig?
Natürlich einen eigenen Debugger entwickeln.
godebug
Alles, was [einem guten Go-Debugger] im Weg steht, ist das Schreiben von viel nicht portablem Low-Level-Code, der mit fehlerhaften, undokumentierten Schnittstellen kommuniziert.
godebug ist eine andere Art von Debugger. Herkömmliche Debugger für kompilierte Sprachen verwenden Low-Level-Systemaufrufe und lesen Binärdateien für Debugging-Symbole aus. Sie sind schwer fehlerfrei zu implementieren und schwer zu portieren.
godebug verfolgt einen anderen Ansatz: Es nimmt den Quellcode eines Zielprogramms, fügt zwischen jeder Zeile Debugging-Code ein und kompiliert und führt stattdessen diesen aus. Das Ergebnis ist ein voll funktionsfähiger Debugger, der extrem portabel ist. In der Tat, dank gopherjs können Sie ihn direkt hier in Ihrem Browser ausführen.
Sie können das Programm bearbeiten und mit der Schaltfläche „DEBUG IT!“ so oft neu starten, wie Sie möchten.
Wie hat das funktioniert?
Hier ist ein kurzes Diagramm der obigen Demo:
Der ursprüngliche Code wird zweimal transformiert. Zunächst fügte godebug die Debugging-Instrumentierung ein. Anschließend kompilierte gopherjs das Ergebnis zu JavaScript.
Sehen wir uns den Instrumentierungsschritt genauer an. (Um mehr über den Teil mit gopherjs zu erfahren (der fantastisch ist), besuchen Sie dessen Website.) Hier sind einige der Aufrufe, die godebug einfügt:
godebug.EnterFuncteilt der godebug-Laufzeitbibliothek mit, dass wir eine Funktion betreten. Da „next“ nicht innerhalb von Funktionsaufrufen anhält, merkt sich die Laufzeitbibliothek diese Aufrufe, um zu wissen, wann Zeilen übersprungen werden müssen.godebug.ExitFuncteilt der Laufzeitbibliothek mit, dass wir eine Funktion verlassen. Inmainweggelassen.godebug.Linebewirkt, dass das Programm pausiert und auf eine Eingabe wartet, jedoch nur dann, wenn ein Nutzerbefehl oder ein Haltepunkt dies verlangt. Wenn es pausiert, fordert es den Nutzer zur Eingabe auf und reagiert auf alle Befehle.godebug.Declarezeichnet die Zuordnung von Variablennamen zu ihren Werten auf. Diese Zuordnung wird vom Print-Befehl verwendet.
Dies ist eine verkürzte Übersicht. Es gibt weitere Funktionen, die godebug einfügt, und viele Details der oben genannten Funktionen wurden weggelassen. Aber dies sind die Grundlagen, wie godebug funktioniert.
Verwendung von godebug
All das (ohne den Server- und JavaScript-Teil) ist in dem Kommandozeilen-Tool godebug zusammengefasst. Und so wird es verwendet:
Schritt 1: Installieren
$ go get -u github.com/mailgun/godebug
Schritt 2: Haltepunkte setzen
Fügen Sie diese Markierung überall dort ein, wo Sie einen Haltepunkt setzen möchten:
_ = "breakpoint"
Diese Anweisung wird zu einem Haltepunkt, wenn sie unter godebug ausgeführt wird, und ist andernfalls ein No-Op.
Da Haltepunkte Teil des Quellcodes sind, können Sie Ihre eigene Logik um sie herum aufbauen. Angenommen, Sie führen einen tabellengesteuerten Test mit Dutzenden von Fällen aus, und einer der Testfälle schlägt fehl: derjenige, der die Eingabe "weird string" getestet hat. Sie können diesen Haltepunkt zu Ihrem Test hinzufügen:
for _, tt := range myTestCases { if tt.in == "weird string" { _ = "breakpoint" } ... }
godebug test pausiert das Programm an der Markierungsanweisung, die praktischerweise genau vor der Ausführung des fehlschlagenden Testfalls positioniert ist.
Schritt 3: Programm ausführen
Verwenden Sie den Befehl godebug run:
godebug run
Oder verwenden Sie für Tests den Befehl godebug test:
godebug test
Standardmäßig fügt godebug nur dem Paket main (für godebug run) oder dem getesteten Paket (für godebug test) Debugging-Instrumentierung hinzu. Importierte Pakete werden nicht instrumentiert. Dies dient dazu, den Overhead des Debuggers zu verringern – alle Pakete, an denen Sie nicht interessiert sind, laufen wie gewohnt bei voller Geschwindigkeit.
Das bedeutet, dass Sie standardmäßig nicht in Funktionsaufrufe von importierten Paketen hineinspringen können. Manchmal ist dies jedoch erforderlich. Um andere Pakete zu debuggen, übergeben Sie das Flag -instrument an godebug run oder godebug test:
godebug test -instrument=pkgA,pkgB,pkgC pkg/under/test
Der obige Befehl instrumentiert pkgA, pkgB, pkgC und pkg/under/test (und ermöglicht es Ihnen somit, in diese hineinzuspringen). Anschließend werden die Tests für pkg/under/test erstellt und ausgeführt. Für godebug run gilt eine ähnliche Semantik.
Ausprobieren
Wenn Sie das nächste Mal verstehen möchten, was in einem Go-Programm passiert, probieren Sie godebug. Bedenken Sie, dass es sich noch um ein neues Tool handelt, das etwas Feinschliff benötigt. Einige bekannte Einschränkungen sind insbesondere:
- Performance-Overhead
- Kann Lesekonflikte verursachen, wenn Ihr Programm von stdin liest
- Kann nicht an einen laufenden Prozess angehängt werden
- Die Pakete, die Sie debuggen möchten, müssen vor dem Start der Sitzung bekannt sein
Abgesehen davon bin ich von diesem Tool begeistert und hoffe, dass es der Go-Community einen großen Mehrwert bieten kann. Probieren Sie es aus und lassen Sie mich wissen, was Sie davon halten. Falls Sie auf Probleme stoßen, würde ich mich freuen, davon zu erfahren. Erstellen Sie ein Issue unter https://github.com/mailgun/godebug/issues oder senden Sie eine E-Mail an: dev@godebug.rocks
Ich hoffe, es gefällt Ihnen.
https://github.com/mailgun/godebug
Möchten Sie mehr über godebug erfahren? Hier ist ein Vortrag, den ich auf der GoSF gehalten habe und der tiefer auf Code-Generierung und Nebenläufigkeitsverwaltung eingeht: