Ostatnio musiałem przekazać klientowi częściowo gotową aplikację zanim jednak to zrobiłem poprosiłem znajomego by ją odpalił u siebie i zobaczył czy wszystko mu śmiga (to, że mi na komputerach w domu działa to nic dziwnego).
Dobrze się złożyło, gdyż niestety aplikacja się nie uruchomiła :) co lepsze dodała do Windows Event Log błąd Windows Error Reporting o ID 1001. Mimo usilnych prób u siebie nie byłem wstanie problemu z reprodukować - suma sumarum okazało się, że wina leży w jednej DLLce, która u mnie była w GAC a u znajomego jej nie było (no i słusznie, że jej tam nie było :), najgorsze z tego było to, że z tej DLLki korzystałem w metodzie obsługującej unhandled exception:)
Jednakże błąd, który znajomy mi pokazał bardzo mnie zaciekawił - głownie, dlatego by w przyszłości wiedzieć jak go czytać i rozumieć. W przybliżeniu wyglądał on tak:
Fault bucket 1039438065, type 5
Event Name: CLR20r3
Response: Not available
Cab Id: 0
Problem signature:
P1: watsonbucketmain.exe
P2: 1.0.0.0
P3: 4b9d671c
P4: WatsonBucketMain
P5: 1.0.0.0
P6: 4b9d671c
P7: 4
P8: 11
P9: System.ArgumentNullException
P10:
Błąd powyżej jest błędem z przykładowej aplikacji do pobrania stąd. To co powyżej widzicie to tak zwany Watson Bucket, lub inaczej Fault Bucket który w pierwszej linijce zawiera ID po którym pomoc MS jest wstanie wam udzielić odpowiedzi.
To co jest w sekcji problem signature pochodzi z integracji CLR z WER i to właśnie ta część najbardziej mnie zainteresowała. Jak widać na pierwszy rzut oka można kilka informacji zgadnąć :) jednak co to jest to P3, P6, P7 i P8?
Zgodnie z tym co udało mi się znaleźć w sieci to sytuacja wygląda następująco:
- P1 - nazwa aplikacji;
- P2 - to wersja aplikacji;
- P3 - Timestamp pliku wykonywalnego (czyli naszego P1);
- P4 - nazwa assembly/modułu;
- P5 - wersja assembly który wywołał błąd;
- P6 - timestamp modułu w którym wystąpił błąd;
- P7 - to jest token definicji metody (MethodDef, strona 127 jak sprawdzałem) pozbawiony High Byte (nie wiem jak to Podolsku powiedzieć, chodzi o to, że mając dwu bajtowy ciąg, to high byte jest ten po lewej a low byte ten po prawej);
- P8 - jest to offset instrukcji IL;
- P9 - wyjątek, jaki został zwrócony;
- P10 - nie udało mi się do tego dotrzeć :) jak ktoś wie niech powie :)
Czyli dla przykładowego kodu:
using System;
using Module;
namespace WatsonBucketMain
{
class Program
{
static void Main(string[] args)
{
ThrowsError p = new ThrowsError();
p.Run();
new ModuleA().AndIWillThrow("some string");
}
}
public class ThrowsError
{
public void Run()
{
IThrowException();
}
private void IThrowException()
{
Console.WriteLine("I will throw an excpetion :)");
throw new ArgumentNullException();
}
}
}
Zostanie nam zwrócony błąd jaki wkleiłem wyżej.
Teraz cały bajer polega na tym by znaleźć tą linijkę, która nas interesuje - która wywołała błąd. Możemy to zrobić za pomocą narzędzia ILDasm, otwieramy aplikację, przerzucamy drag-n-drop pliki EXE lub jak ktoś woli korzystamy z opcji Open (Ctrl+O) i od razu klikamy Ctrl+M (pokazuje to MetaInfo, View -> MetaInfo -> Show!). Tak przygotowani możemy zaczynać szybką i zwięzłą analizę:
(kliknij aby powiększyć)
Teraz przynajmniej jak dostanę tak jak to mój znajomy nazwał „Chesz listę z P?” :) to przynajmniej będę wiedział, co mogę na szybko zrobić by się dowiedzieć, dlaczego błąd wystąpił.
Jeszcze raz, kod dla przykładu dostępny jest tutaj.