Zine.net online

Witaj na Zine.net online Zaloguj się | Rejestracja | Pomoc
w Szukaj

mgrzeg.net - Admin on Rails :)

Wyjątki first i second chance

Nierzadko jest tak, że aplikacja zachowuje się nieprawidłowo, jednak wszystkie wyjątki są przechwycone i nie ma żadnej informacji (czy to wizualnej, czy tez w logach) o tym, że wydarzyło się coś niepokojącego, program po prostu dalej działa. Klasyczny przykład, to ‘połykanie’ wyjątków, czyli konstrukcja typu:

try {...KOD...}
catch(Exception) { }

czyli blok obsługi wyjątku jest puściutki :)
My jednak wyczuwamy jakąś zmyłkę i chcemy dowiedzieć się, co też poszło nie tak. Systemowy mechanizm obsługi wyjątków może nam w tym pomóc.

W momencie, gdy system wykrywa sytuację wyjątkową, wyjątek przechodzi następującą ścieżkę:

  1. Jeśli jest aktywny debugger, system powiadamia go o wyjątku. Jest to tzw. ‘first chance exception’ - debugger otrzymuje pierwszą szansę na zrobienie czegoś z wyjątkiem.
  2. Jeśli nie ma aktywnego debuggera, lub podłączony do aplikacji nie skorzystał z ‘pierwszej szansy’, system powiadamia aplikację. W tym celu system szuka procedury obsługi przeszukując ramki stosu wątku, w którym pojawił się wyjątek. Adresy procedur obsługi wyjątków znajdują się w ramach struktury TEB (thread environment block), a dokładniej w ramach jednokierunkowej kolejki elementów typu EXCEPTION_RECORD.
  3. Jeśli aplikacja nie przechwyciła wyjątku (nie została znaleziona procedura obsługi), system powiadamia po raz drugi aktywny debugger (o ile jest). Jest to tzw. ‘second chance exception’.
  4. Jeśli nie ma aktywnego debuggera, lub podłączony nic nie zrobił z wyjątkiem ‘second chance’, to system przechodzi do standardowej obsługi wyjątku, co zazwyczaj sprowadza się do uruchomienia mechanizmów WER.

Aby zatem otrzymać informację o ‘połkniętym’ wyjątku, musimy zasadzić się z debuggerem na ‘first chance exception’, bowiem na drugą szansę nie możemy już liczyć.

Weźmy dla przykładu kod:

//first_and_second.cs
using System;

namespace pl.net.zine.Articles
{
  class FistSecondChance
  {
    public static void Main(string[] args)
    {
      Console.WriteLine("First & second");
      Console.Read();
      throw new Exception("First & second");
    }
  }
}

oraz

//first_only.cs
using System;

namespace pl.net.zine.Articles
{
  class FistSecondChance
  {
    public static void Main(string[] args)
    {
      Console.WriteLine("First only");
      Console.Read();
      try { throw new Exception("First only - catch!"); }
      catch (Exception) { }
    }
  }
}

Zacznijmy od pierwszego przypadku i skorzystajmy z procdumpa Sysinternals do wygenerowania zrzutu. Aplikacja nawet nie stara się przechwycić wyjątku, więc mamy możliwość zrobienia dwóch zrzutów: przy pierwszym i drugim powiadomieniu nas przez system.

Do zrobienia zrzutu ‘first’ (1.a) skorzystamy z następującego polecenia:

>procdump  -e 1 -ma Articles.exe FirstAndSecond_First.dmp

przy drugim (1.b):

>procdump  -e -ma Articles.exe FirstAndSecond_Second.dmp

W drugim przypadku mamy możliwość zrobienia tylko jednego zrzutu (2):

>procdump  -e 1 -ma Articles.exe FirstOnly.dmp

Mając tak wygenerowane dumpy możemy sprawdzić, co też da się z nich wyczytać.

ad 1.a Sprawdźmy wyjątki - informacja jest, nie ma stacktrace. Bez problemu jednak docieramy do miejsca wypadku korzystając z !mk

0:000> !pe
Exception object: 0241d2ac
Exception type:   System.Exception
Message:          First & second
InnerException:   <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131500
0:000> !mk
Thread 0:
     ESP      EIP
00:U 0038ee64 766bb9bc KERNELBASE!RaiseException+0x58
01:U 0038eebc 5150bd60 clr!RaiseTheExceptionInternalOnly+0x276
02:U 0038ef4c 5150bf99 clr!IL_Throw+0x14c
03:M 0038f00c 002800d3 *** WARNING: Unable to verify checksum for Articles.exe
pl.net.zine.Articles.FistSecondChance.Main(System.String[])(+0x0 IL)(+0x63 Native) [C:\...\FistSecondChance.cs @ 8,5]
04:U 0038f01c 513c21bb clr!CallDescrWorker+0x33
05:U 0038f02c 513e4be2 clr!CallDescrWorkerWithHandler+0x8e
06:U 0038f0a8 513e4d84 clr!MethodDesc::CallDescr+0x194
07:U 0038f1e0 513e4db9 clr!MethodDesc::CallTargetWorker+0x21
08:U 0038f1fc 513e4dd9 clr!MethodDescCallSite::Call+0x1c
09:U 0038f214 515173c2 clr!ClassLoader::RunMain+0x24c
0a:U 0038f378 515174d0 clr!Assembly::ExecuteMainMethod+0xc1
0b:U 0038f5e0 515172e4 clr!SystemDomain::ExecuteMainMethod+0x4ec
0c:U 0038fac4 515176d9 clr!ExecuteEXE+0x58
0d:U 0038fb18 515175da clr!_CorExeMainInternal+0x19f
0e:U 0038fb64 51494a98 clr!_CorExeMain+0x4e
0f:U 0038fb9c 659355ab mscoreei!_CorExeMain+0x38
10:U 0038fba8 659a7f16 mscoree!ShellShim__CorExeMain+0x99
11:U 0038fbb8 659a4de3 mscoree!_CorExeMain_Exported+0x8
12:U 0038fbc0 7688339a kernel32!BaseThreadInitThunk+0xe
13:U 0038fbcc 77719ef2 ntdll!__RtlUserThreadStart+0x70
14:U 0038fc0c 77719ec5 ntdll!_RtlUserThreadStart+0x1b

ad 1.b. Tu jest wszystko - komunikat wyjątku + stacktrace.

0:000> !pe
Exception object: 024dd2ac
Exception type:   System.Exception
Message:          First & second
InnerException:   <none>
StackTrace (generated):
    SP       IP       Function
    001DF1DC 003300D3 Articles!pl.net.zine.Articles.FistSecondChance.Main(System.String[])+0x63

StackTraceString: <none>
HResult: 80131500
0:000> !mk
Thread 0:
     ESP      EIP
00:U 001deb6c 777015de ntdll!NtRaiseException+0x12
01:U 001deb74 776f014d ntdll!KiUserExceptionDispatcher+0x29
02:U 001deb90 766bb9bc KERNELBASE!RaiseException+0x58
03:U 001df08c 5150bd60 clr!RaiseTheExceptionInternalOnly+0x276
04:U 001df11c 5150bf99 clr!IL_Throw+0x14c
05:M 001df1dc 003300d3 *** WARNING: Unable to verify checksum for Articles.exe
pl.net.zine.Articles.FistSecondChance.Main(System.String[])(+0x0 IL)(+0x63 Native) [C:\...\FistSecondChance.cs @ 8,5]
06:U 001df1ec 513c21bb clr!CallDescrWorker+0x33
07:U 001df1fc 513e4be2 clr!CallDescrWorkerWithHandler+0x8e
08:U 001df278 513e4d84 clr!MethodDesc::CallDescr+0x194
09:U 001df3b8 513e4db9 clr!MethodDesc::CallTargetWorker+0x21
0a:U 001df3d4 513e4dd9 clr!MethodDescCallSite::Call+0x1c
0b:U 001df3ec 515173c2 clr!ClassLoader::RunMain+0x24c
0c:U 001df550 515174d0 clr!Assembly::ExecuteMainMethod+0xc1
0d:U 001df7b8 515172e4 clr!SystemDomain::ExecuteMainMethod+0x4ec
0e:U 001dfc9c 515176d9 clr!ExecuteEXE+0x58
0f:U 001dfcf0 515175da clr!_CorExeMainInternal+0x19f
10:U 001dfd3c 51494a98 clr!_CorExeMain+0x4e
11:U 001dfd74 659355ab mscoreei!_CorExeMain+0x38
12:U 001dfd80 659a7f16 mscoree!ShellShim__CorExeMain+0x99
13:U 001dfd90 659a4de3 mscoree!_CorExeMain_Exported+0x8
14:U 001dfd98 7688339a kernel32!BaseThreadInitThunk+0xe
15:U 001dfda4 77719ef2 ntdll!__RtlUserThreadStart+0x70
16:U 001dfde4 77719ec5 ntdll!_RtlUserThreadStart+0x1b

ad 2. Podobnie do 1.a, nie mamy żadnego stacktrace, ale mamy sam wyjątek + komunikat. Do tego !mk umiejscawia nas w odpowiednim miejscu i bez problemu docieramy do źródła wyjątku.

0:000> !pe
Exception object: 0246d29c
Exception type:   System.Exception
Message:          First only - catch!
InnerException:   <none>
StackTrace (generated):
<none>
StackTraceString: <none>
HResult: 80131500
0:000> !mk
Thread 0:
     ESP      EIP
00:U 001eeae4 766bb9bc KERNELBASE!RaiseException+0x58
01:U 001eeb3c 5150bd60 clr!RaiseTheExceptionInternalOnly+0x276
02:U 001eebcc 5150bf99 clr!IL_Throw+0x14c
03:M 001eec8c 004300d1 *** WARNING: Unable to verify checksum for Articles.exe
*** ERROR: Module load completed but symbols could not be loaded for Articles.exe
pl.net.zine.Articles.FistSecondChance.Main(System.String[])(+0x1e IL)(+0x61 Native)
04:U 001eecbc 513c21bb clr!CallDescrWorker+0x33
05:U 001eeccc 513e4be2 clr!CallDescrWorkerWithHandler+0x8e
06:U 001eed48 513e4d84 clr!MethodDesc::CallDescr+0x194
07:U 001eee80 513e4db9 clr!MethodDesc::CallTargetWorker+0x21
08:U 001eee9c 513e4dd9 clr!MethodDescCallSite::Call+0x1c
09:U 001eeeb4 515173c2 clr!ClassLoader::RunMain+0x24c
0a:U 001ef018 515174d0 clr!Assembly::ExecuteMainMethod+0xc1
0b:U 001ef280 515172e4 clr!SystemDomain::ExecuteMainMethod+0x4ec
0c:U 001ef764 515176d9 clr!ExecuteEXE+0x58
0d:U 001ef7b8 515175da clr!_CorExeMainInternal+0x19f
0e:U 001ef804 51494a98 clr!_CorExeMain+0x4e
0f:U 001ef83c 659355ab mscoreei!_CorExeMain+0x38
10:U 001ef848 659a7f16 mscoree!ShellShim__CorExeMain+0x99
11:U 001ef858 659a4de3 mscoree!_CorExeMain_Exported+0x8
12:U 001ef860 7688339a kernel32!BaseThreadInitThunk+0xe
13:U 001ef86c 77719ef2 ntdll!__RtlUserThreadStart+0x70
14:U 001ef8ac 77719ec5 ntdll!_RtlUserThreadStart+0x1b
0:000> !mframe 03
0:000> !muf
pl.net.zine.Articles.FistSecondChance.Main(string[]): void
Source information not available.
        00430070 55              push    ebp
        00430071 8bec            mov     ebp,esp
        00430073 57              push    edi
        00430074 56              push    esi
        00430075 53              push    ebx
        00430076 83ec1c          sub     esp,1Ch
        00430079 33c0            xor     eax,eax
        0043007b 8945e8          mov     dword ptr [ebp-18h],eax
        0043007e 894ddc          mov     dword ptr [ebp-24h],ecx
        00430081 833d3c31280000  cmp     dword ptr ds:[28313Ch],0
        00430088 7405            je      0043008f
        0043008a e804602451      call    clr!JIT_DbgIsJustMyCode (51676093)
    IL_0000: nop
        0043008f 90              nop
    IL_0001: ldstr "First only"
    IL_0006: call System.Console::WriteLine
        00430090 8b0d30204603    mov     ecx,dword ptr ds:[3462030h]
        00430096 e811700d79      call    mscorlib_ni+0x2570ac (795070ac)  [System.Console.WriteLine(System.String), MD=79338650]
    IL_000b: nop
        0043009b 90              nop
    IL_000c: call System.Console::Read
        0043009c e867ab6a79      call    mscorlib_ni+0x82ac08 (79adac08)  [System.Console.Read(), MD=7933858c]
    IL_0011: pop
        004300a1 90              nop
.try {
    IL_0012: nop
        004300a2 90              nop
    IL_0013: ldstr "First only - catch!"
    IL_0018: newobj System.Exception::.ctor
        004300a3 b98cfb5c79      mov     ecx,offset mscorlib_ni+0x31fb8c (795cfb8c)
        004300a8 e8731fe4ff      call    00272020
        004300ad 8945d8          mov     dword ptr [ebp-28h],eax
        004300b0 ba9c2e2800      mov     edx,282E9Ch
        004300b5 b917000070      mov     ecx,70000017h
        004300ba e8b2f40d51      call    clr!JIT_StrCns (5150f571)
        004300bf 8bd0            mov     edx,eax
        004300c1 8b4dd8          mov     ecx,dword ptr [ebp-28h]
        004300c4 e88b136479      call    mscorlib_ni+0x7c1454 (79a71454)  [System.Exception..ctor(System.String), MD=7932c4f4]
    IL_001d: throw
        004300c9 8b4dd8          mov     ecx,dword ptr [ebp-28h]
        004300cc e8e5bd0d51      call    clr!IL_Throw (5150beb6)
} // END TRY (IL_0012)
.catch {
    IL_001e: pop
>>>>>>>>004300d1 90              nop
    IL_001f: nop
        004300d2 90              nop
    IL_0020: nop
        004300d3 90              nop
    IL_0021: leave.s IL_0023
        004300d4 e8df21f950      call    clr!JIT_EndCatch (513c22b8)
        004300d9 eb00            jmp     004300db
} // END CATCH (IL_001e)
    IL_0023: nop
        004300db 90              nop
    IL_0024: ret
        004300dc 90              nop

Na koniec sprawdźmy jeszcze blok teb oraz listę exception records:

0:000> !teb
TEB at 7efdd000
    ExceptionList:        001eebb4
    StackBase:            001f0000
    StackLimit:           001ea000
    SubSystemTib:         00000000
    FiberData:            00001e00
    ArbitraryUserPointer: 00000000
    Self:                 7efdd000
    EnvironmentPointer:   00000000
    ClientId:             00002408 . 00000e48
    RpcHandle:            00000000
    Tls Storage:          7efdd02c
    PEB Address:          7efde000
    LastErrorValue:       0
    LastStatusValue:      8000001a
    Count Owned Locks:    0
    HardErrorMode:        0

0:000> !slist $teb _EXCEPTION_REGISTRATION_RECORD 0
SLIST HEADER:
   +0x000 Alignment          : 1f0000001eebb4
   +0x000 Next               : 1eebb4
   +0x004 Depth              : 0
   +0x006 Sequence           : 1f

SLIST CONTENTS:
001eebb4
   +0x000 Next             : 0x001eec78 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150b9d8     _EXCEPTION_DISPOSITION  clr!_except_handler4+0
001eec78
   +0x000 Next             : 0x001eece0 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150bb22     _EXCEPTION_DISPOSITION  clr! ?? ::FNODOBFM::`string'+0
001eece0
   +0x000 Next             : 0x001eed30 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150dd9c     _EXCEPTION_DISPOSITION  clr!COMPlusFrameHandler+0
001eed30
   +0x000 Next             : 0x001ef000 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150b9d8     _EXCEPTION_DISPOSITION  clr!_except_handler4+0
001ef000
   +0x000 Next             : 0x001ef26c _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150b9d8     _EXCEPTION_DISPOSITION  clr!_except_handler4+0
001ef26c
   +0x000 Next             : 0x001ef750 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x518c17f0     _EXCEPTION_DISPOSITION  clr! ?? ::FNODOBFM::`string'+0
001ef750
   +0x000 Next             : 0x001ef7a0 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x518bf246     _EXCEPTION_DISPOSITION  clr! ?? ::FNODOBFM::`string'+0
001ef7a0
   +0x000 Next             : 0x001ef7f0 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150b9d8     _EXCEPTION_DISPOSITION  clr!_except_handler4+0
001ef7f0
   +0x000 Next             : 0x001ef824 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x518aff2c     _EXCEPTION_DISPOSITION  clr! ?? ::FNODOBFM::`string'+0
001ef824
   +0x000 Next             : 0x001ef894 _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x5150b9d8     _EXCEPTION_DISPOSITION  clr!_except_handler4+0
001ef894
   +0x000 Next             : 0xffffffff _EXCEPTION_REGISTRATION_RECORD
   +0x004 Handler          : 0x777571d5     _EXCEPTION_DISPOSITION  ntdll!_except_handler4+0
ffffffff
   +0x000 Next             : ????
   +0x004 Handler          : ????
Can't read memory at ffffffff, error 0

0:000> !exchain /f
001eebb4: clr!_except_handler4+0 (5150b9d8)
  CRT scope  0, filter: clr!RaiseTheExceptionInternalOnly+242 (5150be86)
                func:   clr!RaiseTheExceptionInternalOnly+255 (51635251)
001eec78: clr! ?? ::FNODOBFM::`string'+2d78d (5150bb22)
001eece0: clr!COMPlusFrameHandler+0 (5150dd9c)
001eed30: clr!_except_handler4+0 (5150b9d8)
  CRT scope  0, filter: clr!CallDescrWorkerWithHandler+af (514a1724)
                func:   clr!CallDescrWorkerWithHandler+c0 (515ed0c3)
001ef000: clr!_except_handler4+0 (5150b9d8)
  CRT scope  1, func:   clr!ClassLoader::RunMain+27f (515cca26)
  CRT scope  0, filter: clr!ClassLoader::RunMain+295 (515cca2d)
                func:   clr!ClassLoader::RunMain+2b0 (515cca48)
001ef26c: clr! ?? ::FNODOBFM::`string'+d4a2 (518c17f0)
001ef750: clr! ?? ::FNODOBFM::`string'+b931 (518bf246)
001ef7a0: clr!_except_handler4+0 (5150b9d8)
  CRT scope  1, func:   clr!ExecuteEXE+62 (5159a142)
  CRT scope  0, filter: clr!ExecuteEXE+72 (5159a149)
                func:   clr!ExecuteEXE+85 (5159a15c)
001ef7f0: clr! ?? ::FNODOBFM::`string'+a7f (518aff2c)
001ef824: clr!_except_handler4+0 (5150b9d8)
  CRT scope  0, filter: clr!_CorExeMain+1e (5159c4d7)
                func:   clr!_CorExeMain+2d (5159c4e6)
001ef894: ntdll!_except_handler4+0 (777571d5)
  CRT scope  0, filter: ntdll!__RtlUserThreadStart+2e (777574b0)
                func:   ntdll!__RtlUserThreadStart+63 (777590cb)

Jak zatem widać, gdy mamy do czynienia z przechwyconymi wyjątkami, nie jesteśmy całkowicie ubezwłasnowolnieni i nie wiemy co się dzieje. Wystarczy, że zasadzimy się na first chance exceptions i przynajmniej zorientujemy się jakiego typu sytuacje wyjątkowe pojawiają się w aplikacji oraz prawdopodobne ich źródło.

Opublikowane 20 lipca 2012 20:18 przez mgrzeg
Filed under: , ,

Powiadamianie o komentarzach

Jeżeli chciałbyś otrzymywać email gdy ta wypowiedź zostanie zaktualizowana, to zarejestruj się tutaj

Subskrybuj komentarze za pomocą RSS

Komentarze:

 

dotnetomaniak.pl said:

Dziękujemy za dodanie artykułu - Trackback z dotnetomaniak.pl

lipca 23, 2012 14:46
 

Matrix said:

Wybieram niebieską pigułkę.

lipca 24, 2012 07:46

Co o tym myślisz?

(wymagane) 
(opcjonalne)
(wymagane) 

  
Wprowadź kod: (wymagane)
Wyślij

Subskrypcje

W oparciu o Community Server (Personal Edition), Telligent Systems