Pilnvērtīgs atbalsts Jūsu sistēmai

Katra programma, ko veido cilvēks, jau piedzimst ar kļūdām :) Tāds ir cilvēks un viņa daba. Šoreiz stāsts par to, kā efektīvāk ķert šāda veida kļūdas, pēc tam, kad sistēma jau ir atdota klientam.

Noteikti kādreiz ikviens no mums ir redzējis savā monitorā šāda veida dialoga logu:

 

 

Šoreiz mēģināsim ātri pārskriet pāri galvenajiem aspektiem, lai pašu spēkiem varētu izveidot šāda veida feedback mehānismu pašu radītajām sistēmām.

Mēģināsim radīt kļūdas situāciju parastā Windows Forms aplikācijā:

 

private void button1_Click(object sender, EventArgs e)

{

    var bc = new BuggyClient();

    bc.BuggyMethod();

}

 

 

public class BuggyClient

{

    public void BuggyMethod()

    {

        var dbc = new DeeperBuggyClient();

        dbc.DeeperBuggyMethod();

    }

}

 

public class DeeperBuggyClient

{

    public void DeeperBuggyMethod()

    {

        var bc = new BuggyComponent();

        bc.TheBuggyMethod();

    }

}

 

public class BuggyComponent

{

    public void TheBuggyMethod()

    {

        var z = NullProperty;

        z.Substring(5);        // Null pointer exception should occur.

    }

 

    public string NullProperty

    {

        get

        {

            return null;

        }

    }

}

 

Tātad no ausgtāk redzamā koda ir saprotams, ka BuggyClient objekta BuggyMethod izsaukumam vajadzētu beigties ar null reference kļūdu.

Palaižot aplikāciju par to varam pārliecināties:

 

 

Tātad, lai pievienotu aplikācijai pilnvērtīgu feedback mehānismu vispirms ir nepeiciešams sagatavot sistēmu kļūdas situāciju apstrādei:

 

AppDomain.CurrentDomain.UnhandledException += ApplicationUnhandledException;

Application.ThreadException += ApplicationThreadException;

 

Ideālā gadījumā būtu jauki, ja kļūdu apstrādes metode būtu viena, bet delegāta signature atšķirības dēļ nākas izveidot divas metodes:

 

public static void ApplicationUnhandledException(object sender, UnhandledExceptionEventArgs e)

{

    if (e.ExceptionObject != null)

    {

        HandleException((Exception)e.ExceptionObject);

    }

}

 

private static void ApplicationThreadException(object sender, ThreadExceptionEventArgs e)

{

    if (e.Exception != null)

    {

        HandleException(e.Exception);

    }

}

 

HandleException metode tad arī ir abildīga par kļūdas apstrādi un kļūdas ziņojuma izveidi un nosūtīšanu izstrādātājiem.

 

private static void HandleException(Exception e)

{

    if (e == null)

    {

        return;

    }

 

    DumpWriter.Write(Process.GetCurrentProcess().Id, "c:\\dump.dmp");

 

    MessageBox.Show(

        "Unhandled exception occurred in your application. See application event log for more details.");

    Application.Exit();

}

 

Šoreiz nenodarbosimies ar tik garlaicīgām lietām, kā kļūdas ziņojuma nosūtīšanu izstrādātājiem, t.i., mums pašiem :), bet apskatīsim, ko dara DumpWriter klase.

 

public static class DumpWriter

{

    public static void Write(int processId, string outputFilePath)

    {

        using (FileStream stream = new FileStream(outputFilePath, FileMode.Create, FileAccess.ReadWrite))

        {

            using (Process proc = Process.GetProcessById(processId))

            {

                int dumpType = 0x00000306;

                MiniDumpWriteDump(proc.Handle, proc.Id, stream.Handle,

                                  dumpType, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

            }

        }

    }

 

    [DllImport("DbgHelp.dll", SetLastError = true)]

    private static extern bool MiniDumpWriteDump(

        IntPtr hProcess, int processId, IntPtr fileHandle,

        int dumpType, IntPtr excepInfo,

        IntPtr userInfo, IntPtr extInfo);

}

 

DumpWriter klase izmanto importēto metodi MiniDumpWriteDump no DbgHelp.dll bibliotēkas, kas atrodama "debugging tools for windows" pakotnē un ir brīvi distribūtējama kopā ar aplikāciju.

dumpType vērtība nosaka cik daudz un ko vēlamies iekļaut atmiņas izrakstā (vairāk info šeit).

Palaižot aplikāciju ar jaunajām izmaiņām iegūstam elegantu kļūdas paziņojuma ekrānu.

 

 

Uz servera C:\ diska saknes direktorijā ir izveidots "dump.dmp" fails, kas satur izstrādātājiem nepeiciešamo informāciju. Faila nogādāšanu līdz pareizajiem cilvēkiem šoreiz skiposim un uzreiz kāpsim izstrādātāja/testētāja čībiņās.

 

Pirmais, kas ir jāveic esot kā izstrādātājam, kas saņēmis ir Crash dump failu, ir ieladēt no WinDbg programmā.

 

 

Pēc sekmīgas crash dump faila ielādes esam gatavi jārlūkot mūsu situāciju, kas notika aplikācijā un kāpēc aplikācija pārstāja darboties.

Ielādējam Son Of Strike (SOS) moduli.

 

0:000> .loadby sos mscorwks

 

Ir iespējams aplūkot visus pavedienus (!threads), kas bija aktīvi kļūdas situācijas momentā:

 

0:000> !threads

ThreadCount: 2
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 0
Hosted Runtime: no
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
   0    1  d38 001611b0      6020 Enabled  013130f0:01313fe8 00158e88     0 STA System.NullReferenceException (012ff0dc)
   2    2  394 001644b8      b220 Enabled  00000000:00000000 00158e88     0 MTA (Finalizer)

 

Komanda !pe (print exception) dod iespēju aplūkot pēdējo kļūdu, kas notika pašreizējā pavedienā. Nepieciešams piefiksēt IP adresi metodei, kas izmeta kļūdu, jo tā noderēs mums vēlāk.

 

0:000> !pe

Exception object: 012ff0dc
Exception type: System.NullReferenceException
Message: Object reference not set to an instance of an object.
InnerException: <none>
StackTrace (generated):
    SP       IP       Function
    0012EFD4 00DD0648 VeryBuggyApp!VeryBuggyApp.BuggyComponent.TheBuggyMethod()+0x38
    0012EFE8 00DD05BC VeryBuggyApp!VeryBuggyApp.DeeperBuggyClient.DeeperBuggyMethod()+0x44
    0012EFFC 00DD0524 VeryBuggyApp!VeryBuggyApp.BuggyClient.BuggyMethod()+0x44
    0012F010 00DD048F VeryBuggyApp!VeryBuggyApp.Form1.button1_Click(System.Object, System.EventArgs)+0x47
    0012F02C 7B194170 System_Windows_Forms_ni!System.Windows.Forms.Control.OnClick(System.EventArgs)+0x70
    0012F044 7B18F55A System_Windows_Forms_ni!System.Windows.Forms.Button.OnClick(System.EventArgs)+0x4a
    0012F054 7B7341E9 System_Windows_Forms_ni!System.Windows.Forms.ButtonBase.OnKeyUp(System.Windows.Forms.KeyEventArgs)+0x95
    0012F064 7B6F5AB1 System_Windows_Forms_ni!System.Windows.Forms.Control.ProcessKeyEventArgs(System.Windows.Forms.Message ByRef)+0x401
    0012F0B4 7B6F5B55 System_Windows_Forms_ni!System.Windows.Forms.Control.ProcessKeyMessage(System.Windows.Forms.Message ByRef)+0x35
    0012F0C4 7B6F7241 System_Windows_Forms_ni!System.Windows.Forms.Control.WmKeyChar(System.Windows.Forms.Message ByRef)+0x15
    0012F0D4 7BA29ABE System_Windows_Forms_ni!System.Windows.Forms.Control.WndProc(System.Windows.Forms.Message ByRef)+0x86146e
    0012F12C 7B1C25D6 System_Windows_Forms_ni!System.Windows.Forms.ButtonBase.WndProc(System.Windows.Forms.Message ByRef)+0x66
    0012F170 7B1C2550 System_Windows_Forms_ni!System.Windows.Forms.Button.WndProc(System.Windows.Forms.Message ByRef)+0x20
    0012F17C 7B1C8640 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.OnMessage(System.Windows.Forms.Message ByRef)+0x10
    0012F184 7B1C85C1 System_Windows_Forms_ni!System.Windows.Forms.Control+ControlNativeWindow.WndProc(System.Windows.Forms.Message ByRef)+0x31
    0012F198 7B1C849A System_Windows_Forms_ni!System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr)+0x5a

StackTraceString: <none>
HResult: 80004003

 

!dumpstack komanda savukārt parāda visu izsaukumu kopu (kopā ar unmanaged metodēm) prety daudz stuffa, bet kopsummā ir jāmeklē "====> Exception cxr@" vērtība un jāpiefiksē ir tās atgriešanās adrese.

 

0:000> !dumpstack

OS Thread Id: 0xd38 (0)
Current frame: ntdll!KiFastSystemCallRet
ChildEBP RetAddr  Caller,Callee
0012da2c 59a8de20 dbghelp!MiniDumpWriteDump+0x2470, calling dbghelp!StackWalk64+0x60a6
0012dae8 7c911538 ntdll!wcsncpy+0xaa9, calling ntdll!wcsncpy+0x13d
...

0012e1f8 00dd0864 (MethodDesc 0x976570 +0xc4 VeryBuggyApp.DumpWriter.Write(Int32, System.String)), calling 0097c138
0012e218 00dd0864 (MethodDesc 0x976570 +0xc4 VeryBuggyApp.DumpWriter.Write(Int32, System.String)), calling 0097c138
0012e280 00dd0772 (MethodDesc 0x973048 +0x5a VeryBuggyApp.Program.HandleException(System.Exception)), calling (MethodDesc 0x976570 +0 VeryBuggyApp.DumpWriter.Write(Int32, System.String))
0012e298 00dd0701 (MethodDesc 0x97303c +0x59 VeryBuggyApp.Program.ApplicationThreadException(System.Object, System.Threading.ThreadExceptionEventArgs)), calling (MethodDesc 0x973048 +0 VeryBuggyApp.Program.HandleException(System.Exception))
0012e2b4 7b6eee1b (MethodDesc 0x7b08b790 +0x8b System.Windows.Forms.Application+ThreadContext.OnThreadException(System.Exception))
0012e2f0 7b6f77f6 (MethodDesc 0x7afea818 +0x16 System.Windows.Forms.Control.WndProcException(System.Exception)), calling 7b15af94
0012e2fc 7b6fa27c (MethodDesc 0x7b08889c +0xc System.Windows.Forms.Control+ControlNativeWindow.OnThreadException(System.Exception)), calling 7b1596b0
0012e300 7b1c84b2 (MethodDesc 0x7afebc50 +0x72 System.Windows.Forms.NativeWindow.Callback(IntPtr, Int32, IntPtr, IntPtr))
0012e31c 79e88826 mscorwks!DllUnregisterServerInternal+0xc80a, calling mscorwks!DllUnregisterServerInternal+0xc787
...

0012efd4 00dd0647 (MethodDesc 0x9764b4 +0x37 VeryBuggyApp.BuggyComponent.TheBuggyMethod()) ====> Exception cxr@12ed08
...

0012efe0 00dd05bc (MethodDesc 0x97644c +0x44 VeryBuggyApp.DeeperBuggyClient.DeeperBuggyMethod()), calling (MethodDesc 0x9764b4 +0 VeryBuggyApp.BuggyComponent.TheBuggyMethod())
0012eff4 00dd0524 (MethodDesc 0x9763e4 +0x44 VeryBuggyApp.BuggyClient.BuggyMethod()), calling (MethodDesc 0x97644c +0 VeryBuggyApp.DeeperBuggyClient.DeeperBuggyMethod())
0012f008 00dd048f (MethodDesc 0x975a10 +0x47 VeryBuggyApp.Form1.button1_Click(System.Object, System.EventArgs)), calling (MethodDesc 0x9763e4 +0 VeryBuggyApp.BuggyClient.BuggyMethod())
0012f020 7b194170 (MethodDesc 0x7afe9dd8 +0x70 System.Windows.Forms.Control.OnClick(System.EventArgs))
0012f03c 7b18f55a (MethodDesc 0x7b085f70 +0x4a System.Windows.Forms.Button.OnClick
...

0012fef0 79ef9473 mscorwks!ClrCreateManagedInstance+0x51e5, calling mscorwks+0x23b5
0012ff18 79ef009f mscorwks!CorExeMain+0x168, calling mscorwks!GetPrivateContextsPerfCounters+0xf5a0
0012ff68 79eeffcf mscorwks!CorExeMain+0x98, calling mscorwks!CorExeMain+0x11f
0012ffc0 7c816fd7 kernel32!RegisterWaitForInputIdle+0x49

 

Kad ir zināms slikās metodes adrese un return adrese kļūdas situācija, varam veikt unassemblēšanu (!u), lai noskaidrotu kļūdaino rindu, kas izsauc kļūdu:

 

0:000> !u 00DD0648

Normal JIT generated code
VeryBuggyApp.BuggyComponent.TheBuggyMethod()
Begin 00dd0610, size 44
00dd0610 55              push    ebp
00dd0611 8bec            mov     ebp,esp
00dd0613 83ec0c          sub     esp,0Ch
00dd0616 894dfc          mov     dword ptr [ebp-4],ecx
00dd0619 833d142e970000  cmp     dword ptr ds:[972E14h],0
00dd0620 7405            je      00dd0627
00dd0622 e85a9e2f79      call    mscorwks!CorLaunchApplication+0xec6b (7a0ca481) (JitHelp: CORINFO_HELP_DBG_IS_JUST_MY_CODE)
00dd0627 33d2            xor     edx,edx
00dd0629 8955f8          mov     dword ptr [ebp-8],edx
00dd062c 90              nop
00dd062d 8b4dfc          mov     ecx,dword ptr [ebp-4]
00dd0630 ff15c8649700    call    dword ptr ds:[9764C8h] (VeryBuggyApp.BuggyComponent.get_NullProperty(), mdToken: 06000013)
00dd0636 8945f4          mov     dword ptr [ebp-0Ch],eax
00dd0639 8b45f4          mov     eax,dword ptr [ebp-0Ch]
00dd063c 8945f8          mov     dword ptr [ebp-8],eax
00dd063f 8b4df8          mov     ecx,dword ptr [ebp-8]
00dd0642 ba05000000      mov     edx,5
00dd0647 3909            cmp     dword ptr [ecx],ecx
00dd0649 e8529f4f78      call    mscorlib_ni+0x20a5a0 (792ca5a0) (System.String.Substring(Int32), mdToken: 0600015a)
00dd064e 90              nop
00dd064f 90              nop
00dd0650 8be5            mov     esp,ebp
00dd0652 5d              pop     ebp
00dd0653 c3              ret

 

Un esam atraduši, ka vainīgais kods ir string apakškopas iegūšana, jo strings, ar kuru operē aplikācija nav inicializēts (null).

 

 

Tātad izmantojot DbgHelp.dll bibliotēku ir iespējams izveidot crash memory dump failus un vēlāk ar Windbg rīka palīdzību jau offline no aplikācijas ir iespējams izanalizēt radušos situāciju un labot izejas kodu pēc nepieciešamības.

 

 

Cerams, ka noderēs!

Published Friday, January 23, 2009 8:36 AM by valdis.iljuconoks

Comments

# re: Pilnvērtīgs atbalsts Jūsu sistēmai

Wow, your post makes mine look feelbe. More power to you!

Thursday, June 02, 2011 3:23 PM by Jennis

# re: Pilnvērtīgs atbalsts Jūsu sistēmai

Such a deep asnewr! GD&RVVF

Friday, June 03, 2011 5:41 PM by Cinderella

# Develops the theme further.

Hooray!, The one who wrote nishtyak wrote!

Thursday, June 23, 2011 11:13 PM by sexy

# Interesting, thanks

Wonderful publish. I'm struggling with several these troubles.

Wednesday, November 16, 2011 11:52 PM by Corey Spine

# xYBSkkwejxTfi

TNOydJ Awesome article.Thanks Again. Cool.

Friday, March 23, 2012 11:48 PM by google+ circles

Leave a Comment

(obligāts) 
(obligāts) 
(brīvizvēles)
(obligāts)