Makale Özeti

Bu makalemde sizlerle, aldığımız bu hafıza dökümlerini Visual Studio içerisinden nasıl inceleyebileceğimizi paylaşıyor olacağım. Visual Studio 2010 ile gelen ve .Net framework 4.0 ve üzeri framework sürümlerince desteklenen bu özellik bizlere müşteride oluşan hataların çözümünde büyük kolaylıklar getirmekte.

Makale

    Hata ayıklamakta kullanılabilecek yöntemleri paylaştığım makale serisinde şimdiye kadar geldiğimiz noktada çoğunlukla hata bilgilerini nasıl toplayabileceğimizi, nasıl hafıza dökümü (Dump) ve mini hafıza dökümü (Mini Dump)  alabileceğimizi görmüştük. Hafıza dökümlerini ADPlus gibi Microsoft tarafından bize sunulan hizmet uygulamalarını kullanarak alabildiğimiz gibi C# ile nasıl çalışan bir işlemin ya da uygulamanın kendi kendinin mini dökümünü alabileceğimizi öğrenmiştik. Sanırım bu kadar hafıza dökümü alma yöntemi şimdilik bizlere yetecektir :) Bu makalemde sizlerle, aldığımız bu hafıza dökümlerini Visual Studio içerisinden nasıl inceleyebileceğimizi paylaşıyor olacağım.

    Makalemde anlatacağım ve hata ayıklamada gerçekten işimize oldukça yaracak olan Visual Studio içerisinden hafıza dökümünü inceleyebilmek için bazı isterler mevcut; öncelikle bu özellik Visual Studio 2010 ile gelmiş olduğu için bilgisayarımızda kurulu olması gerekir, ikinci ve önemli bir diğer nokta ise bu özelliğin .Net Framework 4.0 ile birlikte geliyor olması. Maalesef ki .Net framework'ün önceki sürümleri ile geliştirilen uygulamaların hafıza dökümleri (en azından şu an için) Visual Studio içerisinde incelenememekte. Eğer .Net framework 4.0 öncesi bir framework kullanıyorsanız yine de üzülmeyin; daha karmaşık olsa da windbg gibi araçları kullanarak hafıza dökümlerini incelemeniz hala mümkün, bu konuya ilerleyen makalelerimde değinmeyi planlıyorum.

Döküm dosyası Visual Studio içerisinde File->New->File menüsü yardımıyla açılabilir

    Bir hafıza dökümünü incelemeye başlamak için aynı normal bir dosyayı açar gibi Visual Studio içerisinde File->New->File menüsü üzerinden döküm dosyasını açmalıyız;

Mini hafıza döküm dosyası seçimi

    Visual Studio içerisinde bir hafıza döküm dosyasını açıldığında karşımıza Minidump File Summary ekranı gelecektir;

Mini hafıza döküm dosyası özet bilgileri

    Hafıza döküm dosyası hakkında özet bilgi veren bu ekranda; dökümü alınan işlemin adı, dökümün oluşma zamanı, çalışılan işlemci mimarisi (x86/x64), işletim sistemi ve .Net framework sürümü gibi bilgiler bulunabilir. Ek olarak; dökümün alındığı sırada işlem hafıza alanında yüklü bulunan modüllerde ekranın alt bölümünde listelenmektedir.

    Ekranın sağ tarafında yer alan Actions bölümü hata ayıklamaya başlamak, sembol dosyalarını düzenlemek ve ekrandaki bilgileri hafızaya kopyalamak gibi işlevleri barındırmaktadır. Bu bölümde yer alan Set Symbol Paths seçeneği, debug sırasında kaynak kodu bilgileri, satır numaraları, değişken isimleri v.b. önemli bilgilerin yer aldığı sembol dosyalarının nerelerde bulunabileceğini belirtmemize olanak sunmaktadır.

Sembol ayarları

    Ekran görüntüsünde yer alan ayarlarda, varsayılan olarak Microsoft Symbol Servers verilmiş ve örneğin Windows işletim sistemi kodlarının (belirli bir seviyeye kadar) sembol bilgilerinin indirilmesi sağlanmıştır. Bu bölümün hemen altında indirilecek olan bu sembol dosyalarının hangi klasör tutulacağı (yeniden indirmek yerine önbellekten kullanmak amacıyla)  belirtilmektedir. İstenirse klasör resimli buton yardımıyla yeni adreslerin de listeye eklemesi, sil butonu ile de silinmesi mümkündür. Bu seçeneklerdeki önemli bir nokta da sembollerin ne şekilde yükleneceğidir."All Modules, unless excluded" ve "Only specified modules"  şeklinde sunulan iki seçenekten ilkiyle aksi belirtilmediği sürece tüm sembol dosyalarının yüklenmesi istenmektedir; ki bu durum özellikle sembol dosyalarının Microsoft sembol sunucularından indirildiği senaryolarda önemli bir zaman ve bandwidth kaybına neden olacaktır. İkinci seçenek bize sadece belirttiğimiz modüllerin sembollerinin otomatik olarak yüklenmesi şansını sunacaktır.

Sembolleri otomatik olarak yüklenecek modüllerin seçimi

    Debug with Mixed ve Debug with Native Only seçenekleri ise yapacağımızın hata ayıklama işlemi ihtiyacımız olan seviyede başlatma imkanı sunmaktadır. Geliştirdiğimiz uygulamanın bir .net uygulaması olması durumunda hata ayıklama oturumu ilk seçenek yardımıyla başlatılırken; C/C++ gibi dillerle yazılmış olan native uygulamalar için ikinci seçenek seçilmelidir. Yazımın devamından, hata ayıklama makale serisinde devamlı olarak kullandığım ve yazımın sonunda da kaynak kodlarını bulabileceğiniz C# 4.0 ile geliştirilmiş olan örnek CRM uygulamasının alınmış bir dump'ını kullanacağım için Debug with Mixed seçeneği yardımıyla hata ayıklama oturumunu başlatıyorum.

Hafıza dökümünün Mixed Mode seçeneği ile başlatılması

    Hata ayıklama oturumu başladığında karşınıza yukarıdaki gibi bir ekran gelecektir. Peki bu ekran bana ne anlatmakta? Öncelikle ekranın neredeyse tamamını kaplayan sekmesinde yazılanlar sanırım hepimiz için oldukça net, No Source Avaliable, kayak kodu bulunmamakta :) İşlem dökümü alındığı sırada en son işletilen kodun belirtildiği bu noktada KERNELBASE.dll!75a69617() işaret edilmekte. Ekranın sağ alt kısmında yer alan Call Stack penceresi bize o sıradaki çağrı hiyerarşisini göstermekte. Locals, Watch, Immediate Window v.s. gördüğünüz gibi normal bir .net uygulamasının debug işlemde bir break point'te gelindiğinde Visual Studio içerisinde açılan tüm pencereler açık durumda. Yazılım geliştirici aynı bir break point'e takılmış gibi bir dump dosyasını inceleyebilmekte! Bu pencerelere yazımın devamında değineceğim, şimdi isterseniz yeniden bu ekrana geri dönelim.

    Az önce de değindiğim gibi, call stack penceresi bize dökümün alındığı sırada işletilen kodu göstermekte. Bu sebeple, birden çok iş parçacığının işletildiği uygulamalarda son kod her zaman için hatanın bulunduğu yeri işaret etmeyebilir. Şanslıyız ki Visual Studio bize paralelde işletilen tüm iş parçacıklarını görebilme, bunlar arasında dolaşabilme imkanı sunmakta.

Parallel Stack penceresi Debug->Windows->Parallel Stack menüsü yardımıyla açılabilir Açılan Parallel Stacks ekranı

    Debug->Windows->Parallel Stacks menüsü yardımıyla açabileceğiniz bu ekranda çalışan tüm iş parcacıkları çağrı hiyerarşisi de verilerek listelenmektedir. Üstelik dökümün alındığı sırada o kaç iş parçacığının belirtilen kod penceresini çalıştırdığı bilgisi de verilmekte.

    Peki bu iş parçacıklarından yola çıkarak hatanın kaynağına nasıl ulaşabilirim? Aslına bakarsanız bu durum hatadan hataya değişecektir ve zamanla edindiğiniz pratikle bunu daha hızlı çözebileceksiniz. Örnek hatamızdan  en solda yer alan iş parçacığı odaklanmamız gerekendir. Bu iş parçacığını inceleyecek olursak en son native kodların işletilmiş olduğunu göreceğiz. Buradan adım adım aşağıda (geriye) gidecek olursak managed koddan native koda bir geçiş görülebilir (üstten 4. adım). Bizim odaklanmamız gereken noktada bu adımdan öncesi, yani managed kod içerisinde oluşan hata olmalıdır. Bu adımdan öncesini incelediğimizde artık biraz daha mantıklı mesajlar görmekteyiz; string ifadeden sayıya dönüşü, tam sayı ifadeye parse etme denemesi, tam sayıya dönüştürme denemesi, AnaEkran sınıfı içerisindeki kaydet_click fonksiyonunun çalıştırılması. Bingo! Artık daha anlaşılır şeyler görmeye başladık, adım adım hata nedenine yaklaşıyoruz.

    Bu çağrı hiyerarşisini inceleyerek bile koda inmeden hata hakkında fikir sahibi olabiliyoruz; kullanıcı ana ekranımızda yer alan kaydet butonuna basmış ve bu olayı dinleyen fonksiyon içerisinde işletilen bir kod hataya neden olmuş. İşletilen bu kod, bir string ifadeyi tam sayıya dönüştürmeye çalışmış; hata da tam da bu noktada olmuş. Uygulama kodumuzu hatırlayacak olursak, bu hataya neden olabilecek bir kaç farklı satır olduğunu görebiliriz;

private void kaydet_Click(object sender, EventArgs e) {
    var musteriAd = ad.Text;
    var musteriSoyad = soyad.Text;
    var musteriYas = yas.Text;
    var musteriTCKimlik = tckimlik.Text;

    var musteri = new Musteri();
    musteri.Ad = musteriAd;
    musteri.Soyad = musteriSoyad;
    musteri.Yas = Convert.ToInt32(musteriYas);
    musteri.TCKimlik = Convert.ToInt32(musteriTCKimlik);

    musteriKaydet(musteri);
}

    Bu satırlardan hangisinin hataya neden olduğunu öğrenmek için daha detaylı bilgiye ihtiyacımız olacaktır. İhtiyacımız olan detay bilgisi ise sembol dosyalarında yer alan bilgiler kullanılarak ulaşılabilir. Yukarıda size belirttiğim sembol path'lerini daha önce ayarlayarak uygulamanın pdb dosyasının bulunduğu yeri gösterdi iseniz zaten doğru konuma çoktan yönlendirilmiş olacaksınız.Bu işlemi yapmadığınızı varsayarak, bu bilgiye Parallel Stacks penceresinden nasıl ulaşabileceğinizi paylaşıyorum;

Uygulamamızın sembol dosyasının yüklenmesi

    Parallel Stacks penceresinde AnaEkran.Kaydet_click satırı seçili iken sağ tıklayarak gelen ekranda Load Symbols From->Symbol Path diyerek Visual Studio'nun sembol dosyalarını yüklemesini tetikleyebilirsiniz. Bu durumda önceden tanımlı sembol path'lerinde ve döküm dosyasının bulunduğu klasörde assembly'ye ait sembol dosyası (pdb) aranacak ve bulunması durumunda ise yüklenecektir. Sembol dosyası başarılı şekilde bulunup yüklendiğinde gri olan bu girdi siyaha dönüşecektir.

Sembol dosyasının bulunması için bakılan adreslerin bilgisi

    Sembollerin yüklenmesi sonrasında aynı menüden Symbol Load Information... seçeneği yardımıyla sembol dosyalarının nerelerde arandığını da görmeniz mümkündür. Sembollerin yüklenmesi sonrasında Parallel Stacks penceresinde AnaEkran.Kaydet_click satırına çift tıkladığımızda Visual Studio kaynak kod bilgisine erişerek bizi doğrudan hatanın olduğu dosya ve satıra götürecektir;

Sembollerin yüklenmesi sonrası kaynak kodumuzda hatanın olduğu satırın bulunması

    Sembol dosyaları içerisinde kaynak kodunun bulunmadığı önemle not düşmeliyim. Bu durumda Visual Studio'nun yaptığı sembol dosyasından dosya ve satır bilgisi alarak ilgili dosyayı belirtilen path'te bulup açmak ve ilgili satıra konumlanmaktır. Dolayısıyla da kaynak kodun bilgisayarınızda bulunmaması durumunda bu işlem başarılı olamayacaktır.

   Bu adımdan sonra artık hatanın kaynağına ulaşmış durumdayız :

musteri.Yas = Convert.ToInt32(musteriYas);

   Müstenini yaş bilgisi tam sayıya dönüştürülmeye çalışırken hata oluşmuş... Peki musteriYas değişkeni hangi değere sahip ki böyle bir hata alınmış?? Visual Studio ile bu bilgiye de ulaşmanız oldukça kolay, tek yapmanız gereken aşağıda görüldüğü şekilde locals penceresine geçip değişkenimizin değerini bulmak. Aynı normal hata ayıklama işleminde olduğu gibi kod içerisindeki değişken isimlerinin üzerine gelerek değerlerini öğrenmeniz de mümkün.

Yerel değişkenler Locals penceresinden takip edilebilir

     Normal hata ayıklama sırasında Visual Studio içerisinde yapıp döküm dosyalarını incelerken yapamayacağınız şeylerden en önemlisi fonksiyon çağrıları. Bu ne kadar kritik olabilir diyorsanız tckimlik.Text satırına gelerek text özelliğinin değerini görmeyi deneyin : "Evaluation of method System.Windows.Forms.TextBox.get_Text() calls into native method System.Windows.Forms.SafeNativeMethods.GetWindowTextLength(). Evaluation of native methods while minidump debugging is not supported." Hatırlarsanız .net framework özellikleri (property) derleme zamanında get ve set şeklindeki fonksiyonlara dönüştürmekte, dolayısıyla da bunların değerleri fonksiyon çağrısı yapılması nedeniyle, döküm incelenirken görülememekte.

Çalıştırılmak istenen kod parçacıkları için Immediate Window penceresi kullanılabilir

    Visual Studio'nun dökümleri incelerken sunduğu bir başka güzel özellik ise Immediate Window'un kullanılabiliyor olmasıdır.

    Gördüğünüz gibi Visual Studio'nun 2010 sürümü ile birlikte hata dökümlerinin incelenmesi noktasında oldukça önemli adımlar atılmış ve işimiz gerçekten kolaylaşmıştır. Maalesef ki bu özellik sadece .net framework 4.0 ile geliştirilmiş olan uygulamalar için kullanılabilir durumda. Tabi ki bu 4.0 öncesi uygulamalarınıza ait döküm dosyalarını inceleyemeyeceğiniz anlamına kesinlikle gelmiyor; sadece Visual Studio'nun bu güzelliklerinden mahrum olarak ve daha zor yöntemlerle ;)


Fatih Boy

http://www.enterprisecoding.com
http://twitter.com/fatihboy