27 lipca 2011

Programista i jego Troubleshooting Toolkit (Część II) - .NET

Rozwiązywanie problemów na platformie .NET


W kolejnym poście skupię się na temacie rozwiązywania problemów na platformie .NET z którymi borykają się programiści. Jest to zagadnienie bliskie memu sercu, ponieważ jako etatowy "detektyw .NET" wielokrotnie byłem zmuszony do korzystania z większości z tych narzędzi, szczególnie gdy zawiodło mnie bezmyślne wyszukanie w Google.



support.microsoft.com - powrót do korzeni


http://support.microsoft.com/ - postanowiłem zacząć od tej mało popularnej wśród programistów strony, na której Microsoft publikuje poprawki, artykuły techniczne oraz wszelakie rozwiązania problemów. Ta strona może być o tyle istotna w procesie poszukiwania rozwiązania, że support.microsoft.com zawiera rozwiązania i informacje które zostały już wielokrotnie sprawdzone przez inżynierów Microsoft w akcji.



Artykuły w Microsoft'owym KB są wielokrotnie zatwierdzane i sprawdzane zanim ujrzą światło dzienne, tym zasadniczo różnią się od blogów, artykułów i wypowiedzi na forach etc. W moim odczuciu wyszukiwarki nie zawsze pozycjonują wysoko strony z rozwiązaniami z support.microsoft.com, dlatego zawsze gdy rozwiązanie nie jest oczywiste warto sprawdzić to u ŹRÓDŁA.  Można też pójść w kierunku bardziej ortodoksyjnym i założyć konto na kbalertz.com. Usługa ta umożliwia otrzymywanie powiadomień o świeżo opublikowanych artykułach dla technologii, które wybierzemy w swoim profilu (np. .NET Framework, SQL Server 2008 R2 etc).

Fusion Log Viewer - assembly binding


Fusion Log Viewer (FUSLOGVW) - jest częścią Windows SDK i jest to bardzo użyteczne narzędzie gdy nie jesteśmy w stanie ustalić jakich assembly program nie może właściwie załadować i skąd je próbuje pobrać. Domyślnie Fusion Log Viewer loguje nieudane ładowania assembly. Aczkolwiek po zmianach w rejestrze poprzez dodanie HKLM\Software\Microsoft\Fusion\ForceLog i ustawieniu wartości na 1, otrzymamy pełny log udanych i nieudanych tzw. assembly bindings.


Po zmianach w rejestrze możemy śledzić próby załadowania assembly przez aplikacje uruchamiane na .NET CLR. Poniżej prezentuję prostą aplikację, która próbuje wczytać assembly o nieistniejącej nazwie.

using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            AssemblyName assemblyName = new AssemblyName();
            assemblyName.Name = "FakeAssemblyName";
            Assembly loadedAssembly = Assembly.Load(assemblyName);
        }
    }
}

Log Fusion viewer wyświetli nieudaną próbę w następujący sposób. Można przejrzeć plik logu dla wiersza oznaczonego FakeAssemblyName (patrz poniżej).


Tesktowy log dokładnie prezentuje, w jakich katalogach assembly resolver poszukuje nieszczęsnego FakeAssemblyName.dll.

*** Assembly Binder Log Entry  (2011-07-25 @ 19:23:19) ***

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  c:\users\...\ConsoleApplication1\ConsoleApplication1\bin\Debug\ConsoleApplication1.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = MaxPC\Max
LOG: DisplayName = FakeAssemblyName
 (Partial)
WRN: Partial binding information was supplied for an assembly:
WRN: Assembly Name: FakeAssemblyName | Domain ID: 1
WRN: A partial bind occurs when only part of the assembly display name is provided.
WRN: This might result in the binder loading an incorrect assembly.
WRN: It is recommended to provide a fully specified textual identity for the assembly,
WRN: that consists of the simple name, version, culture, and public key token.
WRN: See whitepaper http://go.microsoft.com/fwlink/?LinkId=109270 for more information and common solutions to this issue.
LOG: Appbase = file:///c:/users/.../ConsoleApplication1/ConsoleApplication1/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = ConsoleApplication1.exe
Calling assembly : ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: No application configuration file found.
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///c:/users/.../ConsoleApplication1/ConsoleApplication1/bin/Debug/FakeAssemblyName.DLL.
LOG: Attempting download of new URL file:///c:/users/.../ConsoleApplication1/ConsoleApplication1/bin/Debug/FakeAssemblyName/FakeAssemblyName.DLL.
LOG: Attempting download of new URL file:///c:/users/.../ConsoleApplication1/ConsoleApplication1/bin/Debug/FakeAssemblyName.EXE.
LOG: Attempting download of new URL file:///c:/users/.../ConsoleApplication1/ConsoleApplication1/bin/Debug/FakeAssemblyName/FakeAssemblyName.EXE.
LOG: All probing URLs attempted and failed.

Jak widać Log Fusion Viewer to ciekawe narzędzie, które może pomóc w rozwikłaniu skomplikowanych problemów z ładowaniem assembly.


IL Diassembler - metadata


IL Diassembler ( ILDASM) - jest to program dystrybuowany razem z Windows SDK lub z Visual Studio. Umożliwia on zapisanie lub przeglądanie kodu IL (Intermediate Language) dla danego assembly (dll lub exe). Częściowo jego funkcjonalność pokrywa się z ILSpy'em o którym piszę poniżej, jego przewagą w pracy z IL'em może być interfejs konsolowy oraz bardziej wyrafinowany sposób eksportu IL'a. Opcją którą zdarzyło mi się używać było przeglądanie Manifestu assembly, który zawiera m.in referencje do innych assembly, klucz publiczny, ustawienia corflags dotyczące architektury procesora który może wykonywać daną aplikację (w Visual Studio możemy ustawić podczas kompilacji x64, x86, any CPU).



Lista referencji do zewnętrznych assembly z ich tokenami klucza publicznego.


ILSpy - dekompilacja


ILSpy - to darmowy odpowiednik .NET Reflector'a. Jest on oczywiście bardziej wyrafinowanym dekompilatorem niż IL Diassembler. Niestety nie ma takiej ilości pluginów jak .NET Reflector, jest też trochę wolniejszy od swojego płatnego odpowiednika, nie integruje się z Visual Studio i nie daje możliwości debuggowania dekompilowanego assembly. Aczkolwiek parafrazując staropolskie przysłowie, darowanemu programowi się w pluginy nie zagląda. Dekompilatory dobrze wpisują się w scenariusz gdy musimy rozwijać system napisany przez zewnętrznego dostawcę (np. SharePoint, Microsoft CRM), ale nie mamy dostępu do kodu źródłowego. Mamy szczęście w momencie gdy dostawca nie użył obfuskatora, gdyż wtedy żaden dekompilator nam nie pomoże. ILSpy umożliwia konwersję kodu CIL (Common Intermediate Language) do kodu C#, pozwala także podejrzeć kod IL. 



Inną sytuacją kiedy ILSpy może okazać się przydatny jest weryfikacja, czy kod źródłowy, który mamy w systemie kontroli wersji zgadza się z kodem assembly, które wykonuje się na danym środowisku serwerowym. Niejednokrotnie musiałem dekompilować assembly na jakimś serwerze, aby tylko sprawdzić czy przypadkiem ktoś nie wrzucił starej/swojej/nowszej wersji danego assembly. W końcu, ILSpy daje nam takie możliwość weryfikacji kodu źródłowego, więc dlaczego z tego nie skorzystać. Alternatywą dla ILSpy'a może być JustDecompile, aczkolwiek ostrzegam, ostatnia beta którą uruchomiłem, rzuciła przy starcie pięknym czerwono-białym wyjątkiem i na tym się skończyło.

Możliwość załadowania i dekompilacji assembly bezpośrednio z GAC'a.



.NET Memory Profiler - CPU i pamięć


MemProfiler - staram się opisywać narzędzia, które są darmowe i ogólnodostępne, aczkolwiek dla MemProfiler'a postanowiłem zrobić wyjątek. Jest to rozbudowany profiler dla aplikacji opartych o platformę .NET. Muszę się przyznać, że przynajmniej w dwóch przypadkach wycieków pamięci udało mi się namierzyć problematyczny kod źródłowy tylko dzięki błyskotliwości tego narzędzia. MemProfiler pozwala wykonywać snapshot'y pamięci i później analizować różnice w ilości obiektów zaalokowanych na stercie, instancje obiektów na których nie została wywołana metoda Dispose (Dispose Tracker) itd. Ogólnie polecam zapoznanie się z tym narzędziem, jeżeli zgłosi się do was szef, że proces na serwerze produkcyjnym zajmuje teraz 2GB, a przed releasem zajmował 300 MB ;).



Diagram pokazujący ilość zaalokowanej pamięci na stercie .NET i pamięci niezarządzanej (unmanaged memory).

 

Na koniec - zestaw hardkorowca czyli Hang, Crash, Dump


Czasami jest tak, że trzeba wykazać się niestandardowym myśleniem. Szczególnie gdy wszystkie znane metody zawiodły. Przykłady takich przypadków to:
  • Niespodziewane crush procesu, usługi czy IIS'a (np. w3wp.exe)
  • Wieszanie się procesów
  • Losowe OutOfMemoryException
W takich momentach należy użyć trochę bardziej wyrafinowanych narzędzi, część z nich wchodzi w skład  pakietu Debugging Tools for Windows. Możemy rozpocząć z Debug Diagnostic Tool v1.1 i spróbować przeanalizować działanie naszej aplikacji pod kątem jednego z kilku możliwych problemów (Crash, Memory Leak lub IIS Hang). W końcu dostępna już jest wersja tego narzędzia dla systemów 64 bitowych. Jeżeli nasza analiza nie przyniesie oczekiwanych rezultatów, proponuje pójść ścieżką dokładnie opisaną przez Tess Ferrandez w swojej serii postów .NET Debugging Demos.
  1. Podstawa to WinDbg (dostępny w ramach  Debugging Tools for Windows)
  2. Adplus - skrypt napisany w vbscript (dostępny w ramach  Debugging Tools for Windows), przydatny przy generowania różnego rodzajów zrzutów pamięci (memory dump) dla danego procesu.
  3. SOS (Sun of strike) - rozszerzenie do debuggowania .NET-towych aplikacji, dla .NET 4 dostępny w %systemDirve%\Microsoft.NET\Framework\v4.0.30319\SOS.dll

Poza tym, lekcje debuggowania u Tess Fernandez to sama przyjemność i satysfakcja z dłubania na poziomie adresów w pamięci. Polecam.

Kilka linków do aplikacji uzupełniających tą listę
  1. Visual Studio Performance Tools - niestety nie wszyscy mogą sobie pozwolić na Visual Studio 2010 Premium lub Ultimate, aczkolwiek na pewno warto przyjrzeć się tym narzędziom.
  2. Performance Analysis of Logs (PAL) Tool - zainstaluj PAL'a i wymagane dodatkowe oprogramowanie (np. Chart Controls), zmień ustawienia regionalne na United States i bez szczegółowej wiedzy na temat performance counterów zbieraj informacje na temat np. aplikacji ASP.NET lub SQL Server'a i wielu innych produktów Microsoft'u. Na stronie projektu możemy znaleźć kilka przydatnych how-to. 
  3. CLR Profiler - profiler Microsoft'u, trudniejszy w obsłudze niż MemProfiler czy inne komercyjne profilery YourKit .NET Profiler czy Ants .NET Profiler, aczkolwiek ma jedną niezaprzeczalną zaletę, jest darmowy.  
Hope this helps.

Brak komentarzy:

Prześlij komentarz

Uwaga: tylko uczestnik tego bloga może przesyłać komentarze.