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:

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:

Flame graph SVG image

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

Nächste Schritte