Corso di Programmazione di Sistema Anno accademico 20052006

  • Slides: 42
Download presentation
Corso di Programmazione di Sistema Anno accademico 2005/2006 Debugging e gdb E. P. Mancini

Corso di Programmazione di Sistema Anno accademico 2005/2006 Debugging e gdb E. P. Mancini e U. Villano

Strategie generali di debugging • Conferma Quando un programma contiene un bug, da qualche

Strategie generali di debugging • Conferma Quando un programma contiene un bug, da qualche parte c’è qualcosa che voi credete vero ma che in realtà non lo è. Esempi: • credete che a un certo punto una variabile abbia un certo valore • credete che in uno statement if-then-else sia eseguito il ramo else • credete che richiamando una funzione, essa riceva correttamente i parametri 2

Strategie generali di debugging • Il processo dell’individuazione della locazione di un bug consiste

Strategie generali di debugging • Il processo dell’individuazione della locazione di un bug consiste nel confermare quello che credete – Se credete che la variabile abbia un certo valore, controllate! – Se credete che venga eseguito un ramo else, controllate! Prima o poi troverete che quello che credete non è confermato avete individuato il bug 3

Strategie generali di debugging • Ricerca binaria Nella ricerca di conferme a quello che

Strategie generali di debugging • Ricerca binaria Nella ricerca di conferme a quello che credete, usate una tecnica di ricerca binaria Es. : supponiamo di avere un file sorgente unico di 200 linee senza chiamate di funzione. Credete che x[4]=9 per tutta l’esecuzione de programma. Esaminate x[4] alla linea 100. Se è 9, la ricerca si restringe alle linee 101 -200. Poi provate alla linea 150, . . . 4

Strategie generali di debugging • E se il programma non viene nemmeno compilato? –

Strategie generali di debugging • E se il programma non viene nemmeno compilato? – Spesso il compilatore segnala l’errore all’ultima linea della definizione di una funzione (o classe). L’errore può essere in un qualsiasi punto della funzione. Usate la ricerca binaria Commentate metà della funzione e ricompilate, . . . 5

Usate un tool di debugging e un buon editor di testo • Non usate

Usate un tool di debugging e un buon editor di testo • Non usate printf()/cout come principale strumento di debugging – Confermare il valore assunto da variabili potrebbe essere fatto aggiungendo printf() o cout nel sorgente Questa è una tecnica particolarmente poco conveniente (aggiunta di printf, ricompilazione, esecuzione, aggiunta di altri printf, ricompilazione, . . . , eliminazione del bug, eliminazione dei printf) 6

Usate un tool di debugging e un buon editor di testo • Un tool

Usate un tool di debugging e un buon editor di testo • Un tool di debugging permette di esaminare i valori di variabili in maniera molto più comoda – Non occorre ricompilare – Potete monitorare automaticamente tutte le variabili che volete Questo vi permette di concentrarvi sulla ricerca del bug!! 7

Usate un tool di debugging e un buon editor di testo • Un tool

Usate un tool di debugging e un buon editor di testo • Un tool di debugging vi dice anche dove si è verificato un errore dalle conseguenze peggiori (per es. , un segmentation fault) • Vi segnala anche chi ha chiamato la funzione che ha generato l’errore Fatevi un favore: usate un debugger, non printf !!! 8

Quali tool utilizzare? • gdb – Un classico, disponibile sulla maggioranza dei sistemi Unix

Quali tool utilizzare? • gdb – Un classico, disponibile sulla maggioranza dei sistemi Unix (e non Cygwin) • front-end grafici per gdb – gdb è un tool in modalità testo. Sono stati sviluppati molti front-end GUI per il gdb. Es. : • ddd • KDbg (kde) • Insight (Red Hat, Cygwin) 9

Quali tool utilizzare? • Un buon editor di testo può aiutare molto – Fondamentale

Quali tool utilizzare? • Un buon editor di testo può aiutare molto – Fondamentale l’utilizzo di un editor con: • funzionalità di undo/redo ( ricerca binaria) • subwindows (anche in modalità testo) • interfaccia con debugger e compilatore – Consigli: • vim (vi improved) • emacs • . . 10

Strategie di base • lanciare gdb • settare un certo numero di breakpoints (posizioni

Strategie di base • lanciare gdb • settare un certo numero di breakpoints (posizioni nel codice nelle quali vogliamo sospendere l’esecuzione del programma) • esaminare (a programma sospeso) i valori di variabili • magari procedere in single step (esecuzione di una istruzione per volta) per esaminare il flusso dell’esecuzione 11

Strategie di base • Per i segmentation faults: – eseguire il programma sotto gdb

Strategie di base • Per i segmentation faults: – eseguire il programma sotto gdb (magari senza breakpoints) – al fault, gdb ci dice la locazione dell’errore – se questa fosse in una funzione di libreria/system call, il comando bt (backtrace) ci permette di risalire alla chiamata della funzione che ha causato l’errore 12

Debugger • Un debugger è un programma che permette di effettuare un'analisi approfondita di

Debugger • Un debugger è un programma che permette di effettuare un'analisi approfondita di un altro programma in modo da aiutare a trovare i difetti dello stesso. Un debugger, tra le altre cose, può, relativamente ad un programma: • • avviarlo interromperlo esaminarlo modificarne lo stato (variabili, registri) 13

Debugger DEBUGGER controllo dell’esecuzione Programma 14

Debugger DEBUGGER controllo dell’esecuzione Programma 14

Debugger Sorgente controlla registri memoria (stack, heap) codice macchina Programma in esecuzione 15

Debugger Sorgente controlla registri memoria (stack, heap) codice macchina Programma in esecuzione 15

Sessione di debugging • Un programma deve essere compilato in modo adeguato: Con gcc:

Sessione di debugging • Un programma deve essere compilato in modo adeguato: Con gcc: gcc –g –o testprog. c -g: inclusione dei dati di debug -o nome: nome dell’eseguibile. *. c: sorgenti c L’opzione –ggdb al posto di –g permette di inserire informazioni specifiche per gdb ma potrebbe non essere compatibile con altri debugger. 16

Sessione di debugging Editor (es. vim) sorgenti (es. *. c) codice oggetto (es. *.

Sessione di debugging Editor (es. vim) sorgenti (es. *. c) codice oggetto (es. *. o, *. obj) Compilatore (es. gcc) informazioni di debug Linker (es. ld) eseguibile/libreria 17

Sessione di debugging Se nell’eseguibile non vengono inserite le informazioni di debug non è

Sessione di debugging Se nell’eseguibile non vengono inserite le informazioni di debug non è possibile avere una corrispondenza con il codice ad alto livello si ha la sola vista del codice assembly. 18

Operazioni possibili • Avvio programma • Esecuzione a passi (single-stepping) • Inserimento di punti

Operazioni possibili • Avvio programma • Esecuzione a passi (single-stepping) • Inserimento di punti di interruzione (breakpoints) • Ispezione dei dati e visualizzazione strutturata 19

Esempio Creare un programma, chiamato test, che accetti un parametro da linea di comando.

Esempio Creare un programma, chiamato test, che accetti un parametro da linea di comando. Se il parametro è la parola “utente” (senza controllo delle maiuscole), deve stampare la frase “abilitato”, altrimenti deve stampare la frase “accesso fallito”. Compilarlo con le opzioni per il debug attive. Il controllo della stringa deve avvenire in un’apposita funzione che usa strcmp (che però è case sensitive). >. /test utente abilitato >. /test Utente abilitato >. /test prova accesso fallito 20

Esempio con errori #include <stdio. h> #include <string. h> #define BUFLEN 7 int test(char

Esempio con errori #include <stdio. h> #include <string. h> #define BUFLEN 7 int test(char *word) { return (strcmp("utente", word)==0); } int main(int argc, char **argv) { char buffer[BUFLEN]; int result; strcpy(buffer, argv[0]); result = test(buffer); if (result) printf("abilitaton"); else printf("accesso falliton"); return result; } 21

Problemi nel programma di esempio • non c’è controllo sul numero di parametri •

Problemi nel programma di esempio • non c’è controllo sul numero di parametri • argv[0] non è il primo parametro • strcpy non controlla il superamento del buffer ( segmentation fault) • strcmp è sensibile alla differenza maiuscole/minuscole 22

Avvio del debugger • Su un eseguibile esterno: gdb nome_eseguibile es. gdb test •

Avvio del debugger • Su un eseguibile esterno: gdb nome_eseguibile es. gdb test • Su un programma in esecuzione gdb programma pid • Su un core dump gdb programma corefile • Per terminare: q, quit o ctrl-D 23

gdb in versione GUI Uscita Esecuzione di una funzione Entrata in una funzione Avvio

gdb in versione GUI Uscita Esecuzione di una funzione Entrata in una funzione Avvio Modulo Espressioni Stack Memoria Registri Funzione Variabili locali Breakpoints Modalità di visualizzazione: - sorgente, - assembly, - mista. 24

Avvio del programma run – esegue il programma caricato fino a quando non trova

Avvio del programma run – esegue il programma caricato fino a quando non trova un punto di interruzione (breakpoint o watchpoint) 25

Avvio del programma Argomenti da linea di comando: (gdb) set args imposta gli argomenti.

Avvio del programma Argomenti da linea di comando: (gdb) set args imposta gli argomenti. (gdb) show args visualizza gli argomenti oppure: (gdb) run args esecuzione con gli argomenti dati Ambiente: path: aggiunge una directory al path. cd: cambia la directory corrente. set environment: imposta una var. di ambiente. 26

Avvio del programma • Connessione a programmi già in esecuzione (attach) (gdb) attach pid

Avvio del programma • Connessione a programmi già in esecuzione (attach) (gdb) attach pid si collega ad un processo avviato fuori dal gdb. (gdb) detach rilascia il processo cui ci si è collegati tramite attach. 27

Avvio del programma • Esame di un core dump Un “core dump” è un’istantanea

Avvio del programma • Esame di un core dump Un “core dump” è un’istantanea prodotta dal sistema operativo di un programma che ha avuto un errore grave (es. Segmentation Fault) durante l’esecuzione. Permette di effettuare un’analisi post-mortem del programma. gdb test core #0 0 x 40054 b 03 in ? ? () from /libc. so. 6 a questo punto si può fare un backtrace (analisi delle chiamate) per capire dove si trova l’errore: (gdb) bt 28

Esecuzione a passi (gdb) continue [ignorecount] (gdb) c [ignorecount] Continua l’esecuzione di un programma

Esecuzione a passi (gdb) continue [ignorecount] (gdb) c [ignorecount] Continua l’esecuzione di un programma dall’istruzione in cui era stato fermato. (gdb) step [count] (gdb) s [count] Esegue una o più istruzioni successive. (gdb) next [count] (gdb) n [count] Esegue le istruzioni successive considerando le funzioni come atomiche. Non entra, cioè, all’interno del corpo delle funzioni che incontra. (gdb) finish Continua fino alla terminazione della funzione corrente. 29

Analisi dello stack Ogni funzione chiamata crea un frame sullo stack. (gdb) frame num

Analisi dello stack Ogni funzione chiamata crea un frame sullo stack. (gdb) frame num (gdb) frame address Visualizza il frame corrente o si sposta su uno diverso. (gdb) backtrace [n] bt [n] Visualizza una riga per gli ultimi n frames presenti sullo stack. Fa un’analisi delle chiamate. 30

Analisi dello stack All’inizio dell’esecuzione programma gli unici frame dello stack sono quelli di

Analisi dello stack All’inizio dell’esecuzione programma gli unici frame dello stack sono quelli di libreria kernel 32 e del main. 31

Analisi delle espressioni e delle variabili (gdb) print [expr] (gdb) p[/formato] [expr] visualizza il

Analisi delle espressioni e delle variabili (gdb) print [expr] (gdb) p[/formato] [expr] visualizza il risultato dell’espressione utilizzando un formato specificato (es. p/x stampa in esadecimale, p/d in decimale con segno, . . . ), se non viene indicato un formato, questo viene desunto dalle informazioni di debug. Es. all’inizio del programma “test”: (gdb) p argv[0] $1 = 0 x 22 fdc 4 “/home/user/testgdb/test“ mostra che argv[0] è il nome del programma e non il primo parametro. 32

Analisi delle espressioni e delle variabili (gdb) display [expr] (gdb) disp[/formato] [expr] visualizza il

Analisi delle espressioni e delle variabili (gdb) display [expr] (gdb) disp[/formato] [expr] visualizza il valore dell’espressione utilizzando un formato specificato ogni volta che l’esecuzione del programma viene sospesa (cioè ai breakpoints e nello stepping). L’effetto di un comando display si cancella con (gdb) undisplay expr_code_number (gdb)info display mostra la lista corrente di code numbers 33

Analisi delle espressioni e delle variabili gdb accetta anche comandi printf, assolutamente simili alla

Analisi delle espressioni e delle variabili gdb accetta anche comandi printf, assolutamente simili alla omonima funzione C (gdb) printf “X=%d, Y=%dn”, X, Y Con display e print, tenere sempre presente la differenza tra variabili globali e locali se x è una variabile dichiarata nella funzione f non e’ attiva, il comando print x produce un messaggio: No variable x in current context 34

Analisi della memoria (gdb) x[/formato] indirizzo visualizza il contenuto della memoria. 35

Analisi della memoria (gdb) x[/formato] indirizzo visualizza il contenuto della memoria. 35

Disassemblare il codice Per disassemblare il codice (gdb) disassemble nome_funzione (gdb) disass main Dump

Disassemblare il codice Per disassemblare il codice (gdb) disassemble nome_funzione (gdb) disass main Dump of assembler code for function main: 0 x 8048420 <main>: push %ebp 0 x 8048421 <main+1>: mov %esp, %ebp 0 x 8048423 <main+3>: sub $0 x 8, %esp 0 x 8048426 <main+6>: call 0 x 8048400 <fun> 0 x 804842 b <main+11>: sub $0 xc, %esp 0 x 804842 e <main+14>: push $0 x 80484 a 8 0 x 8048433 <main+19>: call 0 x 80482 f 0 <printf> 0 x 8048438 <main+24>: add $0 x 10, %esp 0 x 804843 b <main+27>: mov %ebp, %esp 0 x 804843 d <main+29>: pop %ebp 0 x 804843 e <main+30>: ret 0 x 804843 f <main+31>: nop End of assembler dump. 36

Analisi dei registri Si fa riferimento ai registri indicandone il nome preceduto dal simbolo

Analisi dei registri Si fa riferimento ai registri indicandone il nome preceduto dal simbolo $. Ad esempio $pc per il program counter e $sp per lo stack pointer. (gdb) info registers visualizza tutti i registri escluso quelli in virgola mobile. (gdb) info all-registers visualizza tutti i registri. Es. (gdb) p/x $pc $6 = 0 x 401100 visualizza il program counter (gdb) x/i $pc lea 0 xffffffe 8(%ebp), %eax 0 x 401100 <main+30>: visualizza l’istruzione successiva (gdb) set $sp += 4 modifica il valore dello stack pointer 37

Breakpoints Sono punti di interruzione del flusso dell’applicazione, si inseriscono con il comando break

Breakpoints Sono punti di interruzione del flusso dell’applicazione, si inseriscono con il comando break (abbreviato b) seguito dal nome di una funzione, da un indirizzo di memoria o da un numero di linea. es. (gdb) b main interrompe l’esecuzione all’inizio della funzione main es. (gdb) b 7 Breakpoint 2 at 0 x 40109 e: file test. c, line 7. inserisce un breakpoint sulla linea 7 (per vedere il sorgente coi numeri di linea: list) 38

Breakpoints I punti di interruzione possono essere inseriti: • su una funzione: break funzione

Breakpoints I punti di interruzione possono essere inseriti: • su una funzione: break funzione • su un indirizzo relativo: break [+][-]offset • su una linea: break numlinea • su una linea di un file specifico break filename: numlinea • su un indirizzo break *indirizzo • sulla prossima istruzione break E’ possibile inserire breakpoints condizionali es. (gdb) b 3 z>92 ferma l’istruzione al breakpoint n. 3 (settato in precedenza) solo se z>92 39

Watchpoints Un watchpoint è uno speciale breakpoint che si attiva quando il valore di

Watchpoints Un watchpoint è uno speciale breakpoint che si attiva quando il valore di un’espressione cambia. es. (gdb) watch expr il programma viene interrotto quando cambia il valore dell’espressione indicata. 40

Comandi set e call • Il comando set può essere utilizzato per modificare il

Comandi set e call • Il comando set può essere utilizzato per modificare il valore di una variabile o di un registro es. (gdb) set variable x=12 • Il comando call serve per invocare l’esecuzione di una funzione (tipicamente una funzione scritta per il debugging, per es. per stampare una lista a puntatori) 41

Ricompilazione Non occorre uscire dal gdb prima di ricompilare Ricompilate in un’altra finestra. All’esecuzione

Ricompilazione Non occorre uscire dal gdb prima di ricompilare Ricompilate in un’altra finestra. All’esecuzione di r (run), il gdb automaticamente utilizzerà l’eseguibile più recente. 42