Bagikan melalui


Debug penggunaan tinggi CPU di .NET Core

Artikel ini berlaku untuk: ✔️ .NET Core 3.1 SDK dan versi yang lebih baru

Dalam tutorial ini, Anda akan mempelajari cara men-debug skenario penggunaan CPU yang berlebihan. Dengan menggunakan repositori kode sumber contoh ASP.NET Core aplikasi web, Anda dapat secara sengaja menyebabkan kebuntuan. Titik akhir akan berhenti merespons dan mengalami penumpukan utas. Anda akan mempelajari bagaimana Anda dapat menggunakan berbagai alat untuk mendiagnosis skenario ini dengan beberapa bagian utama data diagnostik.

Dalam tutorial ini, Anda akan:

  • Menyelidiki penggunaan CPU yang tinggi
  • Menentukan penggunaan CPU dengan penghitung dotnet
  • Menggunakan dotnet-trace untuk pembuatan jejak
  • Profil kinerja dalam PerfView
  • Mendiagnosis dan memecahkan penggunaan CPU yang berlebihan

Prasyarat

Tutorial ini menggunakan:

Penghitung CPU

Sebelum mencoba tutorial ini, silakan instal versi terbaru penghitung dotnet:

dotnet tool install --global dotnet-counters

Jika aplikasi Anda menjalankan versi .NET yang lebih lama dari .NET 9, antarmuka pengguna output penghitung dotnet akan terlihat sedikit berbeda; lihat penghitung dotnet untuk detailnya.

Sebelum mencoba mengumpulkan data diagnostik, Anda perlu mengamati kondisi CPU yang tinggi. Jalankan aplikasi sampel menggunakan perintah berikut dari direktori akar proyek.

dotnet run

Untuk memeriksa penggunaan CPU saat ini, gunakan perintah alat penghitung dotnet :

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Outputnya harus mirip dengan yang berikut ini:

Press p to pause, r to resume, q to quit.
    Status: Running

Name                                                            Current Value      Last Delta
[System.Runtime]
    dotnet.assembly.count ({assembly})                               111               0
    dotnet.gc.collections ({collection})
        gc.heap.generation
        ------------------
        gen0                                                           8               0
        gen1                                                           1               0
        gen2                                                           0               0
    dotnet.gc.heap.total_allocated (By)                        4,042,656          24,512
    dotnet.gc.last_collection.heap.fragmentation.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     801,728               0
        gen1                                                       6,048               0
        gen2                                                           0               0
        loh                                                            0               0
        poh                                                            0               0
    dotnet.gc.last_collection.heap.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     811,512               0
        gen1                                                     562,024               0
        gen2                                                   1,095,056               0
        loh                                                       98,384               0
        poh                                                       24,528               0
    dotnet.gc.last_collection.memory.committed_size (By)       5,623,808               0
    dotnet.gc.pause.time (s)                                           0.019           0
    dotnet.jit.compilation.time (s)                                    0.582           0
    dotnet.jit.compiled_il.size (By)                             138,895               0
    dotnet.jit.compiled_methods ({method})                         1,470               0
    dotnet.monitor.lock_contentions ({contention})                     4               0
    dotnet.process.cpu.count ({cpu})                                  22               0
    dotnet.process.cpu.time (s)
        cpu.mode
        --------
        system                                                         0.109           0
        user                                                           0.453           0
    dotnet.process.memory.working_set (By)                    65,515,520               0
    dotnet.thread_pool.queue.length ({work_item})                      0               0
    dotnet.thread_pool.thread.count ({thread})                         0               0
    dotnet.thread_pool.work_item.count ({work_item})                   6               0
    dotnet.timer.count ({timer})                                       0               0

Berfokus pada Last Delta nilai dotnet.process.cpu.time, ini memberi tahu kami berapa detik CPU telah aktif dalam periode refresh (saat ini diatur ke default 1 s). Dengan aplikasi web berjalan, segera setelah startup, CPU tidak terpakai sama sekali dan nilai delta ini keduanya 0. Navigasikan ke api/diagscenario/highcpu rute dengan 60000 sebagai parameter rute:

https://localhost:5001/api/diagscenario/highcpu/60000

Sekarang, jalankan kembali perintah dotnet-counters .

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Anda akan melihat peningkatan penggunaan CPU seperti yang ditunjukkan di bawah ini (tergantung pada komputer host, mengharapkan penggunaan CPU yang bervariasi):

Press p to pause, r to resume, q to quit.
    Status: Running

Name                                                            Current Value      Last Delta
[System.Runtime]
    dotnet.assembly.count ({assembly})                               111               0
    dotnet.gc.collections ({collection})
        gc.heap.generation
        ------------------
        gen0                                                           8               0
        gen1                                                           1               0
        gen2                                                           0               0
    dotnet.gc.heap.total_allocated (By)                        4,042,656          24,512
    dotnet.gc.last_collection.heap.fragmentation.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     801,728               0
        gen1                                                       6,048               0
        gen2                                                           0               0
        loh                                                            0               0
        poh                                                            0               0
    dotnet.gc.last_collection.heap.size (By)
        gc.heap.generation
        ------------------
        gen0                                                     811,512               0
        gen1                                                     562,024               0
        gen2                                                   1,095,056               0
        loh                                                       98,384               0
        poh                                                       24,528               0
    dotnet.gc.last_collection.memory.committed_size (By)       5,623,808               0
    dotnet.gc.pause.time (s)                                           0.019           0
    dotnet.jit.compilation.time (s)                                    0.582           0
    dotnet.jit.compiled_il.size (By)                             138,895               0
    dotnet.jit.compiled_methods ({method})                         1,470               0
    dotnet.monitor.lock_contentions ({contention})                     4               0
    dotnet.process.cpu.count ({cpu})                                  22               0
    dotnet.process.cpu.time (s)
        cpu.mode
        --------
        system                                                         0.344           0.013
        user                                                          14.203           0.963
    dotnet.process.memory.working_set (By)                    65,515,520               0
    dotnet.thread_pool.queue.length ({work_item})                      0               0
    dotnet.thread_pool.thread.count ({thread})                         0               0
    dotnet.thread_pool.work_item.count ({work_item})                   6               0
    dotnet.timer.count ({timer})                                       0               0

Selama durasi permintaan, penggunaan CPU akan melayang di sekitar nilai yang ditingkatkan.

Petunjuk / Saran

Untuk memvisualisasikan penggunaan CPU yang lebih tinggi, Anda dapat menjalankan titik akhir ini di beberapa tab browser secara bersamaan.

Pada titik ini, Anda dapat dengan aman mengatakan CPU berjalan lebih tinggi dari yang Anda harapkan. Mengidentifikasi efek masalah adalah kunci untuk menemukan penyebabnya. Kami akan menggunakan efek konsumsi CPU yang tinggi selain alat diagnostik untuk menemukan penyebab masalah.

Menggunakan Profiler untuk Menganalisis Penggunaan CPU Tinggi

Saat menganalisis aplikasi dengan penggunaan CPU yang tinggi, gunakan profiler untuk memahami apa yang dilakukan kode. dotnet-trace collect bekerja pada semua sistem operasi, tetapi bias titik aman dan callstack terkelola membatasinya ke informasi yang lebih umum daripada profiler yang sadar kernel seperti ETW untuk Windows atau perf untuk Linux. Bergantung pada sistem operasi dan versi .NET Anda, kemampuan pembuatan profil yang ditingkatkan mungkin tersedia—lihat tab khusus platform yang mengikuti panduan terperinci.

Gunakan dotnet-trace collect-linux (.NET 10+)

Pada .NET 10 dan yang lebih baru, dotnet-trace collect-linux adalah pendekatan pembuatan profil yang direkomendasikan di Linux. Ini menggabungkan EventPipe dengan perf_events tingkat OS untuk menghasilkan satu jejak terpadu yang mencakup callstack terkelola dan asli, semuanya tanpa memerlukan proses restart. Ini memerlukan izin root dan kernel Linux 6.4+ dengan CONFIG_USER_EVENTS=y. Lihat prasyarat collect-linux untuk persyaratan lengkap.

Pastikan target debug sampel dikonfigurasi untuk menargetkan .NET 10 atau yang lebih baru, lalu jalankan dan coba endpoint CPU tinggi (https://localhost:5001/api/diagscenario/highcpu/60000) lagi. Saat menjalankan permintaan selama 1 menit, jalankan dotnet-trace collect-linux untuk menangkap pelacakan di seluruh komputer.

sudo dotnet-trace collect-linux

Biarkan berjalan selama sekitar 20-30 detik, lalu tekan Ctrl+C atau Enter untuk menghentikan koleksi. Hasilnya adalah .nettrace file yang mencakup tumpukan panggilan terkelola dan asli.

.nettrace Buka dengan PerfView dan gunakan tampilan Tumpukan CPU untuk mengidentifikasi metode yang paling memakan waktu CPU.

Untuk informasi mengenai cara mengatasi simbol-simbol runtime asli dalam pelacakan, lihat Mendapatkan simbol untuk kerangka runtime asli.

Gunakan perf

Alat ini perf juga dapat digunakan untuk menghasilkan profil aplikasi .NET Core. Keluar dari instans sebelumnya dari target debug sampel.

Atur DOTNET_PerfMapEnabled variabel lingkungan untuk menyebabkan aplikasi .NET membuat map file di /tmp direktori. File map ini digunakan oleh perf untuk memetakan alamat CPU ke fungsi yang dihasilkan JIT dengan nama. Untuk informasi selengkapnya, lihat Mengekspor peta perf dan jit dump.

Jalankan target debug sampel dalam sesi terminal yang sama.

export DOTNET_PerfMapEnabled=1
dotnet run

Latih titik akhir API CPU tinggi (https://localhost:5001/api/diagscenario/highcpu/60000) lagi. Saat menjalankan permintaan 1 menit, jalankan perintah perf dengan ID proses Anda.

sudo perf record -p 2266 -g

Perintah perf memulai proses pengumpulan performa. Biarkan berjalan selama sekitar 20-30 detik, lalu tekan Ctrl+C untuk keluar dari proses pengumpulan. Anda dapat menggunakan perintah yang sama perf untuk melihat output pelacakan.

sudo perf report -f

Anda juga dapat menghasilkan grafik api dengan menggunakan perintah berikut:

git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
sudo perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

Perintah ini menghasilkan flamegraph.svg yang dapat Anda lihat di browser untuk menyelidiki masalah performa:

Gambar SVG grafik api

Menganalisis Data CPU Tinggi dengan Visual Studio

Semua file *.nettrace dapat dianalisis di Visual Studio. Untuk menganalisis file Linux *.nettrace di Visual Studio, transfer file *.nettrace, selain dokumen lain yang diperlukan, ke komputer Windows, lalu buka file *.nettrace di Visual Studio. Untuk informasi selengkapnya, lihat Menganalisis Data Penggunaan CPU.

Lihat juga

Langkah selanjutnya