next next
Next: Wstęp do UML Previous: Narzędzia strace i ltrace

Szybkie zapoznanie z GDB

Jak odnaleźć błąd w naszym programie korzystającym ze złośliwej funkcji kwadrat?

Otóż z pomocą przychodzi GNU Debugger. Opiszmy krótko jego możliwości na przykładzie naszego problemu.

W celu odpluskwienia programów musimy je najpierw skompilować w gcc z opcją -g, a dopiero później uruchomić gdb na danym programie.

login@Kubuntu:~$ gcc -g -o test1 test1.c kwadrat.o login@Kubuntu:~$ gdb test1 GNU gdb 6.4-debian Copyright 2005 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i486-linux-gnu"...Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

Najważniejsze polecenia w GDB:

break [file:]function ustaw znak przerwania na funkcji
run [arglist] rozpocznij program (ten w argumencie wywołania gdb) z podanymi parametrami
bt wyświetl stos programu (przydatne w śledzeniu zagnieżdżonych wywołań funkcji)
print expr wyświetl wartość wyrażenia (najczęściej po prostu zmiennej)
Uwaga: print x=5 jest przypisaniem
c wznów działanie programu po zatrzymaniu, np. w przypadku znaku przerwania
next wykonaj całą linijkę kodu w jednym kroku (w razie wywołania funkcji wykonuje ją na raz)
list wyświetl kod funkcji w danym kontekście, w którym się znajdujemy (np. list main)
step wykonaj krok w kodzie (najczęściej używana komenda)
help [name] wyświetl opis danej komendy
quit zakończ działanie GDB

Przejdźmy do programu test1:

(gdb) break main Breakpoint 1 at 0x80483b0: file test1.c, line 7. (gdb) run Starting program: /home/login/test1 Breakpoint 1, main () at test1.c:7 7 scanf("%d",&n); (gdb) step 5 8 for (i = 0; i < n; i++) (gdb) step 10 x = kwadrat(i); (gdb) print i $1 = 0 (gdb) step kwadrat (x=0x0) at kwadrat.c:5 5 (*x) *= (*x); (gdb) bt #0 kwadrat (x=0x0) at kwadrat.c:5 #1 0x080483d7 in main () at test1.c:10 (gdb) print x $2 = (int *) 0x0 (gdb) print *x Cannot access memory at address 0x0 (gdb) step Program received signal SIGSEGV, Segmentation fault. 0x08048402 in kwadrat (x=0x0) at kwadrat.c:5 5 (*x) *= (*x); (gdb) c Continuing. Program terminated with signal SIGSEGV, Segmentation fault. The program no longer exists.

Czas na program test2:

(gdb) list main 1 #include < stdio.h> 2 #include "kwadrat.h" 3 4 int main() 5 { 6 int i, x, n; 7 scanf("%d",&n); 8 for (i = 0; i < n; i++) 9 { 10 x = kwadrat(&i); (gdb) break 11 Breakpoint 1 at 0x80483da: file test2.c, line 11. (gdb) run Starting program: /home/login/test2 5 Breakpoint 1, main () at test2.c:11 11 printf("%d\n",x); (gdb) print i $1 = 0 (gdb) print x $2 = 0 (gdb) c Continuing. 0 Breakpoint 1, main () at test2.c:11 11 printf("%d\n",x); (gdb) print i $3 = 1 (gdb) print x $4 = 1 (gdb) c Continuing. 1 Breakpoint 1, main () at test2.c:11 11 printf("%d\n",x); (gdb) print i $5 = 4 (gdb) print x $6 = 4 (gdb) print i=2 $7 = 2 (gdb) c Continuing. 4 Breakpoint 1, main () at test2.c:11 11 printf("%d\n",x); (gdb) print i $8 = 9 (gdb) print x $9 = 9

Uwaga: W przypadku używania gdb do odpluskwiania programu, warto pisać instrukcje nieco bardziej ,,obszernie'' niż byśmy to robili normalnie. To bardzo ułatwia późniejsze śledzenie programu po faktycznie ,,małych'' krokach. Przykładowo napisanie printf("%d\n",kwadrat(i)) uniemożliwiłoby łatwe dostanie się do wartości zwracanej przez funkcję kwadrat(). Dlatego też rozpisałem tę instrukcję na dwie.