Kihagyás

Hibakeresés

gdb

Ahhoz, hogy kipróbálhassuk, hogyan is működik a gdb, tekintsük az alábbi példa kódot, amely egy szegmentációs hibát vét, és így a program végrehajtása megszakad.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//pelda.c
#include <stdio.h>

int main() {
    int *ptr = NULL;

    printf("Elérkezünk a hibához...\n");
    *ptr = 42;  // 💥 Szegmentációs hiba!

    printf("Ez a sor már nem fog lefutni.\n");
    return 0;
}

Program fordítás debug módban

A gdbhasználatához a programot debug szimbólumokkal kell lefordítani, hogy a forráskódra hivatkozhassunk.

Fordítsuk a terminálból a debuggolandó kódot a gcc pelda.c -o pelda -Wall -g utasítással.

A -g a debug ("hibakövetési") kapcsoló. Debug információk kerülnek a lefordított binárisba, így könnyebb lesz a debuggolás.

GDB elindítása és a cél program betöltése

gdb ./pelda

Ha nem -g kapcsolóval volt a program fordítva, akkor a betöltéskor kiírt logok között azt látjuk, hogy no debugging symbols found (amitől a debuggolás még elindítható, csak kevésbé lesz informatív)

Ezután a GDB interaktív módba lép, ahol különféle parancsokat adhatunk ki. Alap parancsok:

Parancs Jelentés
run Elindítja a programot, de használható arra is, hogy az aktuális debug sessiont újraindítsuk
break <függvény> vagy break <fájl>:<sor> Töréspontot (breakpoint) állít be
info breakpoints breakpontok listázása
next Következő sorra lép (függvényt átugrik)
step Következő sorra lép (függvénybe belép)
continue Futtatás folytatása a következő töréspontig
print <változó> Változó aktuális értékének kiírása
list A forráskód környezetének megjelenítése
quit Kilépés a GDB-ből
disassemble az aktuális függvény gépi kódja. Kis nyíl jelöli éppen melyik utasításnál lévő breakpointban vagyunk.
disassemble function A kért függvény gépi kódjának megnézése
disas /m funcname a C utasítások és a hozzájuk tartozó gépi utasítások együtt nézhetőek
info functions [regexp] Opcionálisan megadható egy reguláris kifejezés a találatok szűrésére

Breakpointok kezelése

  • bekapcsolás, kikapcsolás, törlés

    • disable N: adott sorszámú breakpoint kikapcsolása (dis N)

    • enable N: adott sorszámú breakpoint visszakapcsolása (en N)

    • clear function, clear linenum, clear filename:linenum breakpoint törléshez

    • delete: az összes breakpoint letörlése

  • Következő sor végrehajtása

    • step (s)

    • next (n): a különbség, hogy nem lép bele függvényhívásokba

    • s N vagy n N: egyszerre N darab sort lépünk

    • finish: az aktuális függvényből való visszatérés helyére ugrik. Ha volt visszatérési érték, akkor kiírja.

  • Következő gépi utasítás végrehajtása (ezekkel a parancsokkal assembly utasítás szintén tudunk lépdelni a kódban)

    • stepi (si)

    • nexti (ni): a különbség, hogy nem lép bele függvényhívásokba

  • Breakpoint utáni újraindítás

    • continue (c)

    • c N: ahol N megadja, hányszor ne álljon meg a gdb az aktuális breakpointnál

  • Backtrace (Breakpointnál vagy hiba (pl segmentation fault) esetén megnézhetjük hogyan jutott el a program az adott (hibás) sor lefutásához.)

    • backtrace (bt): az aktuális stack frame-től kezdve visszafelé haladva kiírja az összes frame-et, ami a stack-en van. Ha túl sokáig tart CTRL+C-vel le lehet állítani a kiíratást.

    • bt n: csak az utolsó n darab frame-t írja ki

    • bt -n: csak az első n darab frame-t írja ki

    • bt full: a frame-k lokális változóit is kiíratja. Kombinálható az előző két utasítással.

Információk kiíratása

  • info registers

  • layout regs

  • info variables: globális és statikus változók értékei
  • info locals: lokális változók értékei
  • print symbol: adott szimbólum értéke
    • röviden: p symbol
    • p valtozonev
    • p fuggveny::valtozonev
    • p $eax
  • info address symbol: adott szimbólum memóriacíme
  • info frame: az aktuális frame adatai (argumentumok, lokális változók, ebp, eip értékei)
  • x 0x080485e9: az x utasítással egy konkrét memóriacím tartalmát nézhetjük meg
    • x/16 address: 16 érték legyen megjelenítve az addresstől kezdve (növekvő memóriacímek irányába)
    • x/-16 address: ugyanez, csak a 16 érték csökkenő memóriacímek irányába megy (gdb 8-tól működik)
    • Kiíratási módosítók megadása is lehetséges:
      • x/16x address: 16 hexadecimális érték kiíratása
      • x/16xg address: 16 db 64 bites hexadecimális érték kiíratása
      • x/16c address: 16 karakter kiíratása
      • x/s address: C sztringként értelmezi az adott memóriaterületet
      • x/d address: előjeles, decimális értékként való megjelenítés
      • x/i address: utasításként próbálja értelmezni a memóriaterületet
      • Verem tetjének megtekintése: x/20x $esp

Változáskövetés

  • watch symbol: változó, regiszter, memóriacím (vagy komplexebb kifejezés) értékének a váltazása esetén álljon meg a program futása (mint egy breakpoint érték változása alapján). Többszálú program esetén minden szálon van változásfigyelés.
  • info watchpoints: listázásuk
  • rwatch symbol: megállítja a program futását, ha a szimbólumot olvasták
  • awatch symbol: megállítja a program futását, ha a szimbólumot írták vagy olvasták

Feltételes breakpointok elhelyezése

Használhatjuk a célnyelv utasításait, szimbólumait, hogy a breakpointokat feltételekhez kössük! Meglévő breakpointhoz is tudunk az azonosító száma alapján utólag feltételt rakni (pl. info breakpoints-al kideríthető)

  • break file.c:20 if i == 112: breakpoint a 20-as soron, ha az i 112
  • break file.c:17 if strcmp(input, "password") == 0: breakpoint a 17-es soron, ha a string értéke password
  • cond 8 *p == 78: új feltétel a 8-as breakpointhoz
  • cond 8: a 8-as breakpoint feltételének törlése

Függvényhívás

Lehetőségünk van kézzel meghívni a célprogram valamely függvényét (pl. ha direkt írtunk egyet debug céllal) Paramétereket is adhatunk meg neki.

  • call segedfgv()

.gdbinit használata

GDB konfigurációs fájl, mely a gdb indításakor automatikusan betöltődik. Segítségével lehet kicsit automatizálni a debuggolást. Helyei lehetnek:

  • /etc/gdbinit: rendszerszintű beállítások
  • ~/.gdbinit: user szintű beállítások
  • ./.gdbinit: csak az adott munkakönyvtárból futó gdb-re vonatkozik. A lokális gdbinit betöltése ki lehet kapcsolva, ekkor a megjelenő hibaüzenetek alapján lehet megoldást keresni (hozzáadni a biztonsági kivételt vagy teljesen kikapcsolni ezt az ellenőrzést)