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.
- Installieren Sie
perf
(normalerweise über das Paket linux-tools-common verfügbar, falls nicht bereits installiert) - Versuchen Sie,
perf
auszuführen - es könnte sich über fehlende Kernel-Module beschweren, installieren Sie diese ebenfalls - Führen Sie Node mit aktiviertem perf aus (siehe perf-Ausgabeprobleme für Tipps speziell zu Node.js-Versionen)
perf record -e cycles:u -g -- node --perf-basic-prof app.js
- 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.
- 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 - Installieren Sie stackvis, falls es noch nicht installiert ist,
npm i -g stackvis
- 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.
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:
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:
- https://github.com/nodejs/benchmarking/issues/168
- https://github.com/nodejs/diagnostics/issues/148#issuecomment-369348961
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
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!