GDB и начини уклањања грешака

Аутор: Вељко Симић

Током кодирања, програмер може да направи две врсте грешака: синтаксне и семантичке. У великом програму грешке су неизбежне, синтаксне грешке пријављује преводилац и лакше су за отклањање од семантичких (логичких) грешака, због којих програми могу да дају погрешне резултате, да се уплету у бесконачне петље или да „пукну” због недозвољених операција над меморијом. Проналажење и елеминисање логичких грешака може да се одради на два начина:

  1. Ишчитавањем кôда неколико стотина пута и лупањем главом о зид.
  2. Коришћењем неког програма за откривање грешака (debugger).

Ако је ваш коначан одговор „под 2”, овај текст је писан баш за вас. У наставку текста биће описана инсталација и једноставан пример рада GNU-ове алатке GDB.

Инсталација програма GDB

Уколико је на вашем систему доступан GNU преводилац за језик C (GCC), врло вероватно да је и GDB већ инсталиран. То ћете утврдити наредном командом, која би требало да испише податак о верзији и информације о ауторским правима GDB-а.

gdb -version

Ако је GDB инсталиран исписује се порука слична следећој:

GNU gdb (GDB) 7.6.2
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
<http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and    redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show
copying"
and "show warranty" for details.
This GDB was configured as "x86_64-unknown-linux-gnu".

Уколико GDB није инсталиран, можете преузети изворни кôд са web-а (http://www.gnu.org/software/gdb/download/) и инсталирати га, мада Јуниксолики системи омогућавају погодније решење – инсталирање бинарног пакета. Ако користите Windows, препоручујемо вам да инсталирате софтвер Cygwin. Cygwin обезбеђује стандардно Unix окружење на Windows платформама, где спадају GCC, GDB и друге GNU алатке (http://www.cygwin.com/)

Пример рада GDB-а

Да бисмо вам објаснили рад GDB-а, искористићемо вероватно свима добро познату функцију замене вредности двеју променљивих „swap”.

#include <stdio.h>

void swap (int *p1, int *p2);

int main (){
        int a=10, b=20;
        printf ("Stare vrednosti: a=%d, b=%d.\n",a,b);
        swap(&a,&b);
        printf ("Nove vrednosti: a=%d, b=%d.\n",a,b);
        return 0;
}

void swap (int *p1, int *p2){
        int *tmp=p1;
        p1=p2;
        p2=tmp;
}

Као што видите, програм не ради оно што бисмо ми желели, већ исписује следећи резултат:

Vrednosti promenljivih su: 10 20
Vrednosti zamenjenih promenljivih su: 10 20

Откривање грешака почињемо покретањем програма GDB из командног окружења, наводећи име извршне датотеке као аргумент командне линије за GDB.

gdb ./a.out
GNU gdb (GDB) 7.6.2
Copyright (C) 2013 Free Software Foundation, Inc.
...
Reading symbols from /~/a.out...done.''

GDB исписује (gdb) на почетку новог реда тражећи да унесете команду за откривање грешака. Можете почети командом list, или само њеним првим словом, l, да бисте приказали неколико редова извршног кôда програма у коме откривате грешке. Програм подразумева 10 редова изворне датотеке.

     
      (gdb) l
      1	#include <stdio.h>
      2	
      3	void swap (int *a, int *b){
      4		int *tmp=a;
      5		a=b;
      6		b=tmp;
      7	}
      8	
      9	int main () {
      10		int a=10, b=20;
      (gdb)

Пре него што наложите програму GDB да изврши програм, морате му навести жељену тачку прекида. Када програм дође до тачке прекида, он се зауставља и омогућава вам да погледате тренутно стање програма на том месту, као и наставак извршавања програма ред по ред, пратећи промену стања програма.

Тачку прекида дефинишете помоћу команде break, или b, скраћено задавајући јој број линије изворног кôда у којој желите да зауставите програм.

(gdb) b 8
Breakpoint 1, main () at swap.c:8
8		swap(&a,&b);

Команда run, или r покреће програм:

(gdb) run
Starting program: /home/wex/./a.out 
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Stare vrednosti: a=10, b=20.

Breakpoint 1, main () at swap.c:8
8		swap(&a,&b);

Када наиђе на тачку прекида, програм за откривање грешака прекида испитивање програма и приказује ред са наредбом која се следећа извршава. Пошто претпостављамо да је грешка у функцији swap(), желимо да је извршимо корак по корак. Програм GDB то омогућава помоћу команди next, или n, и step, или s. Иако обе наредбе извршавају програм ред по ред, између њих постоји разлика. Наредба next извршава наредни ред, укључујући све позиве функција, и поново зауставља програм у следећем реду. Команда step извршава скок на позвану функцију и прекида програм на првој наредби у телу функције. У нашем примеру, команда step води до прве наредбе у функцији swap().

(gdb) s
swap (p1=0x7fffffffe99c, p2=0x7fffffffe998) at swap.c:14
14		int *tmp=p1;

GDB омогућава и приказ вредности променљивих помоћу функције print, или p.

(gdb) p *p1
$1 = 10
(gdb) p *p2
$2 = 20

Као што видимо, почетне вредности су у реду. Командом next ћемо извршавати ред по ред функције.

(gdb) n
15		p1=p2;
(gdb) n
16		p2=tmp;
(gdb) n
17	}

Сада ћемо поново проверити вредности променљивих

(gdb) p *p1
$3 = 20
(gdb) p *p2
$4 = 10

Напомена: Командом print можемо да видимо само вредности локалних променљивих.

Сада p1 има вредност 20, а p2 има вредност 10, што се чини исправним. Програм можете да наставите да прегледате помоћу још две команде print:

(gdb) p p1
$5 = (int *) 0x7fffffffe998
(gdb) p p2
$6 = (int *) 0x7fffffffe99c

Обратите пажњу када смо извршили команду step на вредности p1 и p2. (p1=0x7fffffffe99c, p2=0x7fffffffe998) Као што можете да приметите, замењене су вредности показивача p1 и p2, а не садржаји меморијских локација p1 и p2. То је била грешка у функцији swap(). Функцију морамо изменити да замењује целобројне вредности p1 и p2, уместо вредности показивача у променљивама p1 и p2. Исправна верзија изгледа овако:

void swap (int *p1, int *p2){
        int tmp=*p1;
        *p1=*p2;
        *p2=tmp;
}

Команда continue, или c, омогућава извршавање програма до наредне тачке прекида или до свог краја.

(gdb) c
Continuing.
Nove vrednosti: a=10, b=20.
[Inferior 1 (process 1253) exited normally]

Излазак из програма GDB се врши командом quit, или q.

(gdb)quit
$

Пословица „На туђим грешкама се учи” је пуста лаж; човек најбоље учи на својим грешкама. Зато правите грешке и исправљајте их, али нека то буду искључиво програмске грешке.

Оставите одговор

Ваша адреса е-поште неће бити објављена. Неопходна поља су означена *

Time limit is exhausted. Please reload CAPTCHA.