GNU/Linux >> Linux Esercitazione >  >> Linux

5 suggerimenti per GNU Debugger

GNU Debugger (gdb) è uno strumento prezioso per ispezionare i processi in esecuzione e risolvere i problemi durante lo sviluppo di programmi.

Puoi impostare punti di interruzione in posizioni specifiche (per nome della funzione, numero di riga e così via), abilitare e disabilitare tali punti di interruzione, visualizzare e modificare i valori delle variabili ed eseguire tutte le operazioni standard che ti aspetteresti da qualsiasi debugger. Ma ha molte altre funzionalità che potresti non aver sperimentato. Eccone cinque da provare.

Punti di interruzione condizionali

L'impostazione di un punto di interruzione è una delle prime cose che imparerai a fare con GNU Debugger. Il programma si interrompe quando raggiunge un punto di interruzione e puoi eseguire i comandi gdb per ispezionarlo o modificare le variabili prima di consentire al programma di continuare.

Ad esempio, potresti sapere che una funzione chiamata spesso si arresta in modo anomalo a volte, ma solo quando ottiene un determinato valore di parametro. È possibile impostare un punto di interruzione all'inizio di quella funzione ed eseguire il programma. I parametri della funzione vengono visualizzati ogni volta che raggiunge il punto di interruzione e se il valore del parametro che provoca l'arresto anomalo non viene fornito, è possibile continuare fino a quando la funzione non viene richiamata. Quando il parametro problematico provoca un arresto anomalo, puoi scorrere il codice per vedere cosa c'è che non va.

(gdb) break sometimes_crashes
Breakpoint 1 at 0x40110e: file prog.c, line 5.
(gdb) run
[...]
Breakpoint 1, sometimes_crashes (f=0x7fffffffd1bc) at prog.c:5
5      fprintf(stderr,
(gdb) continue
Breakpoint 1, sometimes_crashes (f=0x7fffffffd1bc) at prog.c:5
5      fprintf(stderr,
(gdb) continue

Per renderlo più ripetibile, puoi contare quante volte la funzione viene chiamata prima della chiamata specifica che ti interessa e impostare un contatore su quel punto di interruzione (ad esempio, "continua 30" per ignorare le 29 volte successive che raggiunge il punto di interruzione).

Ma dove i punti di interruzione diventano davvero potenti è nella loro capacità di valutare le espressioni in fase di esecuzione, che consente di automatizzare questo tipo di test. Immettere:punti di interruzione condizionali.

break [LOCATION] if CONDITION

(gdb) break sometimes_crashes if !f
Breakpoint 1 at 0x401132: file prog.c, line 5.
(gdb) run
[...]
Breakpoint 1, sometimes_crashes (f=0x0) at prog.c:5
5      fprintf(stderr,
(gdb)

Invece di fare in modo che gdb chieda cosa fare ogni volta che viene chiamata la funzione, un punto di interruzione condizionale consente di fare in modo che gdb si fermi in quella posizione solo quando una particolare espressione viene valutata come vera. Se l'esecuzione raggiunge la posizione del punto di interruzione condizionale, ma l'espressione viene valutata come false, il

Più risorse Linux

  • Comandi Linux cheat sheet
  • Cheat sheet sui comandi avanzati di Linux
  • Corso online gratuito:Panoramica tecnica RHEL
  • Cheat sheet della rete Linux
  • Cheat sheet di SELinux
  • Cheat sheet dei comandi comuni di Linux
  • Cosa sono i container Linux?
  • I nostri ultimi articoli su Linux

il debugger consente automaticamente al programma di continuare senza chiedere all'utente cosa fare.

Comandi breakpoint

Una caratteristica ancora più sofisticata dei punti di interruzione in GNU Debugger è la capacità di scrivere una risposta al raggiungimento di un punto di interruzione. I comandi di punto di interruzione ti consentono di scrivere un elenco di comandi GNU Debugger da eseguire ogni volta che raggiunge un punto di interruzione.

Possiamo usarlo per aggirare il bug che già conosciamo in sometimes_crash funzione e farlo tornare da quella funzione in modo innocuo quando fornisce un puntatore nullo.

Possiamo usare silenzioso come prima riga per ottenere un maggiore controllo sull'output. Senza questo, lo stack frame verrà visualizzato ogni volta che viene raggiunto il punto di interruzione, anche prima dell'esecuzione dei nostri comandi di punto di interruzione.

(gdb) break sometimes_crashes
Breakpoint 1 at 0x401132: file prog.c, line 5.
(gdb) commands 1
Type commands for breakpoint(s) 1, one per line.
End with a line saying just "end".
>silent
>if !f
 >frame
 >printf "Skipping call\n"
 >return 0
 >continue
 >end
>printf "Continuing\n"
>continue
>end
(gdb) run
Starting program: /home/twaugh/Documents/GDB/prog
warning: Loadable section ".note.gnu.property" outside of ELF segments
Continuing
Continuing
Continuing
#0  sometimes_crashes (f=0x0) at prog.c:5
5      fprintf(stderr,
Skipping call
[Inferior 1 (process 9373) exited normally]
(gdb)

Scarica la memoria binaria

GNU Debugger ha un supporto integrato per esaminare la memoria usando x comando in vari formati, inclusi ottale, esadecimale e così via. Ma mi piace vedere due formati affiancati:byte esadecimali a sinistra e caratteri ASCII rappresentati da quegli stessi byte a destra.

Quando voglio visualizzare il contenuto di un file byte per byte, utilizzo spesso hexdump -C (hexdump deriva dal pacchetto util-linux). Ecco la x di gdb comando che visualizza byte esadecimali:

(gdb) x/33xb mydata
0x404040 <mydata>:    0x02    0x01    0x00    0x02    0x00    0x00    0x00    0x01
0x404048 <mydata+8>:    0x01    0x47    0x00    0x12    0x61    0x74    0x74    0x72
0x404050 <mydata+16>:    0x69    0x62    0x75    0x74    0x65    0x73    0x2d    0x63
0x404058 <mydata+24>:    0x68    0x61    0x72    0x73    0x65    0x75    0x00    0x05
0x404060 <mydata+32>:    0x00

E se potessi insegnare a gdb a visualizzare la memoria proprio come fa hexdump? Puoi, e in effetti, puoi utilizzare questo metodo per qualsiasi formato tu preferisca.

Combinando il dump comando per memorizzare i byte in un file, la shell comando per eseguire hexdump sul file e define comando, possiamo creare il nostro nuovo hexdump comando per utilizzare hexdump per visualizzare il contenuto della memoria.

(gdb) define hexdump
Type commands for definition of "hexdump".
End with a line saying just "end".
>dump binary memory /tmp/dump.bin $arg0 $arg0+$arg1
>shell hexdump -C /tmp/dump.bin
>end

Questi comandi possono anche essere inseriti in ~/.gdbinit file per definire il comando hexdump in modo permanente. Eccolo in azione:

(gdb) hexdump mydata sizeof(mydata)
00000000  02 01 00 02 00 00 00 01  01 47 00 12 61 74 74 72  |.........G..attr|
00000010  69 62 75 74 65 73 2d 63  68 61 72 73 65 75 00 05  |ibutes-charseu..|
00000020  00                                                |.|
00000021

Smontaggio in linea

A volte vuoi capire di più su cosa è successo che ha portato a un arresto anomalo e il codice sorgente non è sufficiente. Vuoi vedere cosa sta succedendo a livello di istruzione della CPU.

Lo smontaggio comando consente di visualizzare le istruzioni della CPU che implementano una funzione. Ma a volte l'output può essere difficile da seguire. Di solito, voglio vedere quali istruzioni corrispondono a una determinata sezione del codice sorgente nella funzione. Per ottenere ciò, usa /s modificatore per includere le righe del codice sorgente con il disassembly.

(gdb) disassemble/s main
Dump of assembler code for function main:
prog.c:
11    {
   0x0000000000401158 <+0>:    push   %rbp
   0x0000000000401159 <+1>:    mov      %rsp,%rbp
   0x000000000040115c <+4>:    sub      $0x10,%rsp

12      int n = 0;
   0x0000000000401160 <+8>:    movl   $0x0,-0x4(%rbp)

13      sometimes_crashes(&n);
   0x0000000000401167 <+15>:    lea     -0x4(%rbp),%rax
   0x000000000040116b <+19>:    mov     %rax,%rdi
   0x000000000040116e <+22>:    callq  0x401126 <sometimes_crashes>
[...snipped...]

Questo, insieme ai registri informativi per vedere i valori correnti di tutti i registri della CPU e comandi come stepi per eseguire un'istruzione alla volta, ti consente di avere una comprensione molto più dettagliata del programma.

Debug inverso

A volte vorresti poter tornare indietro nel tempo. Immagina di aver raggiunto un punto di osservazione su una variabile. Un punto di osservazione è come un punto di interruzione, ma invece di essere impostato in una posizione nel programma, viene impostato su un'espressione (usando il orologio comando). Ogni volta che il valore dell'espressione cambia, l'esecuzione si interrompe e il debugger assume il controllo.

Quindi immagina di aver raggiunto questo punto di osservazione e la memoria utilizzata da una variabile ha cambiato valore. Questo può essere causato da qualcosa che è accaduto molto prima; ad esempio, la memoria è stata liberata e ora viene riutilizzata. Ma quando e perché è stato liberato?

Il GNU Debugger può risolvere anche questo problema perché puoi eseguire il tuo programma al contrario!

Raggiunge questo obiettivo registrando attentamente lo stato del programma ad ogni passaggio in modo che possa ripristinare gli stati precedentemente registrati, dando l'illusione che il tempo scorra all'indietro.

Per abilitare questa registrazione dello stato, utilizza target record-full comando. Quindi puoi usare comandi dal suono impossibile, come:

  • passo indietro , che torna alla riga di origine precedente
  • indietro-successivo , che riavvolge alla riga di origine precedente, tornando indietro sulle chiamate di funzione
  • retromarcia , che torna indietro fino al punto in cui stava per essere chiamata la funzione corrente
  • indietro-continua , che torna allo stato precedente nel programma che (ora) attiverebbe un punto di interruzione (o qualsiasi altra cosa che ne provochi l'arresto)

Ecco un esempio di debug inverso in azione:

(gdb) b main
Breakpoint 1 at 0x401160: file prog.c, line 12.
(gdb) r
Starting program: /home/twaugh/Documents/GDB/prog
[...]

Breakpoint 1, main () at prog.c:12
12      int n = 0;
(gdb) target record-full
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401154 in sometimes_crashes (f=0x0) at prog.c:7
7      return *f;
(gdb) reverse-finish
Run back to call of #0  0x0000000000401154 in sometimes_crashes (f=0x0)
        at prog.c:7
0x0000000000401190 in main () at prog.c:16
16      sometimes_crashes(0);

Queste sono solo alcune delle cose utili che GNU Debugger può fare. Ci sono molti altri da scoprire. Quale caratteristica nascosta, poco conosciuta o semplicemente sorprendente di gdb è la tua preferita? Per favore condividilo nei commenti.


Linux
  1. 3 consigli per la stampa con Linux

  2. Suggerimenti Linux per l'utilizzo di GNU Screen

  3. 8 suggerimenti per la riga di comando di Linux

  4. Suggerimenti e trucchi per curl e wget

  5. 5 suggerimenti avanzati di rsync per gli amministratori di sistema Linux

Un tutorial pratico per l'utilizzo di GNU Project Debugger

Suggerimenti per l'utilizzo del comando top in Linux

Consigli/trucchi utili per Meld per utenti intermedi

Suggerimenti per l'utilizzo di tmux

Suggerimenti per l'utilizzo dello schermo

Converti millisecondi in timespec per la porta GNU