Visual Studio Parallel Stacks ile Memory Dump Analizi
Üretim ortamında donan bir masaüstü uygulaması ya da bir türlü yüklenmeyen bir web sayfası, geliştiricinin elinde çoğu zaman iz bırakmaz. Sorunu yerelde yeniden üretmek için makine özelliklerinden trafik yüküne kadar bir sürü koşulu taklit etmek gerekir. Visual Studio Blog’da Aaron Powell’ın anlattığı yaklaşım bu döngüyü başka yerden kırıyor, olay anında bir memory dump yakalanıyor ve sonradan Visual Studio içinde, sanki hata o anda oluyormuş gibi hata ayıklanıyor. Aşağıda C# uygulamasında aşırı sayıda aktif thread’in yol açtığı kilitlenmenin dump üzerinden nasıl tespit edildiği, Parallel Stacks penceresinin ve GitHub Copilot entegrasyonunun bu süreçte ne işe yaradığı adım adım anlatılıyor.
Sorun: ThreadPool doygunluğu
Senaryodaki hata sınıfı C# uygulamalarında sık karşılaşılan bir durum, ThreadPool‘un kapasitesinin üzerinde iş biriktirmesi. Uzun süre tamamlanmayan çok sayıda asenkron görev sıraya girdiğinde havuz yeni işleri zamanında işleyemiyor, uygulama yanıt veremez hale geliyor. Klasik anlamda bir “çökme” değil bu; süreç ayakta ama ilerlemiyor. Sorunu görebilmek için o andaki thread durumunun bir fotoğrafı gerekiyor.
Memory dump’ı yakalayan izleyici thread
Dump’ın kendiliğinden oluşması için örnekte küçük bir izleme mekanizması kuruluyor. Arka planda çalışan ayrı bir thread, düzenli aralıklarla ThreadPool‘a küçük bir iş gönderiyor ve bu işin tamamlanma süresini ölçüyor. Süre belirlenen eşiği aşarsa havuzun tıkandığı varsayılıyor, süreç belleği diske yazılıyor. Eşik de aralık da örnekte üç saniye.
var interval = 3_000;
var thread = new Thread(() =>
{
while (true)
{
Thread.Sleep(interval);
Stopwatch stopwatch = Stopwatch.StartNew();
Task.Run(() =>
{
stopwatch.Stop();
}).Wait();
if (stopwatch.ElapsedMilliseconds > interval)
{
// Took over the interval to complete
Console.WriteLine($"Task took too long: {stopwatch.ElapsedMilliseconds} ms");
string path = Path.Combine(AppContext.BaseDirectory, $"fulldump-{Environment.ProcessId}-{DateTime.Now:yyyyMMdd-HHmmss}.dmp");
MiniDumper.WriteCurrentProcess(path);
}
}
})
{
Name = "ThreadPool Watcher",
IsBackground = true
};
thread.Start();
Kod her üç saniyede bir yeni bir Stopwatch başlatıyor, kronometreyi durduracak bir Task‘i havuza bırakıyor, görev tamamlandığında geçen süreyi kontrol ediyor. Eşik aşıldığında Windows API’leri üzerinden dump dosyası oluşturuluyor. Thread’in Name değerinin ThreadPool Watcher olarak ayarlanması ayrıntı gibi duruyor ama ilerleyen adımlarda Parallel Stacks görünümünde bu thread’i anında tanımayı sağlıyor. Dump oluşturma kodunun ayrıntıları,.NET Blog’daki eşlik eden yazıda ele alınıyor.
Kilitlenmeyi bilinçli olarak tetiklemek
Demoda havuzu tüketmek için uzun süren çok sayıda iş aynı anda başlatılıyor:
Parallel.For(0, 100, (i) => {
Console.WriteLine("Running task {0}", i);
Thread.Sleep(10_000);
});
Uygulama arka plan thread’lerini ayağa kaldırıyor, her biri on saniye uyuyor, bu işler makul sürede bitmediği için yenileri sıraya ekleniyor ve ThreadPool aşırı yüklenince uygulama takılıyor. İzleyici thread devrede olduğundan takılma anının belleği .dmp dosyası olarak kaydediliyor.
Dump dosyasını Visual Studio’da açmak
Oluşan .dmp dosyası doğrudan Visual Studio ile açılabiliyor. Bir memory dump üzerinde yapılabilecek çok sayıda işlem var ama burada amaç uygulamanın neden yanıt veremez hale geldiğini anlamak, o yüzden Debug with Mixed seçeneği kullanılıyor. Bu mod, hem yönetilen (C#) hem de yerel kodu birlikte inceleyebilen bir hata ayıklama oturumu başlatıyor.
Oturum açıldığında deneyim, koda breakpoint koyup debugger ile çalıştırmaya oldukça benziyor. Call Stack görünür durumda, Autos penceresinde yerel değişken değerleri (örnekte i = 2) okunabiliyor, decompile edilmiş dosyalar arasında gezinilebiliyor. Yine de bu görünümlerin hiçbiri ThreadPool‘un o anda neden sıkıştığını tek başına açıklamıyor, bunun için başka bir pencereye geçmek gerekiyor.
Parallel Stacks: 27 thread’in aynı noktada beklemesi
Debug > Windows > Parallel Stacks menüsünden (kısayol: CTRL + SHIFT + D, S) açılan pencere, uygulamada paralel çalışan tüm thread’lerin genel görünümünü veriyor. Örnekte üç ana blok göze çarpıyor:
- Main Thread, uygulamanın kendisi.
- ThreadPool Watcher, dump’ı üreten izleyici thread.
- Ortadaki kutu: üst kısmında 27 Threads yazan, aynı noktada bloke olmuş thread yığını.
Sorunun izi burada. Bu 27 thread’in hepsi aynı yerde, yani Thread.Sleep(10_000) çağrısında bloke durumda. Yazıda bu sayının 16 çekirdekli bir CPU’da beklenenin çok üzerinde olduğu vurgulanıyor. İlgili metodun üzerine gelindiğinde her thread’in tam olarak nerede duraklatıldığı görülebiliyor; herhangi bir thread’e tıklayıp o andaki kendine özgü durumunu incelemek ve sorunun kaynağını daraltmak da mümkün.
Parallel Stacks’in buradaki değeri, tek bir çağrı yığınına bakarak anlaşılamayacak bir örüntüyü, yani çok sayıda thread’in aynı satırda beklemesini görsel olarak ortaya çıkarması.
Copilot ile thread analizini hızlandırmak
Çok thread’li bir uygulamada kök nedeni bulmak, dump bol miktarda bilgi içerse bile zahmetli olabiliyor. Visual Studio bu noktada Parallel Stacks penceresine GitHub Copilot entegrasyonu ekliyor. Pencere üzerindeki Copilot simgesi, mevcut görünümü bağlam olarak alan ve memory dump’a erişimi olan yeni bir sohbet oturumu başlatıyor. Copilot thread’leri, durumlarını ve yığınlarını analiz ederek sorunun çözümü için bir plan önerebiliyor.
Örnekteki analizde Copilot’ın vardığı sonuç, durumun aslında bir çökme olmadığı, bunun bir thread-pool saturation / blocking workload (thread havuzu doygunluğu / bloklayan iş yükü) tablosu olduğu yönünde.
Çıkarım
Üretimde yaşanan bir kilitlenmeyi teşhis etmenin en büyük zorluğu, uygulamanın o anki durumunun elimizde olmaması. Anlatılan akış bu boşluğu üç adımı birbirine bağlayarak kapatıyor. ThreadPool gecikmesini izleyen bir thread dump’ı otomatik yakalıyor, dump Visual Studio’da mixed mod ile açılıp değişkenler ve çağrı yığını inceleniyor, Parallel Stacks thread kullanımını görselleştirip doygunluğu ortaya çıkarıyor. Copilot entegrasyonu da bu bilgi yığınını yorumlamada yapay zeka destekli ek bir katman.
Dump yakalama kodunun ayrıntılarını merak edenler için.NET Blog’daki eşlik eden yazı bu tarafı daha derinlemesine ele alıyor.
Kaynaklar ve İleri Okuma
- devblogs.microsoft.com
- devblogs.microsoft.com
- devblogs.microsoft.com
- devblogs.microsoft.com
- devblogs.microsoft.com
- Today I will… debug a production crash — Visual Studio Blog (Aaron Powell)
- Creating a Memory Dump in C# —.NET Blog
- Visual Studio Blog
- Kubernetes’te Production Debug Güvenliği: Rehber







Yorum gönder