Il debugger è un particolare software utilizzato per analizzare il comportamento di un altro programma allo scopo di individuare ed eliminare eventuali errori. In informatica si parla di “bug“, parola da cui deriva debug che significa letteralmente “eliminare gli errori”. Il debugger permette di effettuare le seguenti operazioni:
- eseguire un’istruzione del programma per volta
- ispezionare e modificare il valore delle variabili
- far bloccare il programma quando arriva su determinate righe del codice sorgente
- controllare il flusso del programma e quindi l’ordine con cui sono chiamate le funzioni
- analizzare un file “core“
Gli errori trovati possono essere suddivisi in due categorie:
- quelli per cui il programma non viene eseguito come ci si aspetta
- quelli che bloccano il programma mandandolo in crash
Piuttosto che inserire codice di output tra le righe del sorgente per scovare l’errore si preferisce utilizzare un debugger. In questo articolo tratteremo del debugger della GNU disponibile per quasi tutti i principali sistemi Unix e Linux, si tratta del GDB ( GNU Debugger ).
Per illustrare il funzionamento del debugger GNU, useremo un piccolo programma in C che non fa altro che sommare il valore di due variabili e stampare il valore risultante, chiamiamo il nostro file test.c.
#include <stdio.h> void main() { int x, y, t; x = 12; y = 46; t = x + y; printf("Il valore della somma di %d e %d è di %d\n", x, y, t); }
Ora compiliamo il programma con il compilatore che abbiamo a disposizione, il gcc, di cui abbiamo trattato in uno scorso articolo. Affinché il debugger possa funzionare, occorre compilare con l’opzione -g del compilatore in modo tale da avere i simboli ed altre informazioni utili nell’eseguibile, che difatti sarà leggermente più grande.
gcc -g test.c -o test
Per avviare il debugger con il nostro programma di esempio da analizzare diamo il comando:
gdb test
a questo punto si entra nella shell del debugger dove possiamo inserire i comandi; la numerosa serie di comandi può essere elencata con help, specificando il sotto argomento da visualizzare. Al prompt è possibile utilizzare i tasti cursore per scorrere la history di tutti i comandi inseriti, il tasto tab per avere a disposizione la funzione di completamento automatico quando si digita il nome di un comando, variabile o funzione. La pressione del tasto Invio sulla riga vuota causerà la ripetizione del comando precedente. Il programma viene eseguito con il comando run, seguito dagli argomenti che intendiamo passare, però in questo modo l’esecuzione del programma avverrà senza interruzioni non dandoci la possibilità di analizzare i valori delle variabili in determinati punti del sorgente. Per eseguirlo passo passo occorre inserire un breakpoint, ovvero un particolare punto del programma che, quando incontrato dal gdb, mette in pausa l’esecuzione del programma consentendo di inserire nuovamente comandi dalla command line. Un breakpoint può essere specificato mediante il comando breakpoint, break o semplicemente usando la lettera “b“, seguito dal numero di riga del programma o dal nome di una funzione. Impostiamo un breakpoint sulla funzione main.
(gdb) break main
a questo punto possiamo lanciare il programma
(gdb) run test
il debugger si blocca nell’esecuzione del programma appena entrato nella funzione da noi indicata mostrando gli argomenti e la prossima riga del programma che verrà eseguita. A questo punto il debugger è fermo in attesa di un nostro comando, che può essere:
- n, next – per avanzare alla successiva riga del programma senza entrare nelle sotto-funzioni
- s, step – come next, ma entrando nelle sotto-funzioni
- p, print – per visualizzare o assegnare il contenuto di una variabile
- l, list – per visualizzare il codice sorgente del programma sotto esame
- finish – per completare l’esecuzione del programma sino alla fine della funzione corrente
- c, continue – per continuare l’esecuzione del programma fino al prossimo breakpoint
- quit – per uscire dal debugger
diamo quindi il comando next ed avanziamo di una riga di codice. Possiamo visualizzare il valore delle singole variabili con print ed il nome della variabile stessa.
Per visualizzare la lista di tutti i breakpoint inseriti si utilizza il comando info break, mentre per cancellare un breakpoint si utilizza il comando delete seguito dal numero del breakpoint. Un altro comando che torna utile utilizzare è display, che consente di visualizzare il valore delle variabili man mano che procediamo nel debug dell’applicazione. Ora possiamo uscire dal debugger con quit.