Introduzione

GNU Debugger (GDB) è un'applicazione di debugging, cioè uno strumento che consente di controllare un programma in esecuzione per scovare e rimuovere bug. GDB può analizzare programmi scritti in diversi linguaggi. In questa guida verrà spiegato come eseguire il debugging di un programma scritto in C, ma la procedura è analoga anche in altri casi. Inoltre verrà mostrata la procedura per realizzare un backtrace, sempre facendo uso di GDB, utile nella segnalazione dei bug dei programmi in Launchpad.

Preparativi

GDB è preinstallato in modo predefinito in Ubuntu. Prima di avviare l'applicazione è necessario compilare in modo speciale il file sorgente che si vuole analizzare:

gcc -ggdb prova.c -o prova

se per esempio il file sorgente è stato chiamato prova.c.
Con il comando precedente il programma è stato compilato in modo da poter lavorare con GDB sull'eseguibile appena creato.

Eseguire il debugging di un programma

  1. Per avviare il debugger basta aprire un terminale e dare:
    gdb
    L'interfaccia utente che verrà mostrata dal terminale è la seguente:
    GNU gdb 6.8-debian
    Copyright (C) 2008 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 "i486-linux-gnu".
    (gdb)
  2. Ora si deve specificare a GDB l'eseguibile su cui lavorare:

    file prova

    sempre riprendendo l'esempio precedente.
    Se tutto va bene verrà restituito un messaggio del genere:

    Reading symbols from /home/.../prova...done.
  3. A questo punto occorre impostare gli eventuali argomenti che di solito vengono passati al programma da analizzare quando lo si esegue. Se ad esempio per eseguire il programma si usa:
    ./prova arg1 arg2 arg3

    con arg1, arg2 e arg3 argomenti del programma, in GDB si dovrà digitare:

    set args arg1 arg2 arg3
    Se invece non vengono usati argomenti è sufficiente digitare:
    set args
  4. Adesso è indispensabile comunicare al debugger almeno un breakpoint, cioè una riga di codice o una funzione nei pressi della quale fermare l'esecuzione del programma.

    In questo modo, una volta interrotta l'esecuzione del programma, sarà possibile eseguirlo riga per riga.

    Per inserire un breakpoint in corrispondenza della funzione main basta digitare:

    break main
    Se invece lo si vuole mettere ad una riga ben precisa, si usa la seguente sintassi:
    break <numero_riga>

    dove a <numero_riga> ovviamente va sostituito il numero di riga corrispondente al punto dove si vuole mettere il breakpoint.

    Se il programma è composto da diversi file compilati insieme, occorre specificare anche il nome del file a cui appartiene la riga destinataria del breakpoint. Si fa ciò semplicemente separando il nome del file dal numero di riga attraverso l'uso dei due punti (:). Qui un esempio:

    break prova_1.c:<numero_riga>

    in cui prova_1.c è uno dei file facenti parte del programma.

    Si possono specificare tanti breakpoint quanti se ne desidera.

    Se tutto va bene dovrebbe apparire sullo schermo un messaggio del tipo:
    Breakpoint 1 at 0x8048365: file prova_1.c, line 5.

    Il numero che segue la parola Breakpoint (in questo caso 1) identifica in maniera univoca il breakpoint nel programma. Per rimuovere un breakpoint basta digitare:

    delete break <numero_breakpoint>

    dove al posto di <numero_breakpoint> occorre inserire il numero di breakpoint precedentemente assegnato.

  5. Da questo momento in poi è possibile iniziare l'esecuzione vera e propria del programma. Per farlo basta utilizzare il seguente comando:
    run

    Così il debugger eseguirà il programma dalla prima riga in poi, fermandosi al primo breakpoint che incontra.
    Come prima, se il programma riceve argomenti dalla riga di comando, è necessario specificarli:

    run arg1 arg2 arg3
    riprendendo in questo caso l'esempio di prima.
  6. A questo punto si è fermi ad un breakpoint. Il debugger mostra una riga di codice, la quale non è stata ancora eseguita. Tuttavia si possono eseguire alcune operazioni. Per stampare sullo schermo il valore di una variabile digitare:
    print <nome_var>
    oppure:
    p <nome_var>

    dove <nome_var> dev'essere sostituito con il nome corrispondente della variabile da visualizzare.
    Per vedere invece una porzione di codice prima e dopo quella in cui ci si trova attualmemte dare:

    list
    oppure:
    l
    In alternativa è possibile impostare a mano il valore di una variabile:
    set var <nome_var>=<valore>

    specificando il nome della variabile e un valore da assegnare al posto della stringa <valore>.

  7. Si può andare avanti spostandosi alla riga di codice successiva digitando:
    next
    oppure:
    n

    Questo comando permette di saltare alla riga successiva della porzione di codice che si sta analizzando. Se la riga in cui ci si trova attualmente è la chiamata ad una funzione, con next non si entra dentro la funzione ma si passa direttamente alla riga elaborata dopo la sua esecuzione.
    Se invece si volesse entrare all'interno della funzione basterà digitare:

    step
    oppure:
    s
    Se si desidera mostrare costantemente sullo schermo il valore di alcune variabili basta ripetere questo comando per ogni variabile:
    display <nome_var>

    sostituendo come sempre a <nome_var> il nome della variabile da visualizzare.
    Il risultato sarà una riga del genere:

    1: <nome_var> = -1208249712

    dove il numero che precede i due punti (:) è l'identificativo di visualizzazione.

    Per rimuovere la variabile dalla visualizzazione costante è sufficiente utilizzare il comando delete:

    delete disp <numero_id>

    dove <numero_id> è l'identificatore della variabile visualizzata.
    Qualora si voglia invece che il programma continui a lavorare fino al breakpoint successivo, digitare:

    continue
    oppure:
    c
  8. Infine, per uscire dal debugger, digitare:
    quit
    oppure:
    q

Se si vogliono avere maggiori informazioni su un determinato comando si può usare l'istruzione:

help <nome_comando>

sostituendo <nome_comando> con il nome del comando a riguardo del quale si desidera ottenere ulteriori informazioni.

Usare GDB per generare backtrace

Oltre che per eseguire il debugging di un programma, si può sfruttare il GNU Debugger per aiutare gli sviluppatori software a correggere alcuni bug che causano crash alle applicazioni. Infatti Ubuntu fa uso della piattaforma messa a disposizione su Launchpad per la segnalazione dei bug riscontrati dagli utenti.
Quando un utente apre un'issue su Launchpad descrivendo la situazione relativa al crash, può essere utile allegare un file di testo chiamato «backtrace». In sostanza esso è un report che consente di analizzare il flusso di esecuzione per un'applicazione e di capire a che punto si verifica un "segmentation fault".

Per generare un backtrace con GDB e utilizzarlo è sufficiente effettuare i seguenti passi:

  1. Aprire un terminale e digitare:
    touch <path_dst> & tail <path_dst> -f & gdb <path_src> > <path_dst>

    dove al posto di <path_src> occorre specificare il percorso del file eseguibile con cui viene lanciata l'applicazione (compreso il nome stesso dell'eseguibile), invece <path_dst> va sostituito con l'intero percorso del file di testo dove verrà salvato il backtrace (compreso il nome del file di testo che si desidera creare).
    Questo comando fa in modo che l'intero output del terminale venga copiato in un file di testo.

  2. Subito dopo, sempre nel terminale, scrivere:
    run
    e contemporaneamente verrà avviata l'applicazione prescelta.
  3. Ora, provare a riprodurre il crash all'interno dell'applicazione. Una volta fatto ciò, si può notare che il flusso di esecuzione in output sul terminale si blocca.
  4. A questo punto si può creare il backtrace vero e proprio digitando:
    backtrace
    o più brevemente:
    bt
  5. Per terminare è sufficiente:
    quit

    e come si può verificare, sarà possibile localizzare il file di report appena creato (contenente il backtrace), alla posizione specificata prima. Procedendo, recarsi in questa pagina e seguire le istruzioni che appaiono sullo schermo. È necessaria una breve descrizione della dinamica del crash riscontrato (fornendo anche informazioni sulle caratteristiche del sistema utilizzato) e infine allegare il file di testo precedentemente menzionato

Ulteriori risorse


CategoryProgrammazione CategoryDaRevisionare

Programmazione/GnuDebugger (l'ultima modifica è del 13/10/2021 16.48.39, fatta da ivantu)