Fehlerbehebung bei WebAssembly mit modernen Tools

Ingvar Stepanyan
Ingvar Stepanyan

Der Weg bisher

Vor einem Jahr wurde in Chrome die erste Unterstützung angekündigt für natives WebAssembly-Debugging in den Chrome-Entwicklertools.

Wir haben grundlegende Unterstützung bei den Schritten gezeigt und über Chancen gesprochen Verwendung von DWARF-Informationen anstelle von Quellzuordnungen zukünftig für uns geöffnet:

  • Variablennamen auflösen
  • Quelltextformatierungen
  • Ausdrücke in den Ausgangssprachen auswerten
  • ...und vieles mehr!

Heute möchten wir zeigen, wie die versprochenen Funktionen und die Fortschritte der Emscripten- und Chrome DevTools-Teams insbesondere für C- und C++-Apps.

Bevor wir beginnen, möchte ich noch einmal darauf hinweisen, dass dies eine Betaversion ist. der neuen Version müssen Sie die aktuelle Version aller Tools verwenden. auf dein eigenes Risiko. Sollten Probleme auftreten, melde sie bitte https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350.

Beginnen wir mit demselben einfachen C-Beispiel wie beim letzten Mal:

#include <stdlib.h>

void assert_less(int x, int y) {
  if (x >= y) {
    abort();
  }
}

int main() {
  assert_less(10, 20);
  assert_less(30, 20);
}

Zur Kompilierung verwenden wir die neueste Emscripten-Version. und übergeben Sie wie im ursprünglichen Beitrag ein -g-Flag, um Informationen:

emcc -g temp.c -o temp.html

Jetzt können wir die generierte Seite von einem localhost-HTTP-Server (für Beispiel, mit serve) und öffnen Sie sie in der aktuellen Version von Chrome Canary.

Dieses Mal benötigen wir auch eine Hilfserweiterung, die in die Chrome DevTools eingebunden ist und dabei hilft, alle in der WebAssembly-Datei codierten Informationen zur Fehlerbehebung zu verstehen. Installieren Sie sie hier: Link: goo.gle/wasm-debugging-extension

Sie sollten auch das WebAssembly-Debugging in den Entwicklertools aktivieren Experimentelle Funktionen: Öffnen Sie die Chrome-Entwicklertools, klicken Sie in der Symbolleiste auf das Zahnradsymbol () Gehen Sie rechts oben im Bereich „Entwicklertools“ zum Bereich Tests. und wählen Sie WebAssembly Debugging: Enable DWARF support (WebAssembly-Debugging: DWARF-Unterstützung aktivieren) aus.

Bereich „Tests“ in den Entwicklertools-Einstellungen

Wenn du die Einstellungen schließt, schlägt die Entwicklertools vor, sich selbst zu aktualisieren um Einstellungen anzuwenden. Damit ist die einmalige Einrichtung abgeschlossen.

Jetzt können wir zum Bereich Quellen zurückkehren und die Option Pausieren am Ausnahmen (⏸-Symbol) und aktivieren Sie dann Bei erkannten Ausnahmen pausieren und aktualisieren Sie die Seite. Die Entwicklertools sollten bei einer Ausnahme pausiert sein:

Screenshot des Bereichs „Quellen“, in dem zu sehen ist, wie das Pausieren bei erkannten Ausnahmen aktiviert wird

Standardmäßig stoppt er bei einem von Emscripten generierten Glue Code, aber auf der sehen Sie einen Aufrufstack, der den Stacktrace von Fehler aus und können zur ursprünglichen C-Zeile navigieren, abort:

In den DevTools wurde die Ausführung in der Funktion „assert_less“ angehalten und in der Ansicht „Scope“ werden die Werte „x“ und „y“ angezeigt

Wenn Sie jetzt in der Ansicht Scope (Umfang) nachsehen, sehen Sie die ursprünglichen Namen und Werte der Variablen im C/C++-Code. Sie müssen also nicht mehr herausfinden, was unleserliche Namen wie $localN bedeuten und in welcher Beziehung sie zum von Ihnen geschriebenen Quellcode stehen.

Dies gilt nicht nur für primitive Werte wie Ganzzahlen, sondern auch für Typen wie Strukturen, Klassen, Arrays usw. anwenden.

Rich-Type-Unterstützung

Sehen wir uns dazu ein etwas komplizierteres Beispiel an. Dieses zeichnen wir ein Mandelbrot-Faktal mit der folgenden C++ Code:

#include <SDL2/SDL.h>
#include <complex>

int main() {
  // Init SDL.
  int width = 600, height = 600;
  SDL_Init(SDL_INIT_VIDEO);
  SDL_Window* window;
  SDL_Renderer* renderer;
  SDL_CreateWindowAndRenderer(width, height, SDL_WINDOW_OPENGL, &window,
                              &renderer);

  // Generate a palette with random colors.
  enum { MAX_ITER_COUNT = 256 };
  SDL_Color palette[MAX_ITER_COUNT];
  srand(time(0));
  for (int i = 0; i < MAX_ITER_COUNT; ++i) {
    palette[i] = {
        .r = (uint8_t)rand(),
        .g = (uint8_t)rand(),
        .b = (uint8_t)rand(),
        .a = 255,
    };
  }

  // Calculate and draw the Mandelbrot set.
  std::complex<double> center(0.5, 0.5);
  double scale = 4.0;
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {
      std::complex<double> point((double)x / width, (double)y / height);
      std::complex<double> c = (point - center) * scale;
      std::complex<double> z(0, 0);
      int i = 0;
      for (; i < MAX_ITER_COUNT - 1; i++) {
        z = z * z + c;
        if (abs(z) > 2.0)
          break;
      }
      SDL_Color color = palette[i];
      SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
      SDL_RenderDrawPoint(renderer, x, y);
    }
  }

  // Render everything we've drawn to the canvas.
  SDL_RenderPresent(renderer);

  // SDL_Quit();
}

Wie Sie sehen, ist diese Anwendung immer noch recht klein, mit 50 Zeilen Code. Dieses Mal verwende ich aber auch externe APIs wie die SDL-Bibliothek für Grafiken sowie komplexe Zahlen aus der C++-Standardbibliothek.

Ich werde sie mit demselben -g-Flag wie oben kompilieren, und bitte Emscripten, die SDL2-Datei und Arbeitsspeicher mit beliebiger Größe zulassen:

emcc -g mandelbrot.cc -o mandelbrot.html \
     -s USE_SDL=2 \
     -s ALLOW_MEMORY_GROWTH=1

Wenn ich die generierte Seite im Browser aufrufe, fraktale Form mit einigen zufälligen Farben:

Demoseite

Wenn ich die Entwicklertools wieder öffne, sehe ich die ursprüngliche C++-Datei. Dieses Allerdings haben wir keinen Fehler im Code (Puh!), also legen wir einen Haltepunkt am Anfang des Codes.

Wenn wir die Seite erneut laden, pausiert der Debugger direkt im C++-Quelle:

Entwicklertools beim Aufruf „SDL_Init“ pausiert

Auf der rechten Seite sind bereits alle Variablen zu sehen, aber nur width und height initialisiert sind, sodass nicht viel prüfen.

Legen wir einen weiteren Haltepunkt in unserer Hauptschleife für den Mandelbrot-Algorithmus fest und fahren wir fort, um ein wenig vorzuspringen.

Entwicklertools in verschachtelten Schleifen pausiert

Wir haben palette mit zufälligen Farben gefüllt, Wir können sowohl das Array selbst als auch die einzelnen Elemente erweitern, SDL_Color aufgebaut und ihre Komponenten überprüft, um sicherzustellen, alles in Ordnung ist (z. B. ist der Kanal "Alpha" immer volle Deckkraft). Auf ähnliche Weise können wir die realen und imaginären Teile der komplexen Zahl, die in der Variablen center gespeichert sind.

Wenn Sie auf eine tief verschachtelte Property zugreifen möchten, die über die Ansicht Umfang nur schwer zu finden ist, können Sie auch die Console verwenden. Beachten Sie jedoch, dass komplexere C++-Ausdrücke keine unterstützt.

Konsolenbereich mit dem Ergebnis von „palette[10].r“

Führen wir die Ausführung noch ein paar Mal fort, um zu sehen, wie sich auch die innere x ändert. Dazu können wir entweder noch einmal in der Ansicht Scope nachsehen, den Variablennamen der Beobachtungsliste hinzufügen, ihn in der Konsole auswerten oder den Mauszeiger im Quellcode auf die Variable bewegen:

Kurzinfo zur Variablen „x“ in der Quelle mit ihrem Wert „3“

Von hier aus können wir Step-in- oder Step-over-Anweisungen andere Variablen ändern sich ebenfalls:

Kurzinfos und Bereichsansicht mit Werten von „Farbe“, „Punkt“ und anderen Variablen

Okay, das klappt gut, wenn Debug-Informationen verfügbar sind. was ist, wenn wir Fehler in einem Code beheben möchten, der nicht mit dem Debugging-Tool erstellt wurde, Optionen?

Raw-WebAssembly-Debugging

Beispielsweise haben wir Emscripten gebeten, eine vordefinierte SDL-Bibliothek für Anstatt sie aus der Quelle selbst zusammenzustellen, derzeit kann der Debugger die zugehörigen Quellen nicht finden. Lassen Sie uns noch einmal einsteigen, um in den SDL_RenderDrawColor einzusteigen:

DevTools mit der Ansicht „Disassembly“ von „mandelbrot.wasm“

Jetzt haben wir wieder das Debugging mit WebAssembly.

Es sieht jetzt etwas beängstigend aus und ist für die meisten Webentwickler nicht relevant. mit denen Sie sich befassen müssen, aber gelegentlich möchten Sie Fehler ohne Informationen zur Fehlerbehebung erstellt wurde – Drittanbieter-Bibliothek, die Sie nicht steuern können, oder weil Sie auf einen dieser Fehler stößt, der nur in der Produktion auftritt.

Um Ihnen in diesen Fällen zu helfen, haben wir auch einige Verbesserungen am grundlegenden Debugging vorgenommen.

Wenn Sie schon einmal das Debugging von Roh-WebAssembly verwendet haben, werden Sie feststellen, dass die gesamte Deassemblage jetzt in einer einzigen Datei angezeigt wird. Sie müssen also nicht mehr raten, welcher Funktion ein Eintrag wasm-53834e3e/ wasm-53834e3e-7 unter Quellen möglicherweise entspricht.

Neues Schema zur Namensgenerierung

Außerdem haben wir die Namen in der Demontageansicht verbessert. Bisher wurden diese nur numerische Indizes oder, im Fall von Funktionen, überhaupt keinen Namen.

Jetzt generieren wir Namen ähnlich wie bei anderen Werkzeugen zum Auseinandernehmen, mithilfe von Hinweisen aus dem Abschnitt mit WebAssembly-Namen Import/Export von Pfaden und, falls alles andere fehlschlägt, basierend auf dem Typ und dem Index des Elements wie z. B. $func123. Sie können Wie im Screenshot oben gezeigt, Stacktraces und deren Demontage besser lesbar sind.

Wenn keine Typinformationen verfügbar sind, ist der Zugriff möglicherweise schwierig. alle Werte außer den Primitiven, z. B. werden Zeiger angezeigt, als reguläre Ganzzahlen, ohne zu wissen, was in den Daten zu speichern.

Arbeitsspeicherprüfung

Bisher konnten Sie nur das Speicherobjekt WebAssembly maximieren, das in der Ansicht Bereich durch env.memory dargestellt wird, um nachzuschlagen. einzelne Byte. Dies funktionierte zwar in einigen simplen Szenarien, ist besonders praktisch zu erweitern und ließen Daten nicht neu interpretieren in anderen Formaten als Byte-Werten. Wir haben eine neue Funktion hinzugefügt, mit diesem linearen Arbeitsspeicher-Prüftool.

Wenn Sie mit der rechten Maustaste auf das env.memory klicken, sollte jetzt eine neue mit der Option Arbeitsspeicher prüfen:

Kontextmenü für „env.memory“ im Bereich „Scope“ (Umfang) mit dem Menüpunkt „Inspect Memory“ (Arbeitsspeicher prüfen)

Daraufhin wird der Memory Inspector Sie können den WebAssembly-Arbeitsspeicher in Hexadezimal- und ASCII-Ansichten zu bestimmten Adressen navigieren und die Daten in verschiedene Formate verwenden:

Der Bereich „Memory Inspector“ in den Entwicklertools mit Hex- und ASCII-Ansichten des Arbeitsspeichers

Erweiterte Szenarien und Vorbehalte

Profilerstellung für WebAssembly-Code

Wenn Sie die Entwicklertools öffnen, wird WebAssembly-Code „abgestuft“ in ein nicht optimierten Version, um das Debugging zu ermöglichen. Diese Version ist viel langsamer, Das bedeutet, dass Sie sich nicht auf console.time, performance.now und andere Methoden zur Messung der Codegeschwindigkeit, während die Entwicklertools da die angezeigten Zahlen nicht die tatsächliche Leistung überhaupt nicht.

Verwenden Sie stattdessen in den Entwicklertools das Steuerfeld „Leistung“. Dadurch wird der Code mit voller Geschwindigkeit ausgeführt und Sie erhalten eine Eine detaillierte Aufschlüsselung der Zeit, die für verschiedene Funktionen aufgewendet wurde:

Profilbereich mit verschiedenen Wasm-Funktionen

Alternativ können Sie Ihre Anwendung mit geschlossenen Entwicklertools ausführen und Wenn Sie fertig sind, öffnen Sie sie, um die Console zu überprüfen.

Wir werden die Profilerstellung in Zukunft verbessern. zu beachten. Wenn Sie mehr über WebAssembly erfahren möchten Tiering-Szenarien finden Sie in unserer Dokumentation zur WebAssembly-Kompilierungspipeline.

Build und Debugging auf verschiedenen Computern (einschließlich Docker / Host)

Beim Erstellen in einem Docker, einer virtuellen Maschine oder einem Remote-Build-Server werden Sie wahrscheinlich auf Situationen stoßen, in denen die Pfade zu den die während des Builds verwendet wurden, nicht mit den Pfaden in Ihrem eigenen Dateisystem übereinstimmen, die Chrome-Entwicklertools ausgeführt werden. In diesem Fall werden Dateien im Bereich Quellen angezeigt, aber nicht geladen.

Um dieses Problem zu beheben, haben wir in den C/C++-Erweiterungsoptionen eine Pfadzuordnungsfunktion implementiert. Sie können damit beliebige Pfade neu zuordnen und damit die Entwicklertools Quellen leichter finden können.

Wenn sich das Projekt auf Ihrem Hostcomputer beispielsweise unter dem Pfad C:\src\my_project befindet, aber in einem Docker-Container erstellt wurde, in dem dieser Pfad als /mnt/c/src/my_project dargestellt wurde, können Sie ihn beim Debuggen neu zuordnen, indem Sie diese Pfade als Präfixe angeben:

Optionen-Seite der C/C++-Debugging-Erweiterung

Das erste übereinstimmende Präfix „gewinnt“. Wenn Sie mit anderen C++- Debugger erstellt haben, ähnelt diese Option dem Befehl set substitute-path in GDB oder eine target.source-map-Einstellung in LLDB.

Fehler in optimierten Builds beheben

Wie bei anderen Sprachen funktioniert das Debuggen am besten, wenn Optimierungen deaktiviert sind. Durch Optimierungen können Funktionen inline angeordnet oder neu angeordnet werden. oder Teile des Codes ganz entfernen. den Debugger und folglich Sie als Nutzer zu verwirren.

Wenn Sie mit eingeschränkten Debug-Funktionen zufrieden sind und trotzdem einen optimierten Build debuggen möchten, funktionieren die meisten Optimierungen wie erwartet, mit Ausnahme der Funktions-Inline-Optimierung. Wir haben vor, die verbleibenden in Zukunft zu beheben. Verwenden Sie für den Moment -fno-inline, bei der Kompilierung mit Optimierungen auf -O-Ebene deaktivieren, z.B.:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline

Debug-Informationen trennen

In den Informationen zur Fehlerbehebung werden viele Details zu Ihrem Code gespeichert, Variablen, Funktionen, Umfänge und Standorte – alles, was auch für den Debugger nützlich sein. Daher kann sie häufig größer sein als die Code selbst.

Um das Laden und Kompilieren des WebAssembly-Moduls zu beschleunigen, diese Debug-Informationen in eine separate WebAssembly- -Datei. Dazu übergeben Sie in Emscripten ein -gseparate-dwarf=…-Flag mit einen gewünschten Dateinamen:

emcc -g temp.c -o temp.html \
     -gseparate-dwarf=temp.debug.wasm

In diesem Fall speichert die Hauptanwendung nur einen Dateinamen. temp.debug.wasm und die Hilfserweiterung kann nach wenn Sie die Entwicklertools öffnen.

In Kombination mit den oben beschriebenen Optimierungen kann diese Funktion können sogar fast optimierte Produktions-Builds und führen Sie später das Debugging mit einer lokalen Nebendatei durch. In diesem Fall Außerdem müssen wir die gespeicherte URL überschreiben, die Nebendatei suchen, zum Beispiel:

emcc -g temp.c -o temp.html \
     -O3 -fno-inline \
     -gseparate-dwarf=temp.debug.wasm \
     -s SEPARATE_DWARF_URL=file://[local path to temp.debug.wasm]

Wird fortgesetzt...

Puh, das waren eine Menge neuer Funktionen!

Mit all diesen neuen Integrationen werden die Chrome-Entwicklertools ein brauchbares, leistungsstarker Debugger, nicht nur für JavaScript, sondern auch für C- und C++-Apps. Es war noch nie so einfach, Apps zu nutzen, die in einer Vielzahl von und sie in ein gemeinsames, plattformübergreifendes Web zu bringen.

Wir sind jedoch noch nicht am Ende. Einige der Dinge, von hier an arbeiten:

  • Die Fehlerbehebung wurde optimiert.
  • Benutzerdefinierte Typformatierer werden jetzt unterstützt.
  • Wir arbeiten an Verbesserungen der Profilerstellung für WebAssembly-Anwendungen.
  • Codeabdeckung wird unterstützt, um die Suche zu erleichtern nicht verwendetem Code.
  • Verbesserte Unterstützung für Ausdrücke bei der Konsolenauswertung.
  • Unterstützung für weitere Sprachen
  • und weitere!

In der Zwischenzeit können Sie die aktuelle Betaversion mit Ihrem eigenen Code testen und alle gefundenen Probleme unter https://issues.chromium.org/issues/new?noWizard=true&template=0&component=1456350 melden.

Vorschaukanäle herunterladen

Sie können Canary, Dev oder Beta als Standardbrowser für die Entwicklung verwenden. Über diese Vorschaukanäle erhältst du Zugriff auf die neuesten Entwicklertools, kannst hochmoderne Webplattform-APIs testen und Probleme auf deiner Website erkennen, bevor deine Nutzer es tun.

Chrome-Entwicklertools-Team kontaktieren

Mit den folgenden Optionen kannst du die neuen Funktionen und Änderungen des Beitrags oder andere Aspekte der Entwicklertools besprechen.

  • Senden Sie uns über crbug.com einen Vorschlag oder Feedback.
  • Problem mit den Entwicklertools über Weitere Optionen melden Mehr > Hilfe > Hier kannst du Probleme mit den Entwicklertools in den Entwicklertools melden.
  • Twittern Sie unter @ChromeDevTools.
  • Hinterlasse Kommentare in den YouTube-Videos mit den Neuerungen in den Entwicklertools oder in YouTube-Videos mit Tipps zu den Entwicklertools.