C# ile Bir İşlemin Mini Dökümünü Almak

Tools

   Günümüz uygulamalarının müşteri beklentileri doğrultusunda giderek karmaşıklaşması, kimi zaman uygulama modüllerine birden çok yazılım geliştiricinin elinin değmesi –özellikle de belirli bir metodolojiyi/standardı takip etmeyen yazılım evlerinde– ortaya tam bir spagetti kod çıkartabilmekte. Böylesi karışık uygulamalarda müşteri ayağında oluşabilen hataların ise debug edilmesi başlı başına bir problem haline gelmekte. Önceki makalemde sizlerle Log4Net kullanarak bu hata ayıklama sürecini nasıl daha az sancılı hale getirebileceğinizi paylaşmıştım; fakat alınan bazı hatalarda maalesef ki, o ya da bu sebeple, tutulan günlüklerdeki bilgiler bile hatanın kaynağını tespit etmekte yetersiz kalabilmekte. İşte tam da bu noktada yazılım geliştiricilerin en büyük arzusu müşteride oluşan hata durumunun aynısını geliştirme/test ortamına taşıyarak detaylı bir debug yapabilmek. Bunun nasıl mümkün olabileceğini sizlerle paylaşmak istediğim yazılarımdan bu ilkinde, sizlerle öncelikli olarak müşteride oluşan hatalı durumun bir kopyasını nasıl alabileceğinizi paylaşıyor olacağım.

    Müşterideki hatalı durumu bire bir geliştirme ya da test ortamına taşımının yolu uygulamanın o sıradaki anlık verilerini, hafızada kapladığı alanı kopyalayabilmekten geçmekte. Bu noktada da yazılım geliştiricilerin en büyük yardımcısı uygulama döküm (dump) dosyalarıdır. Uygulamanın hafızada bulunan verilerinin, çağırım hiyerarşisinin, uygulama threadlerinin o onda bulunduğu durumun v.s. bir kopyasının bulunduğu dump dosyaları tam kullanıcı dökümü (full user dump) ve mini döküm (mini dump) olmak üzere iki gruba ayrılabilir. Her iki grupta da uygulamanın o sırada hafızada yer alan değerleri bulunmakta ve çeşitli araçlar yardımıyla bu veriler içerisinde gezinerek hatanın sebebi bulunabilmekte. İki döküm arasındaki temel fark ise tam kullanıcı dökümünde sadece uygulamanın hafızada kapladığı alan değil, kullanıcının kullandığı tüm hafıza alanının dökümünün alınıyor olmasıdır. Özellikle hafıza kullanımının yazılım geliştiriciye teslim edildiği native kodlar ile geliştirilmiş olan uygulamalarda verilerin zaman zaman uygulamanın kapladığı alan dışında da bulunabiliyor olması ya da farklı uygulamalarla etkileşim içinde olması nedeniyle ihtiyaç duyulan tam kullanıcı dökümleri doğaları gereği sistemin kullandığı hafızayla doğru orantılı olarak hem fazla alan kaplamakta, hem de ilgilendiğimiz nokta dışında da çok fazla gereksiz veri barındırabilmektedir. Bu sebeple hata incelemesi için ilk tercih olarak mini dökümler seçilmekte ve pek çok durumda da hatanın kaynağına ulaşmakta yeterli olmaktadır.

    Mini dökümler ile tam kullanıcı dökümleri arasındaki seçimde önemli olacak bir başka nokta ise; tam kullanıcı dökümleri için Dr. Watson, Userdump, ADPlus gibi Microsoft tarafından sunulan uygulamalar dışında biz yazılım geliştiricilere açılmış bir API’si bulunmamasına karşın, mini dökümler için sağlanan API’ler bulunmakta. Dolayısıyla da mini dökümlerin programsal olarak alınabilmesi mümkün olmakta. Yazımın devamında bu noktaya odaklanarak bizlere sunulan API’leri kullanarak bir C# uygulaması içerisinden nasıl mini döküm alınabileceğinizi anlatıyor olacağım.

Mini Dump uygulaması ana ekranı

   Yukarıda bir ekran görüntüsünü bulabileceğiniz ve sizlerle paylaşacağım uygulamam, hafızada bulunan süreçleri/yansımaları (process) bizlere listeleyerek hangisinin mini dökümünün alınması istendiğini sormakta. Kullanıcının seçimi doğrultusunda Windows’un API’lerini de kullanarak bir mini döküm dosyası oluşturmakta. Yazımın sonlarında uygulamanın kodlarını kullanarak kendi uygulamanıza nasıl adapte edeceğinizde ve hata durumlarında bu işlemin otomatik olarak nasıl yapılabileceğinden de bahsediyor olacağım.

   Programsal olarak mini döküm almak istiyorsanız yukarıda bahsettiğim üzere Windows API’leri ile muhatap olacaksınız, dolayısıyla öncelikli olarak C# içerisinde bu API’leri nasıl kullanabileceğimizi görelim. Managed kod üzerinde geliştirilen C# uygulamaları, bizim senaryomuzda da olduğu gibi, zaman zaman unmanaged kütüphanelerle etkileşime geçmek zorunda kalabilmekte. Bu gibi senaryolarda managed kod üzerinden unmanaged uygulamaları kullanabilmek için C# içerisinde extern anahtar kelimesi  ve System.Runtime.InteropServices namespace’i altında DllImport özniteliği bulunmaktadır. DllImport özniteliği kullanılırken öncelikle kullanacağınız harici fonksiyonu imzasını (dönüş türünü, adını ve parametrelerini) belirtmelisiniz, ardından da bu imzanın üzerinden kullanacağınız DllImport özniteliği ile verdiğiniz bu fonksiyonun hangi unmanaged dll içerisinde ve ne isimle bulunabileceğini belirtmelisiniz. Bu şekilde hazırlanarak derlenen bir kod, derleyici tarafından yorumlanarak ilgili yerde doğru dll’in ve fonksiyonun bizim adımıza çağırılması sağlanmaktadır. Aşağıda örneklediğim kullanımda C++ ile yazılmış olan Ornek.dll içerisinde yer alan ornekFonksiyon’unu C# kodunuz içerisinde nasıl kullanabileceğinizi görebilirsiniz;

[DllImport("Ornek.dll", EntryPoint="ornekFonksiyon")]
static extern int ornekFonksiyon(int parametre1,int parametre2, ...);

    Kodumuz içerisinde yukarıdaki şekilde atıfta bulunduğumuz fonksiyonun hangi dll içerisindeki hangi isimle yer aldığını belirterek artık kullanıma hazır hale getirmiş oluyoruz. Bu noktadan sonra kodumuz içerisinde aynı C# ile yazılmış standart bir static fonksiyon kullanır gibi kullanabiliriz. Bu konuda düşülmesi gereken önemli bir not; C/C++ gibi dillerle yazılmış unmanaged dll içerisindeki tüm fonksiyonlar bu şekilde kullanılamaz. Böylesi bir kullanım için öncelikle ilgili kütüphane geliştiricisinin kodu içerisinde böylesi bir kullanıma izin vermiş olması gerekli. Ornek.dll üzerinden örneğimize devam edecek olursak, ornekFonksiyon’un C# içerisinden kullanılabilir olması için C/C++ kodunun aşağıdaki şekilde yazılmış olması gerekmekte;

C++ Header dosyası;

extern "C" {
    __declspec(dllexport) void __cdecl ornekFonksiyon (int parametre1, int parametre2, ...);
}

C++ dosyası;

extern void __cdecl ornekFonksiyon (int parametre1, int parametre2, ...) {
    // İş mantığı kodu
}

   Görüldüğü gibi C# içerisinden unmanaged dll’ler içerisindeki fonksiyonları çalıştırmak oldukça kolay. Sanırım DllImport özniteliği ve extern anahtar kelimesi hakkında verdiğim bu kadar bilgi yazımın geri kalanında kullanacağım kodun anlaşılması için yeterli olacaktır. DllImport özniteliğinin kullanımı hakkında daha detaylı bilgi MSDN sitesinde bulunabilir.

    Mini döküm almakta kullanacağımız fonksiyon dbghelp.dll içerisinde yer alan MiniDumpWriteDump‘dır. Bu fonksiyon aşağıdaki şekilde bir imzaya sahiptir;

BOOL WINAPI MiniDumpWriteDump(
  __in  HANDLE hProcess,
  __in  DWORD ProcessId,
  __in  HANDLE hFile,
  __in  MINIDUMP_TYPE DumpType,
  __in  PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  __in  PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  __in  PMINIDUMP_CALLBACK_INFORMATION CallbackParam
);

   http://msdn.microsoft.com/en-us/library/ms680360%28v=vs.85%29.aspx adresinde bulunan dokümantasyondan parametreleri hakkında fikir sahibi olabileceğiniz bu fonksiyonun bizim örneğimizde seçilecek olan işlemin bilgilerinin verildiği hProcess ve ProcessId parametreleri, dökümün yazılacağı dosyanın belirtildiği hFile parametresi ve döküm türünün belirtileceği DumpType parametresi kullanılacaktır.

[DllImport("dbghelp.dll",
    EntryPoint = "MiniDumpWriteDump",
    CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Unicode,
    ExactSpelling = true,
    SetLastError = true)]
static extern bool MiniDumpWriteDump(IntPtr hProcess,
    uint processId,
    SafeHandle hFile,
    uint dumpType,
    IntPtr expParam,
    IntPtr userStreamParam,
    IntPtr callbackParam);

    Kodumuz içerisinde  MiniDumpWriteDump fonksiyonu tanımlaması yaparak aslında işin çok önemli bir bölümünü tamamlamış oluyoruz 🙂 bundan sonraki kısımda geriye kullanıcıya işlemlerin/yansımaların listelenerek, seçilen işlem için bu fonksiyonun çağırılması kalıyor. Aşağıdaki basit kod ile işlemleri getirebilir ve islemListesi adındaki DataGridView içerisinde listeleyebiliriz;

islemListesi.DataSource = Process.GetProcesses();

   Ekrana yerleştireceğimiz bir buton ile kullanıcının işlemi seçtikten sonra mini döküm oluşturması işini tetikleyebiliriz;

private void dumpAl_Click(object sender, EventArgs e) {
    var seciliIslem = (Process)islemListesi.SelectedRows[0].DataBoundItem;
    var onerilenDosyaAdi = seciliIslem.ProcessName + ".mdmp"; 

    dosyaSaklamaDiyalogu.FileName = onerilenDosyaAdi;
    if (dosyaSaklamaDiyalogu.ShowDialog()== DialogResult.OK) {
        using (var fs = new FileStream(dosyaSaklamaDiyalogu.FileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Write)) {
            MiniDumpWriteDump(seciliIslem.Handle,
                    (uint)seciliIslem.Id,
                    fs.SafeFileHandle,
                    0x00000002, //MiniDumpWithFullMemory
                    IntPtr.Zero,
                    IntPtr.Zero,
                    IntPtr.Zero);
        }

        MessageBox.Show("Mini Dump başarıyla alındı");
    }
}

    Ekrandan yer alan dumpAl adındaki butonun click olayına bağlanmış olan  dumpAl_Click fonksiyonu içerisinde islemListesi’nde seçili olan işlem bulunarak bu işlem adıyla bir dosya saklanması üzere kullanıcıya soruluyor, kullanıcı isterse dosya adını değiştirebilmekte. Ardından girilen isim ve klasörde bir FileStream oluşturularak bu stream’in bilgisi, işlem bilgileri ile birlikte MiniDumpWriteDump fonksiyonuna verilmekte. Bu noktadan sonra API içerisinde gerekli işlemler yapılarak işlemin hizmet dökümü belirtilen dosya içerisine yazılacaktır.

   Örneğimizde dikkat edecek olursanız mini dökümümüzü  tam hafıza dökümü, MiniDumpWithFullMemory, ile almaktayız. Döküm dosyası içerisine kaydedilecek olan verinin kapsamını belirleyen ve MSDN sitesinde daha detaylı bilgi bulabileceğiniz bu parametrede aşağıdaki değerler kullanılabilmektedir (C++ notasyonu);

typedef enum _MINIDUMP_TYPE {
  MiniDumpNormal                           = 0x00000000,
  MiniDumpWithDataSegs                     = 0x00000001,
  MiniDumpWithFullMemory                   = 0x00000002,
  MiniDumpWithHandleData                   = 0x00000004,
  MiniDumpFilterMemory                     = 0x00000008,
  MiniDumpScanMemory                       = 0x00000010,
  MiniDumpWithUnloadedModules              = 0x00000020,
  MiniDumpWithIndirectlyReferencedMemory   = 0x00000040,
  MiniDumpFilterModulePaths                = 0x00000080,
  MiniDumpWithProcessThreadData            = 0x00000100,
  MiniDumpWithPrivateReadWriteMemory       = 0x00000200,
  MiniDumpWithoutOptionalData              = 0x00000400,
  MiniDumpWithFullMemoryInfo               = 0x00000800,
  MiniDumpWithThreadInfo                   = 0x00001000,
  MiniDumpWithCodeSegs                     = 0x00002000,
  MiniDumpWithoutAuxiliaryState            = 0x00004000,
  MiniDumpWithFullAuxiliaryState           = 0x00008000,
  MiniDumpWithPrivateWriteCopyMemory       = 0x00010000,
  MiniDumpIgnoreInaccessibleMemory         = 0x00020000,
  MiniDumpWithTokenInformation             = 0x00040000
} MINIDUMP_TYPE;

   Örneğimizde verilen tam hafıza dökümü değeri (0x00000002), işlemin eriştiği tüm hafıza bilgisini döküm dosyasına ekleyeceği için dosyanın büyüklüğünü arttıracaktır. İş mantığınıza uygun olarak  yukarıda sıraladığım değerlerden bir başkasının kullanılması da mümkündür.

   Şimdiye kadar sizlerle paylaştığım bilgileri kullanarak rahatlıkla aktif işlemlerden istediğinizin dökümünü alarak bir dosyaya kaydetmeniz mümkün. İsterseniz bu bilgileri kullanarak gerçek hayatta müşterilerimize sunduğumuz uygulamalarımıza nasıl entegre ederek daha yaralı hale getirebileceğimizi görelim. Aşağıdaki örnek kodumuzda harici bir uygulamanın mini dökümünü almak yerine çalışan uygulamamızın bir hata ile karşılaşması durumunda kendi hata dökümünü almasını sağlıyoruz;

[STAThread]
static void Main() {
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

    //uygulama normal şekilde başlatılır...
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new AnaEkran());
}

// ...
//diğer iş mantığı kodları
// ...

private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) {
   //Kullanıcı uygulamada bir hata olduğu konusunda
   //hata mesajı ile birlikte bilgilendirilir.

   //Hata dökümünün alınıp alınmaması sorulur

   var aktifIslem = Process.GetCurrentProcess();

   var onerilenDosyaAdi = aktifIslem.ProcessName + ".mdmp";
   if (dosyaSaklamaDiyalogu.ShowDialog() == DialogResult.OK) {
        using (var fs = new FileStream(dosyaSaklamaDiyalogu.FileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Write)) {
            MiniDumpWriteDump(aktifIslem.Handle,
                    (uint)aktifIslem.Id,
                    fs.SafeFileHandle,
                    0x00000002, //MiniDumpWithFullMemory
                    IntPtr.Zero,
                    IntPtr.Zero,
                    IntPtr.Zero);
        }

        //bu nokyada oluşan hata dökümü istenirse bir
        //web servise gönderilebilir...

        MessageBox.Show("Hata bilgisi başarıyla saklandı");
    }
}

   Not; bu örneğin debug modunda derleyerek çalıştırırsanız CurrentDomain_UnhandledException fonksiyonu içerisine düşmediğini göreceksiniz. Bunun nedeni hata alınması durumunda varsayılan olarak hatanın .Net Framework tarafından yakalanarak "Unhandled Exception" diyalogunda hata bilgisinin verilmesidir. Bu diyalogun çıkmasını engelleyerek işleyişin fonksiyonumuz içerisinde devam etmesini istiyorsanız, uygulamanızın başına aşağıdaki kodu koyarak hataların yakalanarak bu diyalog içerisinde gösterilmesini engelleyebilirsiniz;

Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);

   Bir ikinci not; örnek uygulamamız UnhandledException‘ı dinlemesi nedeniyle sadece arayüz threadleri dışında oluşan thread’lerdeki hatalarda hata dökümü alacaktır. Arayüz thread’lerinde oluşan hatalarda da hata dökümü alınması için benzer bir işlemin ThreadException olayı dinlenerek de yapılıyor olması gerekli.

    Programsal olarak mini döküm dosyası oluşturulmasını başardıktan sonra işlerimiz oldukça kolaylaşacaktır. Müşteride oluşan hatalarda otomatik hizmet dökümü alarak bu dosyanın gönderilmesi talep edebilir, üzerinden analiz yapabilirsiniz. Bir adım öteye giderek alınan hizmet dökümlerini bir web servisi üzerinden firmanız sunucularına aktarabilir, hatta ilgili yazılım geliştiriciye konu hakkında bilgilendirici bir mail gönderebilirsiniz. Firmanızda Team Foundation  Server kullanıyorsanız, süreci iyice otomatikleştirerek mail göndermek yerine geliştirme ekibine dosyayı da iliştirerek bir task atayabilirsiniz. Böylelikle hata yakalamakla kalmayıp müşterinin tek tuşu basmasıyla bilgiyi firmanızdaki iş süreçlerine dahil edebilirsiniz.

    Makalemle ufkunuzu açmada bir nebze olsun katkıda bulunabildiğimi umuyorum. Eğer bu makalemde paylaştığım bilgiler hoşunuza gittiyse serinin bir sonraki makalesini kesinlikle kaçırmayın derim 😉 Sonraki makalemde sizlerle Visual Studio 2010 ile birlikte gelen ve hata döküm dosyalarını analiz ederek hatalarınızı bulmanızı sağlayan yeni özelliklerden bahsediyor olacağım.

   Aşağıda linkini bulabileceğiniz örnek solution’da işlem dökümünü alabileceğiniz kodların bulunduğu İslemDokumu projesi ile hatay açık şekilde kodlanmış CRMUygulamasi projesini bulabilirsiniz. CRM uygulaması başlatıldığında herhangi bir değer girilmeden kaydet butonuna basıldığında hata oluşacaktır, bu uygulama devam eden makalelerde hataların debug edilmesi sırasında örnek olarak kullanılacaktır.

alt

Hata Ayıklama Örnek Uygulaması

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# konusundan Microsoft tarafından dört yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz.
vCard - Twitter - Facebook - Google+

1 Yorum

  1. Pingback: Tweets that mention C# ile Bir İşlemin Mini Dökümünü Almak | Fatih'in Notları -- Topsy.com

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir