Skip to content

Flame Graphs

Wofür ist ein Flame Graph nützlich?

Flame Graphs sind eine Möglichkeit, die in Funktionen verbrachte CPU-Zeit zu visualisieren. Sie können Ihnen helfen, herauszufinden, wo Sie zu viel Zeit mit synchronen Operationen verbringen.

Wie man einen Flame Graph erstellt

Sie haben vielleicht gehört, dass das Erstellen eines Flame Graphs für Node.js schwierig ist, aber das stimmt nicht (mehr). Solaris VMs werden für Flame Graphs nicht mehr benötigt!

Flame Graphs werden aus perf-Ausgaben generiert, was kein Node-spezifisches Tool ist. Obwohl es die leistungsfähigste Methode zur Visualisierung der verbrauchten CPU-Zeit ist, kann es Probleme damit geben, wie JavaScript-Code in Node.js 8 und höher optimiert wird. Siehe Abschnitt perf-Ausgabeprobleme unten.

Verwenden Sie ein vorkonfiguriertes Tool

Wenn Sie einen einzigen Schritt wünschen, der lokal einen Flame Graph erzeugt, versuchen Sie 0x

Für die Diagnose von Produktionsbereitstellungen lesen Sie diese Hinweise: 0x Produktionsserver.

Erstellen Sie einen Flame Graph mit System-Perf-Tools

Der Zweck dieser Anleitung ist es, die Schritte zur Erstellung eines Flame Graphs zu zeigen und Ihnen die Kontrolle über jeden Schritt zu ermöglichen.

Wenn Sie jeden Schritt besser verstehen möchten, werfen Sie einen Blick auf die folgenden Abschnitte, in denen wir detaillierter darauf eingehen.

Legen wir nun los.

  1. Installieren Sie perf (normalerweise über das Paket linux-tools-common verfügbar, falls nicht bereits installiert)
  2. Versuchen Sie, perf auszuführen - es könnte sich über fehlende Kernel-Module beschweren, installieren Sie diese ebenfalls
  3. Führen Sie Node mit aktiviertem perf aus (siehe perf-Ausgabeprobleme für Tipps speziell zu Node.js-Versionen)
bash
perf record -e cycles:u -g -- node --perf-basic-prof app.js
  1. Ignorieren Sie Warnungen, es sei denn, sie besagen, dass Sie perf aufgrund fehlender Pakete nicht ausführen können; Sie erhalten möglicherweise einige Warnungen, dass Sie nicht auf Kernel-Modul-Samples zugreifen können, die Sie sowieso nicht benötigen.
  2. Führen Sie perf script > perfs.out aus, um die Datendatei zu generieren, die Sie gleich visualisieren werden. Es ist nützlich, etwas Bereinigung anzuwenden, um eine lesbarere Grafik zu erhalten
  3. Installieren Sie stackvis, falls es noch nicht installiert ist, npm i -g stackvis
  4. Führen Sie stackvis perf < perfs.out > flamegraph.htm aus

Öffnen Sie nun die Flame-Graph-Datei in Ihrem Lieblingsbrowser und beobachten Sie, wie sie brennt. Sie ist farblich gekennzeichnet, so dass Sie sich zuerst auf die am stärksten gesättigten orangefarbenen Balken konzentrieren können. Sie repräsentieren wahrscheinlich CPU-intensive Funktionen.

Erwähnenswert ist, dass, wenn Sie auf ein Element eines Flame Graphs klicken, eine Vergrößerung seiner Umgebung über dem Diagramm angezeigt wird.

Verwendung von perf zur Stichprobenentnahme eines laufenden Prozesses

Dies ist großartig, um Flame-Graph-Daten von einem bereits laufenden Prozess aufzuzeichnen, den Sie nicht unterbrechen möchten. Stellen Sie sich einen Produktionsprozess mit einem schwer zu reproduzierenden Problem vor.

bash
perf record -F99 -p `pgrep -n node` -- sleep 3

Wozu dient das sleep 3? Es ist da, um perf am Laufen zu halten - obwohl die Option -p auf eine andere PID verweist, muss der Befehl in einem Prozess ausgeführt werden und mit diesem enden. perf läuft während der Lebensdauer des Befehls, den Sie ihm übergeben, unabhängig davon, ob Sie diesen Befehl tatsächlich profilieren. sleep 3 stellt sicher, dass perf 3 Sekunden lang läuft.

Warum ist -F (Profilierungsfrequenz) auf 99 gesetzt? Das ist ein vernünftiger Standardwert. Sie können ihn bei Bedarf anpassen. -F99 weist perf an, 99 Stichproben pro Sekunde zu nehmen. Erhöhen Sie den Wert für eine höhere Präzision. Niedrigere Werte sollten eine geringere Ausgabe mit weniger genauen Ergebnissen liefern. Die benötigte Präzision hängt davon ab, wie lange Ihre CPU-intensiven Funktionen tatsächlich laufen. Wenn Sie nach der Ursache für eine spürbare Verlangsamung suchen, sollten 99 Frames pro Sekunde mehr als ausreichend sein.

Nachdem Sie die 3-Sekunden-perf-Aufzeichnung erhalten haben, fahren Sie mit den letzten beiden Schritten von oben fort, um das Flame Graph zu generieren.

Ausfiltern von internen Node.js-Funktionen

Normalerweise möchten Sie nur die Leistung Ihrer Aufrufe betrachten. Das Ausfiltern von internen Node.js- und V8-Funktionen kann das Diagramm daher viel einfacher lesbar machen. Sie können Ihre perf-Datei mit Folgendem bereinigen:

bash
sed -i -r \
    -e '/(_libc_start|LazyCompile) |v8::internal::BuiltIn|Stub|LoadIC:\\[\\[' \
    -e '/^$/d' \
    perf.data > perf.out

Wenn Sie Ihr Flame Graph lesen und es seltsam erscheint, als ob etwas in der wichtigsten Funktion, die die meiste Zeit beansprucht, fehlt, versuchen Sie, Ihr Flame Graph ohne die Filter zu generieren - vielleicht haben Sie einen seltenen Fall eines Problems mit Node.js selbst.

Node.js-Profilierungsoptionen

--perf-basic-prof-only-functions und --perf-basic-prof sind die beiden, die für das Debuggen Ihres JavaScript-Codes nützlich sind. Andere Optionen werden für die Profilierung von Node.js selbst verwendet, was außerhalb des Rahmens dieses Leitfadens liegt.

--perf-basic-prof-only-functions erzeugt weniger Ausgabe, daher ist dies die Option mit dem geringsten Overhead.

Warum brauche ich sie überhaupt?

Nun, ohne diese Optionen erhalten Sie immer noch ein Flamegraph, aber die meisten Balken sind mit v8::Function::Call beschriftet.

Perf-Ausgabeprobleme

Node.js 8.x V8-Pipeline-Änderungen

Node.js 8.x und höher wird mit neuen Optimierungen an der JavaScript-Kompilierungs-Pipeline in der V8-Engine ausgeliefert, die Funktionsnamen/Referenzen für perf manchmal unerreichbar macht. (Es wird Turbofan genannt)

Das Ergebnis ist, dass Sie Ihre Funktionsnamen im Flamegraph möglicherweise nicht richtig erhalten.

Sie werden ByteCodeHandler: sehen, wo Sie Funktionsnamen erwarten würden.

0x hat einige integrierte Abhilfemaßnahmen dafür.

Für Details siehe:

Node.js 10+

Node.js 10.x behebt das Problem mit Turbofan mithilfe des Flags --interpreted-frames-native-stack.

Führen Sie node --interpreted-frames-native-stack --perf-basic-prof-only-functions aus, um Funktionsnamen im Flamegraph zu erhalten, unabhängig davon, welche Pipeline V8 verwendet hat, um Ihr JavaScript zu kompilieren.

Fehlerhafte Beschriftungen im Flamegraph

Wenn Sie Beschriftungen wie diese sehen

bash
node`_ZN2v88internal11interpreter17BytecodeGenerator15VisitStatementsEPMS0_8Zone

bedeutet dies, dass das von Ihnen verwendete Linux perf nicht mit Demangle-Unterstützung kompiliert wurde, siehe https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1396654 zum Beispiel.

Beispiele

Üben Sie selbst das Erfassen von Flamegraphs mit einer Flamegraph-Übung!