Debuggen einer hohen CPU-Auslastung in .NET Core
Dieser Artikel gilt für: ✔️ .NET Core 3.1 SDK und höher
In diesem Tutorial erfahren Sie, wie Sie eine exzessive CPU-Auslastung debuggen. Mithilfe des bereitgestellten Beispielquellcoderepositorys einer ASP.NET Core-Web-App können Sie absichtlich einen Deadlock auslösen. Der Endpunkt antwortet nicht mehr und es kommt zu einem Thread-Stau. Des Weiteren werden Sie erfahren, wie Sie in einem solchen Szenario mithilfe von verschiedenen Diagnosedaten und Tools eine Diagnose erstellen.
In diesem Tutorial werden Sie Folgendes durchführen:
- Untersuchen der hohen CPU-Auslastung
- Feststellen der CPU-Auslastung mit dotnet-counters
- Generieren von Ablaufverfolgungen mit dotnet-trace
- Erstellen eines Leistungsprofils in PerfView
- Diagnostizieren einer exzessiven CPU-Auslastung und Beheben derselben
Voraussetzungen
Im Tutorial wird Folgendes verwendet:
- .NET Core 3.1 SDK oder höher
- Beispieldebugziel zum Auslösen des Szenarios
- dotnet-trace zum Auflisten von Prozessen und zum Generieren eines Profils
- dotnet-counters zur Überwachung der CPU-Auslastung
CPU-Indikatoren
Bevor Sie versuchen, Diagnosedaten zu erfassen, muss eine hohe CPU-Auslastung gegeben sein. Führen Sie die Beispielanwendung mit dem folgenden Befehl im Stammverzeichnis des Projekts aus.
dotnet run
Suchen Sie mit dem folgenden Befehl nach der Prozess-ID:
dotnet-trace ps
Notieren Sie sich die in der Befehlsausgabe angezeigte Prozess-ID. Die Prozess-ID lautet in diesem Beispiel 22884
, Sie werden jedoch eine andere erhalten. Überprüfen Sie die aktuelle CPU-Auslastung mithilfe des Toolbefehls dotnet-counters:
dotnet-counters monitor --refresh-interval 1 -p 22884
refresh-interval
stellt die Anzahl von Sekunden dar, die verstreichen, wenn der Zähler CPU-Werte abruft. Die Ausgabe sollte ähnlich der Folgenden aussehen:
Press p to pause, r to resume, q to quit.
Status: Running
[System.Runtime]
% Time in GC since last GC (%) 0
Allocation Rate / 1 sec (B) 0
CPU Usage (%) 0
Exception Count / 1 sec 0
GC Heap Size (MB) 4
Gen 0 GC Count / 60 sec 0
Gen 0 Size (B) 0
Gen 1 GC Count / 60 sec 0
Gen 1 Size (B) 0
Gen 2 GC Count / 60 sec 0
Gen 2 Size (B) 0
LOH Size (B) 0
Monitor Lock Contention Count / 1 sec 0
Number of Active Timers 1
Number of Assemblies Loaded 140
ThreadPool Completed Work Item Count / 1 sec 3
ThreadPool Queue Length 0
ThreadPool Thread Count 7
Working Set (MB) 63
Wenn die Web-App ausgeführt wird, werden unmittelbar nach dem Start keine CPU-Ressourcen verbraucht, und die Auslastung wird mit 0%
gemeldet. Navigieren Sie zur Route api/diagscenario/highcpu
, und verwenden Sie 60000
als Routenparameter:
https://localhost:5001/api/diagscenario/highcpu/60000
Führen Sie nun noch einmal den Befehl dotnet-counters aus. Wenn Sie nur den cpu-usage
-Zähler überwachen möchten, fügen Sie dem vorherigen Befehl „---counters System.Runtime[cpu-usage]“ hinzu. Wir sind uns nicht sicher, ob die CPU verbraucht wird, daher überwachen wir dieselbe Liste von Leistungsindikatoren wie oben, um zu überprüfen, ob die Zählerwerte innerhalb des erwarteten Bereichs für unsere Anwendung liegen.
dotnet-counters monitor -p 22884 --refresh-interval 1
Sie sollten einen Anstieg der CPU-Auslastung wie unten dargestellt feststellen (je nach Hostcomputer ist mit einer unterschiedlichen CPU-Auslastung zu rechnen):
Press p to pause, r to resume, q to quit.
Status: Running
[System.Runtime]
% Time in GC since last GC (%) 0
Allocation Rate / 1 sec (B) 0
CPU Usage (%) 25
Exception Count / 1 sec 0
GC Heap Size (MB) 4
Gen 0 GC Count / 60 sec 0
Gen 0 Size (B) 0
Gen 1 GC Count / 60 sec 0
Gen 1 Size (B) 0
Gen 2 GC Count / 60 sec 0
Gen 2 Size (B) 0
LOH Size (B) 0
Monitor Lock Contention Count / 1 sec 0
Number of Active Timers 1
Number of Assemblies Loaded 140
ThreadPool Completed Work Item Count / 1 sec 3
ThreadPool Queue Length 0
ThreadPool Thread Count 7
Working Set (MB) 63
Während der gesamten Dauer der Anforderung wird die CPU-Auslastung um den erhöhten Prozentsatz herum schwanken.
Tipp
Wenn Sie die gemeldete CPU-Auslastung noch weiter steigern möchten, können Sie diesen Endpunkt in mehreren Browserregisterkarten gleichzeitig aufrufen.
Nun ist Ihre CPU mit Sicherheit höher ausgelastet als erwartet. Die Ermittlung der Auswirkungen eines Problems ist der Schlüssel, um die Ursache zu finden. Wir verwenden zusätzlich zu Diagnosetools den Effekt eines hohen CPU-Verbrauchs, um die Ursache des Problems zu ermitteln.
Analysieren einer hohen CPU-Auslastung mit Profiler
Wenn Sie eine Anwendung mit hoher CPU-Auslastung analysieren, benötigen Sie ein Diagnosetool, das Einblicke in den Code liefert. Die übliche Wahl ist ein Profiler, und es stehen verschiedene Profileroptionen zur Auswahl. dotnet-trace
kann auf allen Betriebssystemen verwendet werden, aber seine Einschränkungen von Safe-Point-Bias und verwalteten Aufrufstapeln führen zu allgemeineren Informationen im Vergleich zu einem Kernel-fähigen Profiler wie „perf“ für Linux oder ETW für Windows. Wenn Ihre Leistungsuntersuchung nur verwalteten Code umfasst, ist im Allgemeinen dotnet-trace
ausreichend.
Mit dem Tool perf
können Profile für .NET Core-Apps generiert werden. Wir zeigen dieses Tool, obwohl auch dotnet-trace verwendet werden könnte. Beenden Sie die vorherige Instanz des Beispieldebugziels.
Legen Sie die Umgebungsvariable DOTNET_PerfMapEnabled
so fest, dass die .NET Core-App eine map
-Datei im Verzeichnis /tmp
erstellt. Mit dieser map
-Datei ordnet perf
anhand des Namens CPU-Adressen zu JIT-generierten Funktionen zu. Weitere Informationen finden Sie unter Exportieren von Perf-Zuordnungen und Jit-Sicherungen.
Hinweis
In .NET 6 ist das Präfix DOTNET_
statt COMPlus_
Standard für Umgebungsvariablen, die das .NET-Runtimeverhalten konfigurieren. Das Präfix COMPlus_
funktioniert jedoch weiterhin. Wenn Sie eine frühere Version der .NET-Runtime verwenden, sollten Sie weiterhin das Präfix COMPlus_
für Umgebungsvariablen verwenden.
Führen Sie das Beispieldebugziel in der gleichen Terminalsitzung aus.
export DOTNET_PerfMapEnabled=1
dotnet run
Rufen Sie noch einmal den API-Endpunkt (https://localhost:5001/api/diagscenario/highcpu/60000
) auf, der die hohe CPU-Auslastung verursacht. Führen Sie den Befehl perf
mit Ihrer Prozess-ID aus, während der Endpunkt innerhalb der 1-minütigen Anforderung aufgerufen wird:
sudo perf record -p 2266 -g
Der Befehl perf
startet die Erfassung von Leistungsdaten. Lassen Sie den Vorgang ungefähr 20–30 Sekunden laufen, und drücken Sie dann STRG+C, um die Erfassung zu beenden. Mit demselben perf
-Befehl können Sie auch die Ausgabe der Ablaufverfolgung anzeigen.
sudo perf report -f
Zudem können Sie mit den folgenden Befehlen ein Flammendiagramm generieren:
git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg
Dieser Befehl generiert eine flamegraph.svg
-Datei, die Sie im Browser anzeigen können, um das Leistungsproblem genauer zu untersuchen:
Analysieren von Daten mit hoher CPU-Auslastung mit Visual Studio
Alle *.nettrace-Dateien können in Visual Studio analysiert werden. Um eine Linux *.nettrace-Datei in Visual Studio zu analysieren, übertragen Sie die Datei *.nettrace zusätzlich zu den anderen erforderlichen Dokumenten auf einen Windows-Computer, und öffnen Sie dann die Datei *.nettrace in Visual Studio. Weitere Informationen finden Sie unter Analysieren der CPU-Auslastungsdaten.
Siehe auch
- dotnet-trace zum Auflisten von Prozessen
- dotnet-counters zum Überprüfen der Auslastung des verwalteten Speichers
- dotnet-dump zum Erfassen und Analysieren einer Speicherabbilddatei.
- dotnet/diagnostics