I přesto, že je aplikace napsaná co nejlépe a je také důkladně otestovaná, občas dojde k jejímu pádu. Příčinou může být odlišná konfigurace počítače, nová verze operačního systému, nebo konflikt s jiným softwarem. Výsledkem je (ne)chvalně známého hlášení o tom, že program provedl neplatnou operaci a bude ukončen (obr. vpravo ukazuje hlášení, které uvidíme ve Windows XP). Tento článek popisuje možnost analýzy pádu aplikací, které nastanou mimo vývojové prostředí, nebo dokonce mimo pracoviště, kde byla aplikaca vyvinuta nebo testována. Jde o tzv. ladění "post mortem", tedy ladění po pádu aplikace.
Pokud k pádu dojde na počítači, na kterém je program vyvíjen (a tedy je nainstalováno vývojové prostředí), je možné příčinu pádu snadno najít i v případě, že spadla Release verze aplikace. Díky funkci Just-In-Time Debugging, dostupné v moderních ladicích nástrojích je možné zachytit vyjímku v aplikaci i přesto, že v okamžiku pádu neběží pod debuggerem. Pokud sestavíme Release verzi aplikace s ladicími informacemi, někdy dokonce Visual Studio ukáže přímo zdrojový soubor a řádek, kde nastala chyba. Pro nakonfigurování je nutné provést následující kroky:
int WINAPI _tWinMain(HINSTANCE, HINSTANCE, LPTSTR, int) { LPTSTR szString; _stprintf(szString, _T("Uživatel %s na počítači %s"), _T("Test"), _T("TestComputer")); return 0; }
Zkompilujeme Release verzi a spustíme aplikaci. Hlášení o chybě aplikace vypadat jinak než v přechozím případě (viz. obr.). Stiskem tlačítka "Yes" bude spuštěno MSVS.NET 2003, které se připojí na běžící proces a zastaví běh na adrese, na které nastala vyjímka. Hlášení o chybě zní "Unhandled exception at 0x0040312e in SuperAplikace.exe: 0xC0000005: Access violation writing location 0x00000006.". To v překladu znamená, že naše aplikace se snaží zapisovat nějaká data na adresu 0x00000006. Tato adresa opravdu nevypadá jako správné místo pro zapsání dat. Na adrese, kde nastala výjimka, vidíme následující kód (může se lišit v závislosti na použité verzi Visual Studia):
return (wint_t) (0xffff & (*((wchar_t *)(str->_ptr))++ = (wchar_t)ch));
Tento kód ukládá jeden znak do bufferu. Protože víme, že jde o funkci swprintf, která ukládá formátovaný řetězec do námi specifikovaného bufferu, lze z toho usoudit, že při volání funkce swprintf předáváme nesprávný parametr pro výstupní buffer.
Pokud zobrazíme zásobník (call stack), ukáže nám hierarchii volaných funkcí:
Vidíme, že chyba nastala ve vnitřní funkcí knihovny CRT volané z funkce swprintf. Chybu ale nebudeme hledat ve funkci swprintf samotné, ale spíš v místě, odkud je funkce volaná (z naší funkce WinMain). Dvojklikem na řádek označený na obrázku se dostaneme na řádek v našem kódu, kde byla chyba. Nyní už jen zbývá zajistit nápravu a kód už bude fungovat, jak má.
Pokud aplikace spadne v reálném provozu, tedy většinou mimo vývojové prostředí, je možné velmi dobře analyzovat příčinu pádu pomocí tzv. crash dumpu aplikace. Jde o soubor, obsahující informace o příčině pádu. V každé instalaci operačního systému řady NT je přítomen nástroj Dr. Watson (drwtsn32.exe), který se umí nainstalovat jako výchozí Just-In-Time Debugger. V případě pádu aplikace je spuštěn a vytvoří crash dump aplikace. Pro nakonfigurování Dr. Watsona proveďte následující kroky:
Nyní je Dr. Watson připraven k vygenerování crash dumpu v případě, že v aplikace dojde k chybě. Program Dr. Watson neběží na pozadí, spuštěn bude až v případě že v libovolné aplikaci dojde k vyjímce. Crash dump bude uložen do adresáře specifikovaném v políčku "Crash Dump".
Jakmile k výjimce dojde, Dr. Watson vygeneruje soubor, který můžeme dále analyzovat. K analýze dumpu budeme potřebovat tzv. Debugging Tools for Windows, dostupné zdarma na webu Microsoftu. Velikost instalačního balíčku je asi 11 MB. Aplikaci nainstalujte s výchozími volbami.
Dále je potřeba nakonfigurovat tzv. Symbol Server. Symbol Server je databáze PDB souborů (symbolů) pro binárky operačního systému, např. kernel32.dll. Ačkoliv jsou tyto PDB symboly dostupné jako balíček na webu Microsoftu a také v rámci MSDN, po instalaci záplaty operačního systému přestanou odpovídat symboly ke komponentám jejichž nové verze byly při aplikaci záplaty nainstalovány. Nakonfigurováním WinDbg pro použití Symbol Serveru zajistíme, že při potřebě symbolů k operačnímu systému si WinDbg stáhne z webu Microsoftu souboru symboly přesně k té verzi, kterou máte na počítači nainstalovanou.
Vytvoříme složku, do které budeme kopírovat jak symboly k naší aplikaci, tak i symboly k operačnímu systému (např. "C:\Symbols"). Do této složky nakopírujeme PDB soubor vygenerovaný linkerem při sestavování naší aplikace. Pokud má aplikace více komponent (např. knihovny DLL), přidáme do složky také jejich PDB soubory. Symboly musejí být vždy synchronizovány s binárními moduly aplikace, jinak WinDbg bude zobrazovat nesprávné informace. Dokonce i pouhé přeložení aplikace bez provedení změn může způsobit stížnosti programu WinDbg na nesprávné symboly.
Symbol server nakonfigurujeme pomocí proměnné prostředí _NT_SYMBOL_PATH ("My Computer\Properties\Advanced\Environment Variables"). Do "System Variables" přidejte proměnnou "_NT_SYMBOL_PATH" s hodnotou "C:\Symbols;srv*C:\Symbols*http://msdl.microsoft.com/download/symbols". Pro správnou funkci Symbol Serveru je nutné připojení k internetu, ale pokud jsou všechny potřebné symboly již staženy, WinDbg se bez internetu obejde. Databázi symbolu je také možné přenést na jiný počítač prostým zkopírováním celého adresáře. Podrobné informace najdete také v nápovědě k aplikaci WinDbg.
Nyní spusťte aplikaci WinDbg a otevřete aplikační crash dump (File\Open Crash Dump). WinDbg je příkazový debugger, jeho ovládání se vykonává pomocí příkazů. Základním příkazem debuggeru pro analýzu crash dumpu je "!analyze -v". Zadáme tento příkaz a WinDbg vypíše podrobnou analýzu, vč. obsahu registrů a call stacku. Právě call stack je nejdůležitější vypsanou informací o pádu, umožňuje zjistit sekvenci volaných funkcí, která vedla k pádu aplikace:
STACK_TEXT: 0012f7c0 00401481 7ffd0055 0012fc54 0040174f SuperAplikace!fputwc+0xb0 [f:\vs70builds\3077\vc\crtbld\crt\src\fputwc.c @ 131] 0012f7cc 0040174f 7ffd0055 00000006 77d96116 SuperAplikace!write_char+0x16 [f:\vs70builds\3077\vc\crtbld\crt\src\output.c @ 1133] 0012fc3c 004010d5 0012fc54 004060ee 0012fc84 SuperAplikace!_woutput+0x25d [f:\vs70builds\3077\vc\crtbld\crt\src\output.c @ 406] 0012fc74 0040107f 00000006 004060ec 0012fd98 SuperAplikace!swprintf+0x2e [f:\vs70builds\3077\vc\crtbld\crt\src\swprintf.c @ 106] 0012fe98 00401304 00400000 00000000 00020758 SuperAplikace!wWinMain+0x7f [e:\ladik\appdir\superaplikace\winmain.cpp @ 51] 0012ffc0 7c816d4f 00360032 00360032 7ffd6000 SuperAplikace!wWinMainCRTStartup+0x18a [f:\vs70builds\3077\vc\crtbld\crt\src\crt0.c @ 251] 0012fff0 00000000 0040117a 00000000 78746341 kernel32!BaseProcessStart+0x23
Protože máme k modulu SuperAplikace.exe kompletní symboly, WinDbg zobrazí i jméno zdrojového souboru a číslo řádku u každé položky ve výpisu. Další postup je shodný s postupem při ladění pomocí Visual Studia.
Pokud vyvíjíme aplikaci s větším množstvím modulů a navíc udržujeme více verzí, je možné integrovat PDB symboly do Symbol Serveru. Odpadnou starosti o to, která verze aplikace patří ke kterým symbolům. WinDbg pak automaticky načte symboly, které jsou potřeba (kontrola se provádí na základě časové známky modulu). Po sestavení Release verze aplikace je nutné spustit utilitu SymStore.exe (součást balíčku Debugging Tools for Windows).
symstore.exe add /f BinaryFile.exe /s C:\Symbols /t Comment
Je vhodné umístit uložení symbolů do dávkového souboru např. "PostBuild.bat", který Visual Studio spustí po každém sestavení Release verze aplikace.
V reálné praxi samozřejmě není hledání chyb tak jednoduché, jak ukázal tento příklad. Složitost nalezení chyby se závisí na povaze problému, zkušenosti a schopnostech programátora. Tento článek by měl sloužit jako úvod do možností ladění aplikací "post-mortem". Další informace a postupy o možnostech analýzy najdete v nápovědě k aplikaci WinDbg.
Copyright Ladislav Zezula 12.04.2005