20/11/2008

Pożytki płynące z używania Process Monitor'a

Home

W poście tym chciałbym zachęcić do używania darmowego narzędzia Process Monitor, które umożliwia monitorowanie wszelkiej aktywności w systemie operacyjnym dotyczącej zasobów takich jak: pliki, klucze rejestru, połączenia sieciowe itd.

Pożytki płynące z tego narzędzia opiszę na swoim przypadku. Ostatnio napisałem prostą bibliotekę, która parsuje plik Xml, przetwarza jego zawartość przy pomocy transformacji Xsl w celu stworzenia plików Html i finalnie uruchamia kompilator pomocy w celu wytworzenia pliku chm. Biblioteka nie jest skomplikowana i bardzo szybko udało się uzyskać pożądany efekt (prawie). Niestety ale okazało się, że kompilator pomocy nie potrafi przetworzyć wszystkich plików Html i generuje dla nich błąd HHC5003: Error: Compilation failed while compiling... Co ciekawe efekt ten był obserwowany tylko w przypadku programowego uruchomienia kompilatora przy pomocy klasy Process. Przy ręcznym uruchamiania wszystko działało prawidłowo. Po bliższym przyjrzeniu okazało się, że kompilator zgłaszał błędy zawsze dla tych samych plików. W przypadku kiedy lista plików została powiększona błąd zaczął być zgłaszany dla nowych plików! Pliki, które wcześniej zdawały się nieprawidłowe okazywały się nagle jak najbardziej w porządku.

Nie udało mi się znaleźć dokładnych informacji na temat zgłaszanego przez kompilator błędu z wyjątkiem tego, że przyczyną może być brak dostępu do pliku. Postanowiłem więc przyjrzeć się procesowi kompilacji przy pomocy Process Monitor'a. Ponieważ aplikacja w trybie domyślnym wyświetla ogromną ilość danych (setki tysiące wierszy) postanowiłem skupić się tylko na wpisach dotyczących jednego z plików, dla których kompilator zgłaszał błąd. Po włączeniu filtrowania uzyskałem taki wynik:



Na niebiesko oznaczyłem wiersz, w którym widać, że kompilator nie uzyskał dostępu do pliku z powodu błędu SHARING VIOLATION. Błąd ten oznacza, że plik był w tym momencie w użyciu. Ponieważ problematyczny plik Html był generowany w całości przez mój kod mogłem założyć, że błąd tkwi po mojej stronie. Jak się okazało popełniłem akademicki błąd i nie zamykałem strumienia do pliku (do tej pory biję się w pierś). Oczywiście konstrukcja using rozwiązała problem.

Cały czas pozostaje jednak pytanie czemu błąd pojawiał się tylko dla określonych plików. Strumień nie był przecież zamykany dla każdego dynamicznie wygenerowanego pliku Html. W tym przypadku odpowiedź też nie jest skomplikowana - musiał zadziałać mechanizm automatycznego zwalniania pamięci garbage collector.

19/11/2008

Zakamarki Visual Studio (cz. 1)

Home

Visual Studio to potężne narzędzie, o ogromnych możliwościach, które pozwala tworzyć i debugować programy w łatwy i przyjemy sposób. Dobra znajomość swojego środowiska pracy do podstawa dla każdego programisty. Dlatego poniżej zamieszczam opis kilku "zaawansowanych" narzędzi udostępnionych w Visual Studio. Tak naprawdę prezentowane przeze mnie techniki nie są ani trudne, ani skomplikowane w użyciu. Nie ulega jednak wątpliwości, że są stosunkowo mało znane. Działają poprawnie w przypadku języka C# (dla innych języków może być inaczej).

Okienko Find

Z pewnością każdy kiedyś korzystał z tego narzędzia aby szybko wyszukać w kodzie interesującą go frazę. Ale możliwości okienka Find są znacznie większe. Po pierwsze wpisanie do niego nazwy jakieś metody i naciśnięcie przycisku F9 spowoduje wstawienie breakpoint'a do każdej metody o podanej nazwie. Moim zdaniem bardzo przydatna funkcjonalność jeśli mamy w projekcie jedną lub więcej, wielokrotnie przeciążonych metod. Do okienka Find możemy również wpisać nazwę pliku (razem z rozszerzeniem). Teraz jeśli naciśniemy kombinację Ctrl+Shift+G i plik o podanej nazwie istnieje to zostanie otworzony gotowy do edycji. Przydaje się przy dużym drzewie projektów z rozbudowaną hierarchią plików i folderów.

Podglądanie cudzych metod

Przypuśćmy przez chwilę, że pracujemy ze złośliwym programistą, który nie chce udostępnić innym swoich kodów. Przypuśćmy dalej, że w kodzie złośliwego programisty jest generowany wyjątek. Wyjątek ten powstaje głęboko w kodzie złośliwego programisty i oznacza, że do metody został przekazany ciąg znaków o błędnym formacie. Wspomniany ciąg znaków dostarczany jest przez nas i może to być np.: connection string.

Co więcej programista jest tak uparty, że twierdzi, że to nie jego wina oraz, że z pewnością przekazujemy do jego metody złe dane. Co gorsza jest na tyle sprytny, że zabezpieczył swoje biblioteki przed programem takim jak Lutz Roeder’s Reflector. Tak na marginesie to genialne narzędzie, niezbędne w pracy każdego programisty .Net. Należy jeszcze dodać, że ten programista to syn szefa :)

My wiemy swoje i on wie swoje. Nie mamy dostępu to kodu biblioteki złośliwego programisty ale oczywiście znamy stack trace. Znamy więc nazwy kolejno wołanych metod aż do wystąpienia wyjątku. Jedyna nasza szansa to udowodnić złośliwemu programiście, że nasze dane są prawidłowe i to w jego kodzie jest błąd.

I tutaj wkracza do akcji Visual Studio. Okazuje się, że można postawić breakpoint w metodzie, dla której nie mamy kodu źródłowego. Kiedy debugger zatrzyma się będziemy mogli w oknie Locals zobaczyć wartości wszystkich argumentów przekazanych do metody! W celu postawienia takiego breakpoint'u korzystamy z bardzo prostego sposobu: otwieramy okno New Breakpoint, w polu Function wpisujemy nazwę metody, naciskamy przycisk Ok i gotowe. Do okna New Breakpoint dostajemy się z poziomu standardowego okna Breakpoints (Debug -> Windows -> Breakpoints) wybierając przycisk New, a następnie polecenie Brak at Function...

Okno Call Stack

Run To Cursor to bardzo znane polecenie, które umożliwia rozpoczęcie debugowania i wskazanie miejsca w kodzie, w którym debugger ma się zatrzymać (pod warunkiem, że nie zatrzyma się wcześniej z powodu breakpoint'a). Niewiele osób jednak wie, że polecenie to jest dostępna z poziomu okna Call Stack. Pozwala wskazać, do którego miejsca chcemy zwinąć stos. Wystarczy wybrać interesującą nas metodę i z menu kontekstowego wybrać tę komendę. W bardzo podobny sposób można przy pomocy okna Call Stack postawić breakpoint. W tym celu podobnie jak wyżej wybieramy interesującą nas metodę, zaznaczamy ją i naciskamy przycisk F9 (albo wywołujemy menu kontekstowe i wybieramy Breakpoint -> Insert Breakpoint).

Podsumowanie

Mam nadzieję, że opisane przeze mnie narzędzia i techniki okażą się przydatne. W następnej części opiszę kolejną porcję ciekawych funkcji Visual Studio.

Opisane techniki testowałem w środowiskach Visual Studio 2005 oraz Visual Studio 2008.