Debug con gdb

Il debugger GNU (gdb) consente di esaminare gli interni di un altro programma mentre il programma viene eseguito o retrospettivamente per vedere cosa stava facendo un programma nel momento in cui si è bloccato.

gdb consente di esaminare e controllare l'esecuzione del codice ed è utile per valutare le cause degli arresti anomali o del comportamento generale non corretto. gdb non gestisce i processi Java™ , quindi è di uso limitato su un programma Java puro. È utile per il debug delle librerie native e della JVM stessa.

In esecuzione gdb

È possibile eseguire gdb in tre modi:

Avvio di un programma
Di solito, il comando gdb <application> viene utilizzato per avviare un programma sotto il controllo di gdb. Tuttavia, a causa del modo in cui viene avviato Java, è necessario avviare gdb impostando una variabile di ambiente e richiamando Java:
export IBM_JVM_DEBUG_PROG=gdb
java
Si riceve quindi un prompt gdb e si forniscono il comando di esecuzione e gli argomenti Java:
r <java_arguments>
Collegamento a un programma in esecuzione
Se un programma Java è già in esecuzione, è possibile controllarlo in gdb. L'ID processo del programma in esecuzione è richiesto e quindi gdb viene avviato con l'applicazione Java come primo argomento e l'ID processo come secondo argomento:
gdb <Java Executable> <PID>

Quando gdb è collegato ad un programma in esecuzione, questo programma viene arrestato e la relativa posizione nel codice viene visualizzata per il visualizzatore. Il programma è quindi sotto il controllo di gdb ed è possibile iniziare a immettere comandi per impostare e visualizzare le variabili e generalmente controllare l'esecuzione del codice.

Esecuzione su un dump di sistema (file core)
Un dump di sistema viene in genere prodotto quando un programma viene arrestato in modo anomalo. gdb può essere eseguito su questo dump di sistema. Il dump di sistema contiene lo stato del programma quando si è verificato l'arresto anomalo. Utilizzare gdb per esaminare i valori di tutte le variabili e i registri che portano a un arresto anomalo. Queste informazioni aiutano a scoprire cosa ha causato l'arresto anomalo. Per eseguire il debug di un dump di sistema, avviare gdb con il file dell'applicazione Java come primo argomento e il nome del dump di sistema come secondo argomento:
gdb <Java Executable> <system dump>

Quando si esegue gdb su un dump di sistema, inizialmente vengono visualizzate informazioni quali il segnale di terminazione ricevuto dal programma, la funzione che era in esecuzione al momento e anche la riga di codice che ha generato l'errore.

Quando un programma è sotto controllo di gdb, viene visualizzato un messaggio di benvenuto seguito da un prompt (gdb). Il programma è ora in attesa di immettere le istruzioni. Per ogni istruzione, il programma continua in qualsiasi modo tu scelga.

Impostazione dei punti di interruzione e dei punti di controllo

I punti di interruzione possono essere impostati per una particolare riga o funzione utilizzando il comando:

break linenumber

oppure

break functionName

Una volta impostato un punto di interruzione, utilizzare il comando continue per consentire l'esecuzione del programma fino a quando non raggiunge un punto di interruzione.

Impostare i punti di interruzione utilizzando le condizioni in modo che il programma venga arrestato solo quando viene raggiunta la condizione specificata. Ad esempio, l'utilizzo di breakpoint 39 if var == value provoca l'arresto del programma quando raggiunge la riga 39, ma solo se la variabile è uguale al valore specificato.

Se si desidera sapere dove e quando una variabile è diventata un determinato valore, è possibile utilizzare un punto di controllo. Impostare il punto di controllo quando la variabile in questione è nell'ambito. Dopo aver fatto ciò, si verrà avvisati ogni volta che questa variabile raggiunge il valore specificato. La sintassi del comando è: watch var == value.

Per visualizzare i punti di interruzione e i punti di controllo impostati, utilizzare il comando info :

info break
info watch
Quando gdb raggiunge un punto di interruzione o un punto di controllo, stampa la riga di codice che viene successivamente impostata per l'esecuzione. L'impostazione di un punto di interruzione alla riga 8 causerà l'arresto del programma dopo il completamento dell'esecuzione della riga 7 ma prima dell'esecuzione della riga 8. Oltre ai punti di interruzione e ai punti di controllo, il programma si arresta anche quando riceve determinati segnali di sistema. Utilizzando i comandi riportati di seguito, è possibile arrestare lo strumento di debug ogni volta che riceve questi segnali di sistema:
handle sig32 pass nostop noprint 
handle sigusr2 pass nostop noprint 

Esame del codice

Una volta raggiunta la posizione corretta del codice, esistono diversi modi per esaminare il codice. Il più utile è backtrace (abbreviato in bt), che mostra lo stack di chiamata. Lo stack di chiamata è la raccolta di frame di funzione, dove ogni frame di funzione contiene informazioni quali parametri di funzione e variabili locali. Questi frame di funzione vengono posizionati sullo stack di chiamata nell'ordine in cui vengono eseguiti. Ciò significa che la funzione richiamata più di recente viene visualizzata all'inizio dello stack di chiamata. È possibile seguire la traccia di esecuzione di un programma esaminando lo stack di chiamata. Quando viene visualizzato lo stack di chiamata, viene visualizzato un numero di frame all'inizio della linea, seguito dall'indirizzo della funzione chiamante, seguito dal nome della funzione e dal file origine per la funzione. Ad esempio:
#6 0x804c4d8 in myFunction () at myApplication.c

Per visualizzare informazioni più dettagliate su un frame di funzione, utilizzare il comando frame insieme a un parametro che specifica il numero di frame. Una volta selezionata una cornice, è possibile visualizzarne le variabili utilizzando il comando print var.

Utilizzare il comando print per cambiare il valore di una variabile; ad esempio, print var = newValue.

Il comando info locals visualizza i valori di tutte le variabili locali nella funzione selezionata.

Per seguire la sequenza esatta di esecuzione del programma, utilizzare i comandi step e next . Entrambi i comandi utilizzano un parametro facoltativo che specifica il numero di righe da eseguire. Tuttavia, next considera le chiamate di funzione come una singola linea di esecuzione, mentre step procede attraverso ogni linea della funzione richiamata, un passo alla volta.

Comandi utili

Una volta terminato il debug del codice, il comando run fa sì che il programma venga eseguito fino alla sua fine o al suo punto di arresto anomalo. Il comando quit viene utilizzato per uscire da gdb.

Altri comandi utili sono:

ptype
Stampa il tipo di dati della variabile.
info share
Stampa i nomi delle librerie condivise attualmente caricate.
info functions
Stampa tutti i prototipi di funzione.
list
Mostra le 10 righe di codice sorgente intorno alla riga corrente.
help
Visualizza un elenco di argomenti, ognuno dei quali può essere richiamato dal comando di aiuto, per visualizzare la guida dettagliata su tale argomento.