.NET Core'da yüksek CPU kullanımında hata ayıklama

Bu makale şunlar için geçerlidir: ✔️ .NET Core 3.1 SDK ve sonraki sürümler

Bu öğreticide, aşırı CPU kullanımı senaryosunda hata ayıklamayı öğreneceksiniz. Sağlanan örnek ASP.NET Core web uygulaması kaynak kodu deposunu kullanarak, kasıtlı olarak bir kilitlenmeye neden olabilirsiniz. Uç nokta yanıt vermeyi durdurur ve iş parçacığı birikimi meydana gelir. Birçok önemli tanılama verisi parçasıyla bu senaryoyu tanılamak için çeşitli araçları nasıl kullanabileceğinizi öğreneceksiniz.

Bu kılavuzda şunları yapacaksınız:

  • Yüksek CPU kullanımını araştırma
  • dotnet-counters ile CPU kullanımını belirleme
  • İzleme oluşturma için dotnet-trace kullanma
  • PerfView'da profil performansı
  • Aşırı CPU kullanımını tanılama ve çözme

Önkoşullar

Öğreticide aşağıdakiler kullanılır:

CPU sayaçları

Bu öğreticiyi denemeden önce lütfen dotnet-counters'ın en son sürümünü yükleyin:

dotnet tool install --global dotnet-counters

Uygulamanız .NET 9'dan daha eski bir .NET sürümü çalıştırıyorsa dotnet-counters çıkış kullanıcı arabirimi biraz farklı görünür; Ayrıntılar için dotnet-counters bölümüne bakın.

Tanılama verilerini toplamaya çalışmadan önce yüksek CPU koşulunu gözlemlemeniz gerekir. Proje kök dizininden aşağıdaki komutu kullanarak örnek uygulamayı çalıştırın.

dotnet run

Geçerli CPU kullanımını denetlemek için dotnet-counters tool komutunu kullanın:

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Çıkış aşağıdakine benzer olmalıdır:

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

Last Delta dotnet.process.cpu.time değerlerine odaklanan bu değerler, şu anda varsayılan olarak 1 sn olan yenileme süresi içinde CPU'nun kaç saniye aktif olduğunu belirtir. Web uygulaması çalışırken, başlangıçtan hemen sonra CPU hiç tüketilmiyor ve bu delta değerlerinin her ikisi de 0. api/diagscenario/highcpu yoluna, 60000 yol parametresi olarak kullanarak gidin.

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

Şimdi dotnet-counters komutunu yeniden çalıştırın.

dotnet-counters monitor -n DiagnosticScenarios --showDeltas

Aşağıda gösterildiği gibi CPU kullanımında bir artış görmeniz gerekir (konak makineye bağlı olarak değişen CPU kullanımı beklenmelidir):

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

İsteğin süresi boyunca CPU kullanımı, artan değerin yakınında kalır.

Tavsiye

Daha da yüksek bir CPU kullanımını görselleştirmek için bu uç noktayı aynı anda birden çok tarayıcı sekmesinde kullanabilirsiniz.

Bu noktada, CPU'nin beklediğinizden daha yüksek çalıştığını güvenle söyleyebilirsiniz. Sorunun etkilerini belirlemek, nedeni bulmanın anahtarıdır. Sorunun nedenini bulmak için tanılama araçlarına ek olarak yüksek CPU tüketiminin etkisini kullanacağız.

Profil Oluşturucu ile Yüksek CPU'ları Analiz Etme

Yüksek CPU kullanımına sahip bir uygulamayı analiz ederken, kodun ne yaptığını anlamak için bir profil oluşturucu kullanın. dotnet-trace collect tüm işletim sistemlerinde çalışır, ancak güvenli nokta önyargısı ve yalnızca yönetimli çağrı yığınları, bunu Windows için ETW veya Linux için perf gibi çekirdek-odaklı bir profil oluşturucusundan daha genel bilgilerle sınırlar. İşletim sisteminize ve .NET sürümünüze bağlı olarak, iyileştirilmiş profil oluşturma özellikleri kullanılabilir; ayrıntılı yönergeler için aşağıdaki platforma özgü sekmelere bakın.

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

.NET 10 ve sonraki sürümlerde, dotnet-trace collect-linux Linux'ta önerilen profil oluşturma yaklaşımıdır. İşlem yeniden başlatma gerektirmeden hem yönetilen hem de yerel çağrı yığınlarını içeren tek bir birleşik izleme oluşturmak için EventPipe'i işletim sistemi düzeyinde perf_events ile birleştirir. Bunun için kök izinleri ve CONFIG_USER_EVENTS=y ile Linux çekirdeği 6.4+ gerekir. Tam gereksinimler için bkz. collect-linux önkoşulları .

Örnek hata ayıklama hedefinin .NET 10 veya üzerini hedeflemek için yapılandırıldığından emin olun, ardından bunu çalıştırın ve yüksek CPU uç noktasını (https://localhost:5001/api/diagscenario/highcpu/60000) yeniden kullanın. 1 dakikalık istek boyunca çalışırken, makine genelinde bir iz yakalamak için dotnet-trace collect-linux komutunu çalıştırın.

sudo dotnet-trace collect-linux

Yaklaşık 20-30 saniye çalışmasına izin verin, ardından koleksiyonu durdurmak için Ctrl+C veya Enter tuşlarına basın. Sonuç, hem yönetilen hem de yerel çağrı yığınlarını içeren bir .nettrace dosyadır.

ile .nettrace dosyasını PerfView açın ve CPU Yığınları görünümünü kullanarak en fazla CPU süresini tüketen yöntemleri belirleyin.

İzlemedeki yerel çalışma zamanı simgelerini çözümleme hakkında bilgi için bkz. Yerel çalışma zamanı çerçeveleri için sembolleri alma.

perf komutunu kullanma

Araç perf , .NET Core uygulama profilleri oluşturmak için de kullanılabilir. Örnek hata ayıklama hedefinin önceki örneğinden çıkın.

.NET uygulamasının DOTNET_PerfMapEnabled dizinde bir map dosya oluşturmasına neden olacak ortam değişkenini /tmp ayarlayın. Bu map dosya, CPU adreslerini ada göre JIT tarafından oluşturulan işlevlere eşlemek üzere perf tarafından kullanılır. Daha fazla bilgi için Performans haritalarını ve JIT dökümlerini dışa aktarma kısmına bakın.

Örnek hata ayıklama hedefini aynı terminal oturumunda çalıştırın.

export DOTNET_PerfMapEnabled=1
dotnet run

Yüksek CPU API uç noktasını (https://localhost:5001/api/diagscenario/highcpu/60000) yeniden kullanın. 1 dakikalık istek içinde çalışırken, komutu işlem kimliğiniz ile çalıştırın perf :

sudo perf record -p 2266 -g

komutu perf performans toplama işlemini başlatır. Yaklaşık 20-30 saniye çalışmasına izin verin, ardından toplama işleminden çıkmak için Ctrl+C tuşlarına basın. İzlemenin sonucunu görmek için aynı perf komutunu kullanabilirsiniz.

sudo perf report -f

Aşağıdaki komutları kullanarak bir alev grafiği de oluşturabilirsiniz:

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

Bu komut, performans sorununu araştırmak için tarayıcıda görüntüleyebileceğiniz bir flamegraph.svg oluşturur:

Alev grafiği SVG görüntüsü

Visual Studio ile Yüksek CPU Verilerini Çözümleme

Tüm *.nettrace dosyaları Visual Studio'da analiz edilebilir. Visual Studio'da bir Linux *.nettrace dosyasını analiz etmek için, *.nettrace dosyasını, diğer gerekli belgelere ek olarak bir Windows makinesine aktarın ve ardından *.nettrace dosyasını Visual Studio'da açın. Daha fazla bilgi için bkz. CPU Kullanım Verilerini Çözümleme.

Ayrıca bakınız

Sonraki Adımlar