Uso del debugger

Quando un programma che apparentemente è tutto giusto, come questo, che può essere compilato senza batter ciglio, ha qualcosa che non funziona:

> ./ex1_bug
Segmentation fault

il debugger (gdb sui sistemi con GNU, dbx sugli altri) è di prezioso aiuto. Permette di capire cosa non funziona mentre il programma sta girando, ispezionare (ed eventualmente modificare) il contenuto delle variabili e dei registri. Ha anche molte altre funzioni sulle quali non possiamo dilungarci.

Ecco un esempio di sessione di gdb applicata al programma indicato sopra.

> gdb ./ex1_bug
GNU gdb 4.17
Copyright 1998 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 "i586-pc-linux-gnu"...
(gdb) run
Starting program: /home/prelz/doc/clang/l18/./ex1_bug 

Program received signal SIGSEGV, Segmentation fault.
0x4008b657 in memcpy ()
(gdb) where
#0  0x4008b657 in memcpy ()
#1  0x8048a17 in determinant (matrix=0xbffffa8c, n=6, do_pivot=0)
    at ex1_bug.c:106
#2  0x80487e6 in main (argc=1, argv=0xbffffc2c) at ex1_bug.c:60
#3  0x4004ea42 in __libc_start_main ()
(gdb) up
#1  0x8048a17 in determinant (matrix=0xbffffa8c, n=6, do_pivot=0)
    at ex1_bug.c:106
106          memcpy(m[i],matrix[i],n * sizeof(double));
(gdb) l
101            free(swap);
102            for(j=0;j<i;j++) free(m[j]);
103            free(m);
104            return(-1);
105           }
106          memcpy(m[i],matrix[i],n * sizeof(double));
107         }
108
109        for (i=0;i<n;i++) 
110         {
(gdb) print i
$1 = 6
(gdb) print m[i]
$2 = (double *) 0x804a4b8
(gdb) print matrix[i]
$3 = (double *) 0xc2ea0c9e
(gdb) l 90
85         double mul_factor;
86
87         swap = (double *)malloc(n * sizeof(double));
88         if (swap == NULL) return (-1);
89         m = (double **)malloc(n * sizeof(double *));
90         if (m == NULL)
91          {
92           free(swap);
93           return(-1);
94          }
(gdb) l
95
96         for (i=0;i<=n;i++)
97          {
98           m[i] = (double *)malloc(n * sizeof(double));
99           if (m[i] == NULL)
100           {
101            free(swap);
102            for(j=0;j<i;j++) free(m[j]);
103            free(m);
104            return(-1);
(gdb) l
105           }
106          memcpy(m[i],matrix[i],n * sizeof(double));
107         }
108
109        for (i=0;i<n;i++) 
110         {
111          if (do_pivot)
112           {
113            /* Find a good pivot element in the lower right (n-i)x(n-i) */
114            /* minor to put in the upper left corner. */
(gdb)

Quando un programma muore, e produce una copia della sua memoria virtuale su disco (il cosiddetto core), è sempre il debugger che permette di analizzare il core come se il programma fosse appena terminato:

> gdb ./ex1_bug core

Se un programma è bloccato in un ciclo infinito, eseguendo con il debugger ed interrompendo con Ctrl-C appare il prompt del debugger, che permette di eseguire il programma passo-passo con i comandi next (salta alla prossima istruzione senza scendere nelle funzioni chiamate) e step (salta alla prossima istruzione, eventualmente nella funzione che viene chiamata). Il comando continue permette di riprendere l'esecuzione. Tutti i comandi del debugger possono essere abbreviati (ad esempio c al posto di continue).

Molto utile anche il comando

(gdb) break nome della funzione/numero di riga

che predispone un'interruzione quando il programma incontra la funzione o il numero di riga specificato. Così si crea, si visualizza e si rimuove un'interruzione programmata (questi comandi funzionano solo per gdb e non per dbx):

(gdb) break 125
Breakpoint 1 at 0x8048aaf: file ex1_bug.c, line 125.
(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x08048aaf in determinant at ex1_bug.c:125
(gdb) del 1
(gdb) info break
No breakpoints or watchpoints.
(gdb)

Per riprendere l'esecuzione di un programma dopo un'interruzione si usa il comando

(gdb) continue

(che si può anche abbreviare con c).
Esistono infine anche interfacce grafiche al debugger, come ddd o dde, che in qualche occasione possono risultare più pratiche.