Hlavní navigace

Gdb pro normální lidi (2)

22. 10. 2003
Doba čtení: 5 minut

Sdílet

Druhý a poslední díl úvodního zasvěcení do Gdb splní sliby z minula - správa breakpointů, prohlížení proměnných, oťukávání paměti, natahování coru a samozřejmě spousta zbytečných řečí k tomu :).

Milé děti,

minule jsme nakousli gdb výpisem backtrace, dnes si přiblížíme další základní příkazy, zejména práci s proměnnými a breakpointy.

Jak jsem již uvedla posledně, program můžete za běhu v gdb zastavit buď sami stiskem Ctrl+C (což je také důvod, proč samotné gdb tímto zprofanovaným chvatem nesejmete a musíte použít příkaz quit na příkazové řádce), nebo si předem zadáte breakpoint a program se zastaví sám přesně na místě, na kterém chcete.

V obou případech získáte příkazovou řádku, ve které můžete začít kouzlit. Příkazem info breakpoints vypíšete seznam breakpointů, příkazem b nekam přidáte breakpoint a pomocí delete cislo_breakpointu daný breakpoint ze seznamu smahnete, názorně to vypadá asi takto:

(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x080791a9 in main at testgtk.c:12566
breakpoint already hit 1 time
2 breakpoint keep y 0x42029cd6 <exit+6>
(gdb) b gdkrgb.c:3049
Breakpoint 3 at 0x4005455d: file gdkrgb.c, line 3049.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x080791a9 in main at testgtk.c:12566
breakpoint already hit 1 time
2 breakpoint keep y 0x42029cd6 <exit+6>
3 breakpoint keep y 0x4005455d in gdk_rgb_select_conv at gdkrgb.c:3049
(gdb) delete 3
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y 0x080791a9 in main at testgtk.c:12566
breakpoint already hit 1 time
2 breakpoint keep y 0x42029cd6 <exit+6>
(gdb)


Pro úplnost dodávám, že gdb umí příkazy doplňovat, takže napíšete-li pouze info bre, také vám to projde. Breakpoint na jedno použití přidáte příkazem tbreak nekam. V chodu stopnutého programu můžete pokračovat nejen příkazem cont alias c, zmíněným dříve, ale lze i klasicky stepovat po řádcích kódu (step (s) se zanořuje do funkcí, next (n) nezanořuje) či nechat dojet aktuální funkci příkazem finish.

Zde můžeme nakousnout (ale dojíst už ho musíte sami :)) definování vlastních příkazů. Když do souboru .gdbinit ve vašem homu přidáte následující řádky:

define dojed
     tbreak $arg0
     continue
     end

máte k dispozici nový příkaz dojed, který, při použití dojed nekam, kde nekam má stejnou syntaxi, jako při zadávání breakpointů, nechá program dojet až na dané místo, přičemž použije temporární breakpoint.

Příkaz list alias l vám vypíše zdroják z delta okolí místa, na kterém právě program sušíte, další listy pokračují ve vypisování kódu až do konce zdrojáku (nejen na tomto místě se vám bude hodit, že prostý Enter opakuje poslední příkaz :)), l – zase vypisuje pozpátku. list s parametry může vypsat tajnosti z jiných míst, zadáváte l cislo_radky (aktuálního zdrojáku), případně l jmeno_zdrojaku­.c:cislo_radky.

Teď konečně nastoupí ta pravá akce. Příkaz p jmeno_promenne vám vypíše obsah proměnné. Není-li to proměnná, nýbrž pointer, což buď víte, nebo se dovtípíte z toho, že p vám pouze ukáže do paměti, použijete p* jmeno_pointeru. Názorná ukázka:

(gdb) p filled
$1 = 1
(gdb) p gc
$2 = (GdkGC *) 0x80b9ff0
(gdb) p* gc
$3 = {parent_instance = {g_type_instance = {g_class = 0x80b9f88}, ref_count = 1, qdata = 0x0}, clip_x_origin = 0, clip_y_origin = 0, ts_x_origin = 0, ts_y_origin = 0, colormap = 0x80b9518}
(gdb) p* gc->colormap
$4 = {parent_instance = {g_type_instance = {g_class = 0x80b94d0}, ref_count = 4, qdata = 0x0}, size = 16, colors = 0x80b95d0, visual = 0x80b93f0, windowing_data = 0x80b9538}


Pomocí příkazů up a down můžeme probublávat nahoru (zpět) po zásobníků funkcí, kterými jsme prošli, a zpět (tedy dopředu :)) a samozřejmě si tam proklepávat všechno možné (obsah proměnných, kde jsme ve zdrojáku apod.). Drsní hoši tomu prý říkají „skákání po stack framech“, hezký český ekvivalent neznám.

Máme-li podezření na to, že střílíme do paměti, která nám nepatří, vezmeme podezřelé paměťové místečko (nějaké to 0×12345678) a vyhledáme si jej v souboru /proc/cislo_pro­cesu/maps. Číslo procesu zjistíme z ps, je to proces příslušející programu, běžícímu pod gdb (tedy nikoliv gdb samotnému). Pokud v rozsazích, uvedených v souboru maps, ono paměťové místečko vůbec není, máme problém, pokud tam je, ale v rozsahu, který nemá práva rw (tudíž nepatří datům), máme také problém – v obou případech to znamená, že proměnná zřejmě není naalokovaná.

Často se nám stane, že program se chová jinak, když je spuštěn sám, a jinak v gdb, což je pochopitelné vzhledem k úplně jinému rozložení paměti. Někdy to nevadí, ale pokud program sám o sobě padá a v gdb tento pád ne a ne vyvolat, je třeba to nějak řešit. Někdy situaci změní efence, takže její preloadování zkuste vypnout/zapnout (nebo ji zkuste naopak preloadnout samotnému programu bez gdb), pokud propastný kázeňský rozdíl přetrvává, nastal čas použít core dump (nikdo vám samozřejmě nebrání ho používat i jindy místo pouštění programu v gdb, každý má jiné chutě, jak jsme se minule dočetli v diskusi).

ict ve školství 24

Core dump je obraz paměti (doplněný dalšími informacemi o procesu), který program po sobě zanechá, když vypustí duši na Segfault či po dobře mířeném zásahu některými typy signálů. Pro běžný život je nejlepší mít dumpování coru vypnuto (příkaz ulimit -c 0, na rozumných systémech default), protože core dumpy bývají dosti velké a člověk pak pracně hledá, proč zase překročil kvótu. Zapneme jej příkazem ulimit -c unlimited či ulimit -c max_velikost_v_ki­lech, doporučuji 10000. Pustíme program, vyvoláme crash a uzříme v adresáři nový vypasený soubor core čicore.číslo_pro­cesu (pokud vás tento konfigurační detail nějak drásá, můžete jej změnit zapsáním příslušné hodnoty do /proc/sys/ker­nel/core_uses_pid :)). Core pak natáhneme gdbčku příkazem gdb [–nx] jmeno_binarky jmeno_core_dumpu (parametr nx zabrání čtení souboru .gdbinit). A nyní už se můžeme chovat úplně stejně, jako kdyby nám program padl přímo v gdb.

Tím bych asi pro dnešek a s gdb i navždy :) skončila, protože přiznávám otevřeně, že víc o něm nevím, ovšem jako lehký úvod to snad stačilo (alespoň já, programátor spíše sváteční, jsem si s těmito chabými znalostmi dosud vystačila). Pokud byste někdo chtěl napsat pokračování o advanced technikách či jiných debuggerech, budete vítáni, ozvěte se buď mně, nebo jinému členovi redakce, a jistě se domluvíme (při rychlém jednání sleva na honoráři :) a lízátko zdarma!).

Autor článku