IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

3. diel - Céčko a Linux - Debugging

V minulej lekcii, Céčko a Linux - Makefile , sme sa venovali Makefile. Už vieme prekladať svoje programy v termináli a vieme si to aj uľahčiť. Dnes sa pozrieme na ďalšie, veľmi dôležitá téma. Určite sa vám už stalo, že ste mali v programe niekde chybu a prechádzali ste ho znova a znova ... Ale stále ju nemohli nájsť. Je to tak častý problém, že bola vytvorená kopa nástrojov, ktoré nám ju majú uľahčiť. Hovorí sa im ladiace programy a najdôležitejšie z nich je tzv. Debugger (doslovný preklad je "odvšivovací").

Indent

Začneme zľahka a pozrieme sa najskôr na tento jednoduchý nástroj pre formátovanie kódu. Nainštalovať ho môžete príkazom sudo apt-get install indent a detailný popis jeho používanie nájdete v manuálových stránkach (man indent). Verím (dúfam), že väčšina z vás píše pekne odsadený kód. Načo teda taký program? Čas od času sa môžete dostať ku kódu, ktorý buď z nejakého dôvodu stratil pôvodné formátovanie (prišiel vám cez FB) alebo autor používa výrazne iný spôsob odsadzovanie a vás to jednoducho "štve". V takom prípade nie je nič jednoduchšie, než si nechať preformátovať kód k obrazu svojmu. :)

Malá ukážka - máme potrebné takýto nepekný kód (predstavte si, že má 10 000 riadkov). V tomto by sa vám nechcela hľadať chyba, že?

#include <stdio.h>
#include <stdlib.h>
int main
(int argc,char argv[]){int a=b=c=1;
printf("Výpočet: %d\n",(a+a+b)*(++c+c+++c);return 4;}

Spustíme teda indent soubor.c. Toto nastavenie nám kód sformátuje do klasického UNIXového štýlu. Ak nemáte radi odsadenie tabulátory, použite prepínač -nut (indent -nut soubor.c). Ten povie indent, že nechceme tabulátory, ale medzery. Výhoda tabulátorov je možnosť nastaviť si v editore ich šírku. Niekto má radšej odsadenie väčšie, niekto menšie (ja preferujem šírku dve medzery) .. Táto vlastnosť môže byť ale teoreticky aj nevýhoda - napríklad v prehliadačoch má tabulátor štandardne šírku 8 medzier, takže kód s tabulátormi môže mať naozaj obrovské odsadenie.

Po použití indent bude náš kód vyzerať nejako takto:

#include <stdio.h>
#include <stdlib.h>
int main (int argc, char argv[])
{
  int a = b = c = 1;
  printf ("Výpočet: %d\n", (a + a + b) * (++c + c++ + c);
          return 4;
          }

Nie je to samozrejme dokonalé (nepáči sa mi napríklad väčšie odsadenie returne), ale na rýchle sprehľadnenie nečitateľného kódu to stačí. Indent sa dá samozrejme (ako všetky Linuxové nástroje) dopodrobna nastaviť, takže s trochou trpezlivosti si môžete vytvoriť vlastnú konfiguráciu, ktorá bude formátovať kód presne podľa vašich predstáv.

Ešte poznámka - môžete si vytvoriť vlastný "profil" indent, ktorý bude automaticky použitý pri každom spustení. Stačí len vo svojom domovskom adresári vytvoriť súbor indent.pro a napísať do neho použité prepínača. Ich význam nájdete v manuálových stránkach.

Splint

Ďalší nástroj, na ktorý sa dnes pozrieme, slúži na analýzu kódu - vypíše nám možné problémy. Máme síce k dispozícii chybové hlášky prekladača, ale ako skoro uvidíme, tie nie vždy stačí. Pozrieme sa na nasledujúci kód - takto nejako by mohol vyzerať kód začiatočníka, čo si niekde niečo prečítal a teraz sa sám snaží vyskúšať si prácu so znakmi ...

#include <stdio.h>
#include <stdlib.h>

int main ()
{
  char c;
  while (c != 'x');
  {
    c = getchar();
    if (c = 'x')
      return 0;
    switch (c)
    {
      case '\t':
        printf("Zadal jsi tabulátor.\n");
      default:
        printf("%c", c);
    }
  }
  return 0;
}

Skúsime ho preložiť ..

$ gcc prog.c -o prog

Keď preložíme tento kód, prekladač nám nevráti žiadnu chybu. Napriek tomu ale kód nebude vôbec fungovať. Skúsenejší programátor určite vie prečo, ale začiatočník bude stratený. Prekladač nehlási chybu, ale program nefunguje. Čo s tým? V prvom diele sme hovorili o parametroch prekladača a ako by sme mali správne prekladať. Skúsime to teda znova.

$ gcc -std=c99 -Wall -Wextra -pedantic prog.c -o prog

Tentoraz už náš prekladač upozornil na dva problémy - v cykle while používame premennú c bez inicializácie a v podmienke máme namiesto porovnávanie priradenie.

warning: ‘c’ is used uninitialized in this function while (c != 'x');
warning: suggest parentheses around assignment used as truth value  if (c = 'x')

Varovanie samotné ale nie je príliš detailné a ani nám nehovorí, čo by sme s tým mali robiť. Skúsime splint prog.c (výpis je dlhý, takže si to každý skúste sám, aby ste presne videli, čo splint vypíše). Splint vykoná analýzu kódu a upozorní nás na nasledujúce problémy:

  • premenná c použitá pred spustením
  • hrozí nekonečná slučka
  • priradenie int do char: c = getchar () - hrozí strata informácie
  • priradenie v podmienke
  • "Fall trough case" - "prepadnutie" casom (chýba break, ak príde \ t, vykoná sa i default)

Ku každému problému je vypísaný detailný popis, prečo je to problém a ako by sa dal vyriešiť. Okrem toho sa aj dozvieme, ako toto varovanie vypnúť. Povedal by som, že splint sa môže občas celkom hodiť - najmä ak máme dlhý kód. ;) Opravený kód si môžete stiahnuť.

GDB

Prekladač ani splint nám nehlási žiadne chyby, ale program rovnako nefunguje tak, ako sme chceli. Čo s tým? Zavoláme si na pomoc debugger. Čo to ale vlastne je?

Debugger je nástroj, ktorý nám v zásade umožňuje krokovať (vykonávať náš kód po jednotlivých príkazoch) au toho sledovať hodnoty premenných. Existuje celý rad debugger a vie celý rad vecí. My sa dnes pozrieme na klasický gdb, ktorý je taký nepísaný štandard pre debuggovania programov v C / C ++, ale zvláda aj mnoho ďalších. Je tiež často základ rôznych grafických prostredí (dnes sa pozrieme na odd, nabudúce na Code :: Blocks, ďalej napríklad Nemiver).

Väčšina debugger umožňuje preskakovať bloky kódu, čo nás nezaujímajú (vieme, že tam nemôže byť chyba), pomocou tzv. Breakpoint. Breakpoint je miesto, kde sa zastaví vykonávanie a odtiaľ typicky ďalej ideme po riadkoch a sledujeme, ako program funguje. Existuje veľké množstvo možností a módov. My sa dnes pozrieme na debuggovania len rámcovo, aby sme vedeli, ako debugger spustiť a použiť. Detailnejšie postupy a triky si snáď ukážeme niekedy v budúcnosti.

Prvá vec, ktorú musíme urobiť, keď chceme použiť debugger, je preložiť náš kód s parametrom -g. Ten, ako už vieme, pridá debuggovací informácie, bez ktorých je debugger takmer nepoužiteľný. Pre ukážku použijeme nasledujúci kód.

#include <stdio.h>
#include <stdlib.h>

int main ()
{
  int sum = 0;
  for(int i = 0; i -= 1000; i++)
    sum += i;
  printf("Součet: %d\n", sum);
  return 0;
}

Je mi jasné, že prevažná väčšina z vás na prvý pohľad uvidí, kde je problém. Ale predpokladajme, že nie. Sme napríklad unavení alebo nastal nejaký skrat a my sme si istí, že - = je ten správny operátor. Skúsime program preložiť - žiadne chyby. Natešení teda program spustíme a čo sa nestane?

výstup programu - Linux a programovanie v jazyku C

Chvíľku na výsledok nechápavo pozeráme ... Suma čísel od 1 do 1000 predsa nie je -34394132! Pozrieme na kód, ale chybu nevidíme. Celé to prečítať a hľadať chybu sa nám nechce, takže spustíme debugger. Stačí napísať v termináli gdb soubor. GDB vypíše nejaké informácie o sebe a čaká na ďalšie príkazy.

spustenie gdb - Linux a programovanie v jazyku C

GDB môžeme ukončiť zadaním klasického "q" alebo "quit". Zadaním "help" sa nám zobrazia témy pomoci. Keď napíšeme help tému (napr. Help breakpoints), vypíšu sa nám konkrétne príkazy a ich význam. Program spustíme jednoducho napísaním "run". Keby sme to ale teraz urobili, tak len prebehne, ukáže nám výstup a návratovú hodnotu, ale nič ďalšie sa nedozvieme. Nastavíme teda breakpoint a spustíme program.

break main
run

Ako vidíte, breakpoint môžeme ľahko nastaviť na začiatok funkcie. Program pobeží, než sa dostane k Breakpoint (v našom prípade ako je zavolaná funkcie main) a tam sa zastaví a vypíše riadok, ktorý bude spracovávať. Teraz môžeme vypísať obsah premennej príkazom display. Skúsime to display sum. Vidíme, že premenná obsahuje nejakú náhodnú hodnotu - ešte sa nevykonalo priradenie nuly. Krok prevedieme príkazom n (skratka pre next). Vidíme, že sa vypísal ďalší riadok (číslo na začiatku je číslo riadka) a pod ním hodnota premennej sum.

Výpis premenné v gdb - Linux a programovanie v jazyku C

Vidíme, že príkaz display sum v skutočnosti zapína zobrazovanie premenné, nemusíme ho teda písať zakaždým znova. Vykonáme ďalší riadok - teraz by mala byť inicializovaná premenná i. Pre istotu si ju skontrolujeme (display i). Hneď vidíme problém - aj by malo byť nula, ale je -1000. Pre zaujímavosť môžeme pokračovať ďalej.

gdb – nájdenie chyby - Linux a programovanie v jazyku C

Vidíme, že suma sa počíta správne a že ki je síce s každou iterácií pripočítaná jednička, ale zároveň je odpočítaný tisíc. Vypneme debugger, chvíľu budeme nechápavo krútiť hlavou nad tým, ako sme mohli urobiť takú chybu a nakoniec ju opravíme. Hotovo, problém vyriešený. :)

GDB toho vie samozrejme oveľa oveľa viac, ale pre ukážku to stačí. Ešte uvediem niekoľko užitočných príkazov a pozrieme sa na DDD.

  • list název_fce - vypíše zdrojový kód funkcie
  • list 10,50 - vypíše riadky 10 až 50
  • print proměnná - ak chceme len raz vypísať premennú a nechceme ju hneď sledovať
  • info display - zobrazí, ktoré premenné sledujeme
  • undisplay číslo - zruší sledovanie, číslo získame pomocou info display
  • continue - program pokračuje do ďalšieho Breakpoint (ak nie je, tak do konca)

DDD

Ďalej sa krátko pozrieme na DDD (Data Display Debugger), čo je vlastne len grafická nadstavba pre debugger. Okrem GDB vie DDD pracovať s množstvom ďalších a to je tiež jedna z jeho hlavných výhod. Je ale dnes už trochu zastaraný (napríklad chýbajúca podpora UTF-8), ale stále hojne používaný a dobrá ukážka. V niektorom budúcom článku sa pozrieme na Nemiver, čo je pekná, moderná nadstavba pre GDB, určená špecificky pre GNOME. Používatelia KDE, sa môžu pozrieť napríklad na KDbg. Ďalšou alternatívou môže byť napríklad Insight.

V DDD môžeme používať rovnaké príkazy (a kľudne ich zadávať ručne), ale môžeme tiež využiť pripravené GUI. Výhoda môže byť, že neustále vidíme svoj kód a nemusíme poznať všetky príkazy naspamäť. Takto nejako vyzerá DDD po spustení (ddd program).

DDD – prehľad - Linux a programovanie v jazyku C

Tak ... Pozrieme sa na nejaké základné ovládanie. Breakpoint môžeme vytvoriť kliknutím (a držaním) pravého tlačidla na riadok.

DDD – vytvorenie Breakpoint - Linux a programovanie v jazyku C

Jednoduché, že? Všimnime si, že na riadku sa nám objavila krásna "stopka". Keď na nej klikneme (zas pravým tlačidlom), môžeme ju odstrániť, vypnúť alebo prípadne otvoriť vlastnosti (tam môžeme nastaviť napríklad podmienku - o tých ale v tomto diele hovoriť nebudeme).

DDD – vypnutie breaku - Linux a programovanie v jazyku C

Program spustíme tlačidlom run. Všimnime si, že v príkazovom riadku v spodnej časti obrazovky sa objavujú rovnaké výpisy, ako sme videli pri práci s GDB. Tiež sa objavila sympaticky zelená šípka, ktorá ukazuje na riadok, ktorý má byť vykonaný.

DDD – beh programu - Linux a programovanie v jazyku C

Ďalej nás bude určite zaujímať, ako sledovať premennú. Opäť je to jednoduché - stačí na ňu kliknúť pravým tlačidlom a vybrať si príslušný príkaz. Ja sa rozhodol, že ju chcem sledovať pomocou display. Hore sa objavila nová časť okna a v tej má premenná.

DDD – sledovanie premenné - Linux a programovanie v jazyku C

Teraz môžeme krokovať programom. Ja urobil niekoľko krokov, ale potom som si povedal, že sa mi naozaj nechce preklikávať sa celým cyklom (ktorý bude mať o veľa viac ako pôvodne plánovaných 1000 iterácií). Nastavil som si teda ďalšie breakpoint (na riadok za cyklom).

DDD – continue - Linux a programovanie v jazyku C

Teraz len klikneme na cont (continue) a program pobeží do ďalšieho Breakpoint. V sum sa nám ukázala finálna hodnota. Keď teraz vyberieme next, prebehne printf. Keď by sme vybrali znovu cont, program prebehne až do konca.

DDD – dokončenie programu - Linux a programovanie v jazyku C

A to je pre dnešok všetko. V budúcej lekcii, Céčko a Linux - Code :: Blocks , sa pozrieme na prácu v Code :: Blocks. K pokročilejším debuggovacím technikám sa vrátime v niektorom z budúcich dielov.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 35x (719 B)
Aplikácia je vrátane zdrojových kódov v jazyku C

 

Predchádzajúci článok
Céčko a Linux - Makefile
Všetky články v sekcii
Linux a programovanie v jazyku C
Preskočiť článok
(neodporúčame)
Céčko a Linux - Code :: Blocks
Článok pre vás napísal David Novák
Avatar
Užívateľské hodnotenie:
1 hlasov
Autor se zajímá především o nízkoúrovňové programování (C/C++, ASM) a návrh hardwaru (VHDL).
Aktivity