Coding with Titans

so breaking things happens constantly, but never on purpose

Prywatne Repozytorium plików PDB

Mało kto zdaje sobie tak naprawdę sprawę, że oprócz wersjonowania kodu oraz oficjalnych/inżynierskich releasów wysyłanych do klientów wypada również wersjonować pliki PDB z nimi związane. Dlaczego? Odpowiedź jest bardzo prosta. Wyobraźmy sobie, iż nasza aplikacja się po prostu wysypuje. Zaraz ktoś mi powie – “hola hola, ale przecież mamy logi, w pięknym tekstowym formacie i wszystko w nich widać”. Może ktoś nawet słyszał o plikach MAP :) Super! No ale nie wszystkie nasze moduły muszą być przecież napisane w .NET. Te napisane w językach natywnych (C/C++) nie podadzą nam tak łatwo miejsca wystąpienia błędu (czytaj: pełny stos wywołań, call-stack). Jedyne na co możemy wtedy liczyć, bez wspomnianej informacji debugowej, to nazwa modułu i adres, pod którym spotkany został błąd. Ponadto nie zawsze mamy pod ręką Visual Studio. Jeśli chcielibyśmy przeprowadzić debugowanie na nieco niższym poziomie przy pomocy WinDBG, aby wykryć zakleszczenia (dead-locks) oraz wysokie zużycie pamięci, czy po prostu różne inne wycieki pamięci natywnej (nawet w kodzie .NET) to okazują się one wręcz niezbędne. W dodatku na różnych maszynach, ten sam błąd może wystąpić pod różnym adresem, co związane jest z niedeterministyczną realokacją modułów lub po prostu dysponujemy zrzutem pamięci (memory dump file), który pozwala nam w pełni cieszyć się chwilą awarii naszej aplikacji w trybie “pośmietnym” (określanym jako post-mortem debugging).

A tak niewiele trzeba, aby całe zadanie w pełni zautomatyzować. Potrzebujemy zrobić około trzech rzeczy:

  • założyć lokalne repozytorium plików PDB oraz EXE/DLL dla naszych produktów (dajmy na to w katalogu D:\SymbolStore\Projects)
  • dla jeszcze większej wygody założyć lokalne repozytorium na pliki PDB dla bibliotek systemu operacyjnego, które będziemy pobierać z publicznych serwerów Microsoftu (co pomoże rozszyfrować nazwy funkcji oraz przekazywane parametry do systemowych wywołań WinAPI – kernel32.dll, user32.dll…)
  • zdefiniować zmienną systemową _NT_SYMBOL_PATH, która będzie pokazywała na te repozytoria:
_NT_SYMBOL_PATH=srv*D:\SymbolStore\Microsoft\* http://msdl.microsoft.com/download/symbols;srv* D:\SymbolStore\Projects\

Od tej pory z każdą nową wersją naszej aplikacji wykonamy poniższe dwie linijki skryptu (rozszerzone samemu już też o pliki EXE/DLL lub inne cenne dla nas dane):

set MODPATH=D:\SymbolStore\Projects\
set ProjectPath=D:\Projects\MyProject
symstore add /r /f "%ProjectPath%\*.pdb" /s %MODPATH% /t "MyProject" /v "Build v2.0.1" /c "15/12/2009 Daily Build"

Narzędzie symstore służące do zarządzania lokalnym repozytorium plików PDB dostępne jest za darmo po zainstalowaniu pakietu Debugging Tools for Windows (czyli po prostu z WinDBG). Dodatkowo zmienną tę poprawnie rozpoznaje większość Microsoftowych debuggerów i automatycznie pobiera odpowiednie pliki PDB związane z modułami EXE/DLL, które aktualnie analizujemy.

Nie ma róży bez kolców:

  • w ciągu kilku pierwszych uruchomień, większość tych plików PDB dla naszego OSa musi zostać pobrana z Internetu, co może trochę potrwać (300MB się jednak trochę pobiera…)
  • zajmują one też coraz więcej miejsca na dysku ;)
  • no i w końcu sam czas włączania debugera, zwłaszcza Visual Studio, znacząco się wydłuża, bo są one ładowane do pamięci, aby rozwiązywać nazwy funkcji; WinDBG jest tu o tyle inteligentniejszy, że ładuje te pliki na żądanie lub kiedy podejrzewa, że będą potrzebne nie obciążając maszyny aż tak bardzo.

Miłego debugowania.