Jak na pro­jek­ty v ja­zy­ce C

© Damig, 2004 – 2016
Koncept

Ladění a testování programů
Ladění pomocí debuggeru

Prvním pomocným programem, po kterém programátor chtivý odhalení chyb sáhne, je debugger. Je to program určený pro krokování programu a hledání chyb. Umožňuje sledovat běh programu řádek po řádku a sledovat při tom hodnoty proměnných, registrů nebo dokonce vybraných míst v paměti. Kvalitnější debuggery umožňují klást do programu chytré zarážky (breakpoints) a přeskakovat tak kusy kódu, které nás zrovna nezajímají.

Debugger si lze představit jako simulátor instrukční sady procesoru, tedy jako program, který interpretuje instrukce programu namísto procesoru. Některé debuggery takto skutečně fungují a umožňuje to ladit programy pro jiné instrukční sady, než zná náš procesor.

Debugger spouští přeložený program (binárku) a zobrazuje k němu odpovídající zdrojový text ze zdrojového souboru. Aby mohl správně fungovat, je potřeba zdrojový kód překládat s ladícími informacemi. Jde o nadbytečné informace v kódu programu, které nemají vliv na jeho funkčnost. Debugger je ale umí využít pro správné zobrazení právě vykonávaného řádku zdrojovém kódu (umí si pak spojit binární kód s odpovídajícím řádkem ve zdrojovém souboru), hodnot používaných proměnných a ostatních potřebných informací.

Základním debuggerem dodávaným s GCC je GDB – GNU Debugger. Jde o program ovládaný z příkazového řádku (CLI). Téměř všechny debuggery s GUI, zaměřené na ladění programů vytvořených pomocí GCC, jsou vytvořeny jako nástavba nad GDB. To znamená, že GDB musí být nainstalován a tato GUI pak program GDB používají pro vykonávání všech operací, které nabízejí.

S debuggerem se můžete setkat ve dvou podobách. Buďto je zabudovaný ve vašem IDE (Code::Blocks, Eclipse, ...) nebo existuje jako samostatný program. Pokud jde o základní schopnosti jsou si rovnocenné.

Obecný postup při ladění debuggerem

Tento postup platí s malými odchylkami pro všechny dnešní debuggery s GUI (tedy i ty, které jsou součástí nějakého vývojového prostředí). Některé nabízejí i chytřejší funkce, ale základní princip používání je totožný.

  1. Přeložte program, který budete ladit s ladícími informacemi. Při překladu pomocí make zkontrolujte Makefile, používáte-li IDE, zkontrolujte nastavení překladu v příslušném nastavovacím okně.

  2. Spusťte přeložený binární soubor pomocí debuggeru. Binárku lze obvykle vyhledat i pomocí menu (Soubor → otevřít...).

    U většiny samostatných debuggerů (zde je jako příklad použit linuxový kdbg) stačí binárku předat jako parametr z příkazového řádku.

    $ kdbg ladenyprogram
    
    Spouštění samostatného debuggeru.

    U integrovaných vývojových prostředí (IDE) toto odpadá, stačí zvolit příkaz Debug z hlavního menu (Ladění, Spustit, ...).

    Pokud byl program přeložen s ladícími informacemi, debugger zobrazí zdrojový text programu, jinak většinou uvidíte kód v assembleru.

  3. Pokud je potřeba spouštět program s parametry příkazového řádku, je potřeba to programu sdělit pomocí příkazu z menu (Spuštění → Argumenty..., Projekt → Argumenty..., Run → Arguments..., a podobně).

Debugger zabudovaný v IDE Code::Blocks.
Nastavení argumentů příkazového řádku v Code::Blocks
Samostatný debugger KDbg běžící v Linuxu.
  1. Umístěte do zdrojového textu zarážku (breakpoint). Obvykle se to dělá kliknutím myší před řádek zdrojového textu. V tomto místě se objeví značka zarážky. Také je možné vložit zarážku na řádek pomocí kontextového menu (klik pravým tlačítkem myši nad vybraným řádkem).

    Na zarážce debugger zastaví vykonávání programu, aby šlo sledovat jeho stav (proměnné, paměť, registry, atd.).

    Existují dva typy zarážek:

    • Pevné – na těchto zarážkách se debugger zastaví vždy. Nepoužívejte je uprostřed dlouhých cyklů (co když chyba nastává až při 100479 iteraci?).
    • Podmíněné – lze do nich vložit podmínku, při jejímž splnění se debugger zastaví. Při nesplnění podmínky debugger tuto zarážku přeskakuje. Podmínka se obvykle nastavuje jako vlastnost obyčejné zarážky kontextovým menu nebo z okna zobrazujícího všechny nastavené zarážky programu. Pokud je debugger neumí, lze si pomoci vložením dodatečné podmínky bez vlivu na funkčnost programu do zdrojového textu.
  2. Začněte s krokováním programu (většinou Debug → Run nebo Spustit).

    Existuje několik módů krokování programu:

    • Další krok (step) – základní operace, umí ji všechny debuggery. Vykoná jeden řádek programu a zastaví se. Pokud je na aktuálním řádku volání funkce, vykoná ji jako jeden příkaz.
    • Krok dovnitř (step inside) – základní operace, umí ji všechny debuggery. Vykoná jeden řádek programu a zastaví se. Pokud je na aktuálním řádku volání funkce, přejde na první řádek těla této funkce.
    • Pokračuj (continue) – pokračuje ve vykonávání programu, dokud nenarazí na další zarážku nebo nedojde k ukončení programu.
    • Pokračuj do vybraného řádku – pokračuje ve vykonávání programu, dokud nenarazí na řádek programu, na kterém je nastaven textový kurzor.
    • Pokračuj do konce aktuální funkce – pokračuje ve vykonávání funkce a zarazí se na jejím konci (další příkaz bude skok z těla této funkce).
Nastavení zarážky v Code::Blocks
Editace podmíněné zarážky v Code::Blocks
Editace podmíněné zarážky v KDbg
Příkazy pro krokování programu v KDbg
Příkazy pro krokování programu v Code::Blocks. Okna pro sledování proměnných najdete pod ikonou Debugging windows
  1. Při zastavení na zvoleném řádku programu lze sledovat

    • Proměnné, parametry funkcí, výrazy – okno Watch, Watches, ... Některé debuggery umí zobrazit hodnotu proměnné v bublině, když podržíte kurzor myši nad identifikátorem nebo nad označeným výrazem.
    • Paměť – okno Memory, Memory dump, ... Pokud toto debugger umí, lze sledovat, jak jsou data ve skutečnosti umístěna v paměti. Lze tím odhalit chyby typu buffer overflow nebo překrývání polí.
    • Registry – okno Registry, CPU Registers, ... Lze sledovat aktuální obsah všech registrů, včetně příznaků. Většina debuggerů umí krokovat kód i po jednotlivých instrukcích.
    • Stav zásobníku – okno Stack, Call Stack, ... Většinou se v okně zobrazí funkce, které jsou zrovna uloženy na zásobníku v pořadí v jakém byly volány. To je důležité při ladění rekurzivních programů, protože pak lze názorně sledovat rekurzivní zanořování programu. Po výběru funkce v tomto okně se obvykle v okně pro sledování proměnných (Watches) objeví aktuální hodnoty parametrů a proměnných, které jsou pro tuto funkci uloženy na zásobníku.
  2. Debuggery umožňují měnit hodnoty proměnných zobrazených v okně pro jejich sledování. V dalším kroku pak bude program pracovat s těmito novými hodnotami. Obvykle lze hodnotu měnit kliknutím myší nebo vyvoláním kontextového menu nad měněnou hodnotou.

    Tato funkce výrazně usnadňuje ladění, pokud je potřeba navodit specifický stav programu, do kterého se lze jinak dostat jen zdlouhavým výpočtem nebo interakcí s programem (např. když chcete otestovat, co udělá funkce, když jí místo ukazatele předáte NULL).

  3. Některé debuggery obsahují i editor, takže lze do kódu hned vkládat opravy. Nezapomeňte ale, že upravený program je potřeba před dalším laděním znovu přeložit. Toto platí zejména pro různá IDE.

Okno Watches v Code::Blocks umí sledovat aktuální lokální proměnné a parametry, složitější výrazy, ale umí také měnit hodnoty proměnných.
Okna pro lokální proměnné a sledování výrazů v KDbg umí totéž. Všimněte si okna pro zobrazení stavu zásobníku.