Development z Zapomnianej Strony

..:: Paweł Hofman .NET Portal ::..

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.



Resource Governor Resource Governor to nowa opcja dostępna tylko w Microsoft SQL Server 2008 Enterprise. Dzięki niej możliwe jest w końcu zagwarantowanie priorytetów oraz średniego czasu wykonania zapytań, od których zależy nasz biznes. Pozwala on między innymi na zarządzanie i przypisywanie zasobów “typom” użytkowników łączących się z naszą bazą danych. Każdy “typ” reprezentowany jest jako pewna workload groupa, która z kolei posiada referencję do zasobów (resource pool). Zasobami są tutaj: minimalna i maksymalna ilość pamięci RAM oraz czasu procesora. Oba podawane są w procentach.

Procedura wygląda następująco - podczas łączenia, zanim jeszcze kontrola (czy uchwyt) zostanie zwrócony wywołującemu, wykonywana jest funkcja klasyfikująca (User Defined Function, UDF), którą sami możemy utworzyć, czy modyfikować. A następnie wszystkie zapytania (niezależnie od ich rodzaju), które wykonywane są w ramach danego połączenia będą ograniczane. Ograniczenia te zostają zaaplikowane jednak dopiero wówczas, gdy aktywnych jest kilka, różnych połączeń. Nawet restrykcyjne ograniczenia nie są więc brane pod uwagę, jeśli w danych aktualnie warunkach serwer dysponuje większymi zasobami, które może przydzielić. Gwarantuje to tym samym, że przy mało obłożonym serwerze, nawet połączenia o niskim priorytecie wykonają się możliwie najszybciej.

Dwie wbudowane grupy (“default” i “internal”) nie posiadają żadnych ograniczeń. Ich zadaniem jest wykonywanie zapytań, gdy: coś źle zostało sklasyfikowane (wtedy “default”) lub aby nie zagłodzić procesów własnych engine'u bazy, tzn.: SQL Server Agent, Service Brokera, checkpointów, czy lazy-writera (wtedy “internal”).

Za:

  • w końcu można kontrolować, co jest najważniejsze w naszym biznesie
  • jeden użytkownik nie ubije nam całego serwera dzięki “runaway” query

Braki:

  • jest to tylko własność Database Engine’u (czyli nici z zarządzaniem Analysis Services, Reporting Services, Integration Services…)
  • działa w obrębie jednej instancji (jeśli na jednej maszynie chodzi ich więcej, to o ile z pamięcią można próbować skonfigurować, to czasem procesora możemy zarządzać tylko przez opcje affinity, co nie jest dość optymalnym rozwiązaniem)
  • ograniczenie pamięci działa tylko na Query Execution Memory (nie pamięć cache), czyli podlegać mu będą jedynie zapytania alokujące pamięć (tj.: wymagające sortowania, łączenia tabel…)
  • brak restrykcji na operacje I/O
  • połączenie jest klasyfikowane tylko raz, podczas łączenia i później nie może być przeniesione do innej grupy, bez wcześniejszego rozłączenia (choć zasoby grupy mogą być zmieniane dynamicznie).


Z różnych powodów, czasem nie chcemy, aby ktoś przyglądał się naszej aplikacji w trakcie jej działania. Pomińmy jednak na chwilę te powody i skupmy się na samym wykrywaniu debuggera…

Najprostszą metodą sprawdzenia w systemie Windows, czy nasza aplikacja jest aktualnie uruchomiona w jego kontekście lub jest on podłączony można wykonać poprzez wywołanie funkcji IsDebuggerPresent() z WinAPI. Jednakże sprytny debugger może ją przesłonić i zawsze zwracać, że nie.

Informację tę zatem wyciągniemy samemu, “ręcznie” z bloku informacji o wątku (Thread Information Block – TIB). Pod indeksem 0x30 znajduje się wskaźnik na strukturę opisującą debugger. Drugi bajt tej struktury opisuje status tego, czego szukamy. Ponadto wartość ta jest dynamicznie uaktualniana, gdy tylko coś dzieje się z naszym procesem. Przykład implementacji funkcji w C, która pobierze dla nas tę wartość przestawiam poniżej.


int IsSuperDebuggerPresent ()
{
    int result;

    __asm
    {
        /* get the Thread Information Block (TIB) pointer */
        mov eax, fs:[18h]

        /* 0x30 byte points to the debugger structure: */
        mov eax, dword ptr [eax + 0x30]

        /* then second word indicates if the process is under debugging: */
        movzx eax, byte ptr [eax + 2]
        mov result, eax
    }

    return result;
}


Inną metodą pozwalającą sprawdzić, czy nic nie dzieje się wokół naszej aplikacji jest również sprawdzanie licznika instrukcji RTDSC (Time Stamp Counter). W swoim kodzie możemy oszacować ilość ticków procesora, która mija przy wykonywaniu pewnych, zabezpieczanych bloków instrukcji. Następnie porównywać je  ze statystykami prowadzonymi wśród całego kodu.  Tutaj jednak pojawiają się pytania – jak zachowywać się i w środowiskach wielowątkowych i wieloprocesorowych, gdzie wielkości te nie dają się łatwo przewidzieć i mogą zmieniać się w czasie…



9.-te spotkanie przygotowujące do egzaminu 70-432 za nami. Poprowadzona sesja o Database Engine Tuning Advisor oraz Resource Governor udana! Więcej takich sobie i wam życzę.

Materiały do pobrania tutaj. Video opublikowane zostanie niebawem.

Te same sesje również obejrzeć można na portalu www.VirtualStudy.pl.



Autor

Paweł Hofman [CodeTitans]

ASP.NET
C/C++, C#, Objective-C
SQL

License and Disclaimer

Moje Gry i Aplikacje

Zobacz mnie na GoldenLine

Zobacz mnie na LinkedIn
Supported by Polish SQL Server User Group (PLSSUG)

Supported by WrocNet.org

Zine.net.pl

Wpisy

Projekty

Moje projekty open-source:

Sign in