Eseguire il debug dell'utilizzo elevato della CPU in .NET Core
Questo articolo si applica a: ✔️ .NET Core 3.1 SDK e versioni successive
In questa esercitazione si apprenderà come eseguire il debug di uno scenario di utilizzo eccessivo della CPU. Usando l'esempio fornito nel repository del codice sorgente dell'app Web ASP.NET Core, è possibile causare intenzionalmente un deadlock. L'endpoint smetterà di rispondere e si verificherà un accumulo di thread. Si apprenderà come usare vari strumenti per diagnosticare questo scenario con diversi dati di diagnostica chiave.
Questa esercitazione illustra come:
- Analizzare l'utilizzo elevato della CPU
- Determinare l'utilizzo della CPU con dotnet-counters
- Usare dotnet-trace per la generazione di tracce
- Profilare le prestazioni in PerfView
- Diagnosticare e risolvere un utilizzo eccessivo della CPU
Prerequisiti
L'esercitazione usa:
- .NET Core 3.1 SDK o versione successiva.
- Destinazione di debug di esempio per attivare lo scenario.
- dotnet-trace per elencare i processi e generare un profilo.
- dotnet-counters per monitorare l'utilizzo della CPU.
Contatori CPU
Prima di tentare di raccogliere i dati di diagnostica, è necessario osservare una condizione di utilizzo elevato della CPU. Eseguire l'applicazione di esempio con il comando seguente dalla directory radice del progetto.
dotnet run
Per trovare l'ID del processo, usare il comando seguente:
dotnet-trace ps
Prendere nota dell'ID del processo dall'output del comando. L'ID del processo nell'esempio era 22884
, ma sarà diverso in altre condizioni. Per controllare l'utilizzo corrente della CPU, usare il comando dello strumento dotnet-counters:
dotnet-counters monitor --refresh-interval 1 -p 22884
refresh-interval
è il numero di secondi che intercorre tra i polling del contatore per il recupero dei valori della CPU. L'output avrà un aspetto analogo al seguente:
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
Con l'app Web in esecuzione, subito dopo l'avvio, la CPU non viene affatto usata e l'utilizzo segnalato è 0%
. Passare alla route api/diagscenario/highcpu
con 60000
come parametro di route:
https://localhost:5001/api/diagscenario/highcpu/60000
A questo punto, eseguire di nuovo il comando dotnet-counters. Se si è interessati al monitoraggio solo del contatore cpu-usage
, aggiungere '--counters System.Runtime[cpu-usage]' al comando precedente. Non si è certi che la CPU sia in uso, quindi verrà monitorato lo stesso elenco di contatori precedente per verificare che i valori dei contatori siano compresi nell'intervallo previsto per l'applicazione.
dotnet-counters monitor -p 22884 --refresh-interval 1
Si dovrebbe notare un aumento dell'utilizzo della CPU, come illustrato di seguito (a seconda del computer host, prevedere un utilizzo diverso della CPU):
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
Per tutta la durata della richiesta, l'utilizzo della CPU si attesterà intorno alla percentuale aumentata.
Suggerimento
Per visualizzare un utilizzo ancora più elevato della CPU, è possibile eseguire questo endpoint in più schede del browser contemporaneamente.
A questo punto, è possibile affermare senza dubbio che l'utilizzo della CPU è più alto del previsto. Identificare gli effetti di un problema è fondamentale per trovare la causa. Si userà l'effetto dell'utilizzo elevato della CPU oltre agli strumenti di diagnostica per individuare la causa del problema.
Analizzare l'utilizzo elevato della CPU con un profiler
Quando si analizza un'app con un utilizzo elevato della CPU, è necessario uno strumento di diagnostica in grado di fornire informazioni dettagliate sulle operazioni eseguite dal codice. La scelta abituale è un profiler e sono disponibili diverse opzioni tra cui scegliere. dotnet-trace
può essere usato in tutti i sistemi operativi, tuttavia, le sue limitazioni per la distorsione del punto sicuro e gli stack di chiamate solo per codice gestito generano informazioni più generali rispetto a un profiler in grado di riconoscere il kernel come 'perf' per Linux o ETW per Windows. Se l'analisi delle prestazioni prevede solo codice gestito, in genere dotnet-trace
sarà sufficiente.
Lo strumento perf
può essere usato per generare profili di app .NET Core. Verrà proposta una dimostrazione di questo strumento, anche se si potrebbe usare anche dotnet-trace. Uscire dall'istanza precedente della destinazione di debug di esempio.
Impostare la variabile di ambiente DOTNET_PerfMapEnabled
per fare in modo che l'app .NET crei un file map
nella directory /tmp
. Questo file map
viene usato da perf
per eseguire il mapping degli indirizzi della CPU alle funzioni generate da JIT in base al nome. Per altre informazioni, vedere Esportare mappe delle prestazioni e dump jit.
Nota
.NET 6 usa come standard il prefisso DOTNET_
anziché COMPlus_
per le variabili di ambiente che configurano il comportamento di runtime di .NET. Tuttavia, il prefisso COMPlus_
continuerà a funzionare. Se si usa una versione precedente del runtime .NET, è comunque consigliabile usare il prefisso COMPlus_
per le variabili di ambiente.
Eseguire la destinazione di debug di esempio nella stessa sessione del terminale.
export DOTNET_PerfMapEnabled=1
dotnet run
Eseguire di nuovo l'endpoint dell'API di uitlizzo della CPU elevato (https://localhost:5001/api/diagscenario/highcpu/60000
). Mentre è in esecuzione all'interno della richiesta di 1 minuto, eseguire il comando perf
con l'ID del processo:
sudo perf record -p 2266 -g
Il comando perf
avvia il processo di raccolta delle prestazioni. Lasciarlo in esecuzione per circa 20-30 secondi, quindi premere CTRL+C per uscire dal processo di raccolta. È possibile usare lo stesso comando perf
per visualizzare l'output della traccia.
sudo perf report -f
È anche possibile generare un Flame Graph usando i comandi seguenti:
git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg
Questo comando genera un flamegraph.svg
che è possibile visualizzare nel browser per analizzare il problema di prestazioni:
Analisi dei dati di utilizzo elevato della CPU con Visual Studio
Tutti i file *.nettrace possono essere analizzati in Visual Studio. Per analizzare un file *.nettrace Linux in Visual Studio, trasferire il file *.nettrace, oltre agli altri documenti necessari, in un computer Windows e quindi aprire il file *.nettrace in Visual Studio. Per altre informazioni, vedere Analizzare i dati di utilizzo della CPU.
Vedi anche
- dotnet-trace per elencare i processi
- dotnet-counters per controllare l'utilizzo della memoria gestita
- dotnet-dump per raccogliere e analizzare un file dump
- dotnet/diagnostics