Title: Diapositiva 1
1Corso di Programmazione di Sistema Anno
accademico 2005/2006
Debugging e gdb E. P. Mancini e U. Villano
2Strategie 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
3Strategie generali di debugging
- Il processo dellindividuazione 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
4Strategie 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 x49 per tutta lesecuzione de
programma. Esaminate x4 alla linea 100. Se è 9,
la ricerca si restringe alle linee 101-200. Poi
provate alla linea 150, ...
5Strategie generali di debugging
- E se il programma non viene nemmeno compilato?
- Spesso il compilatore segnala lerrore allultima
linea della definizione di una funzione (o
classe). Lerrore può essere in un qualsiasi
punto della funzione. - Usate la ricerca binaria
- Commentate metà della funzione e ricompilate, ...
6Usate 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)
7Usate 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!!
8Usate 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 lerrore -
- Fatevi un favore usate un debugger, non printf
!!!
9Quali 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)
10Quali tool utilizzare?
- Un buon editor di testo può aiutare molto
- Fondamentale lutilizzo 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
- ....
11Strategie di base
- lanciare gdb
- settare un certo numero di breakpoints (posizioni
nel codice nelle quali vogliamo sospendere
lesecuzione 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
dellesecuzione
12Strategie di base
- Per i segmentation faults
- eseguire il programma sotto gdb (magari senza
breakpoints) - al fault, gdb ci dice la locazione dellerrore
- 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 lerrore
13Debugger
- 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)
14Debugger
DEBUGGER
controllo dellesecuzione
Programma
15Debugger
Debugger
Sorgente
controlla
memoria (stack, heap)
codice macchina
registri
Programma in esecuzione
16Sessione di debugging
- Un programma deve essere compilato in modo
adeguato - Con gcc
- gcc g o testprog testprog.c
- -g inclusione dei dati di debug
- -o nome nome delleseguibile.
- .c sorgenti c
- Lopzione ggdb al posto di g permette di
inserire informazioni specifiche per gdb ma
potrebbe non essere compatibile con altri
debugger.
17Sessione di debugging
sorgenti (es. .c)
Editor (es. vim)
codice oggetto (es. .o, .obj)
Compilatore (es. gcc)
informazioni di debug
eseguibile/libreria
Linker (es. ld)
18Sessione di debugging
Se nelleseguibile 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.
19Operazioni possibili
- Avvio programma
- Esecuzione a passi (single-stepping)
- Inserimento di punti di interruzione
(breakpoints) - Ispezione dei dati e visualizzazione strutturata
20Esempio
- 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 unapposita funzione che usa strcmp (che però
è case sensitive). - gt ./test utente
- abilitato
- gt ./test Utente
- abilitato
- gt ./test prova
- accesso fallito
21Esempio con errori
- include ltstdio.hgt
- include ltstring.hgt
- define BUFLEN 7
- int test(char word)
- return (strcmp("utente", word)0)
-
- int main(int argc, char argv)
- char bufferBUFLEN
- int result
- strcpy(buffer, argv0)
- result test(buffer)
- if (result)
- printf("abilitato\n")
- else
- printf("accesso fallito\n")
- return result
22Problemi nel programma di esempio
- non cè controllo sul numero di parametri
- argv0 non è il primo parametro
- strcpy non controlla il superamento del buffer (?
segmentation fault) - strcmp è sensibile alla differenza
maiuscole/minuscole
23Avvio 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
24gdb in versione GUI
Uscita
Esecuzione di una funzione
Espressioni
Entrata in una funzione
Stack
Memoria
Avvio
Variabili locali
Breakpoints
Registri
Modalità di visualizzazione - sorgente, -
assembly, - mista.
Funzione
Modulo
25Avvio del programma
- run esegue il programma caricato fino a quando
non trova un punto di interruzione (breakpoint o
watchpoint)
26Avvio 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.
27Avvio 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.
28Avvio del programma
- Esame di un core dump
- Un core dump è unistantanea prodotta dal
sistema operativo di un programma che ha avuto un
errore grave (es. Segmentation Fault) durante
lesecuzione. Permette di effettuare unanalisi
post-mortem del programma. - gdb test core
- 0 0x40054b03 in ?? () from /lib/libc.so.6
- a questo punto si può fare un backtrace (analisi
delle chiamate) per capire dove si trova
lerrore - (gdb) bt
29Esecuzione a passi
- (gdb) continue ignorecount
- (gdb) c ignorecount
- Continua lesecuzione di un programma
dallistruzione 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è,
allinterno del corpo delle funzioni che
incontra. - (gdb) finish
- Continua fino alla terminazione della funzione
corrente.
30Analisi dello stack
- Ogni funzione chiamata crea un frame sullo stack.
- (gdb) frame
- (gdb) frame num
- (gdb) frame address
- Visualizza il frame corrente o si sposta su uno
diverso. - (gdb) backtrace n
- (gdb) bt n
- Visualizza una riga per gli ultimi n frames
presenti sullo stack. Fa unanalisi delle
chiamate.
31Analisi dello stack
Allinizio dellesecuzione programma gli unici
frame dello stack sono quelli di libreria
kernel32 e del main.
32Analisi delle espressioni e delle variabili
- (gdb) print expr
- (gdb) p/formato expr
- visualizza il risultato dellespressione
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. allinizio del programma test
- (gdb) p argv0
- 1 0x22fdc4 /home/user/testgdb/test
- mostra che argv0 è il nome del programma e non
il primo parametro.
33Analisi delle espressioni e delle variabili
- (gdb) display expr
- (gdb) disp/formato expr
- visualizza il valore dellespressione utilizzando
un formato specificato ogni volta che
lesecuzione del programma viene sospesa (cioè ai
breakpoints e nello stepping). - Leffetto di un comando display si cancella con
- (gdb) undisplay expr_code_number
- (gdb)info display
- mostra la lista corrente di code numbers
34Analisi delle espressioni e delle variabili
- gdb accetta anche comandi printf, assolutamente
simili alla omonima funzione C - (gdb) printf Xd, Yd\n,X,Y
- Con display e print, tenere sempre presente la
differenza tra variabili globali e locali - se x è una variabile dichiarata nella funzione f
e f non e attiva, il comando print x produce un
messaggio - No variable x in current context
35Analisi della memoria
- (gdb) x/formato indirizzo
- visualizza il contenuto della memoria.
36Disassemblare il codice
- Per disassemblare il codice
- (gdb) disassemble nome_funzione
- (gdb) disass main
- Dump of assembler code for function main
- 0x8048420 ltmaingt push ebp
- 0x8048421 ltmain1gt mov esp,ebp
- 0x8048423 ltmain3gt sub 0x8,esp
- 0x8048426 ltmain6gt call 0x8048400 ltfungt
- 0x804842b ltmain11gt sub 0xc,esp
- 0x804842e ltmain14gt push 0x80484a8
- 0x8048433 ltmain19gt call 0x80482f0 ltprintfgt
- 0x8048438 ltmain24gt add 0x10,esp
- 0x804843b ltmain27gt mov ebp,esp
- 0x804843d ltmain29gt pop ebp
- 0x804843e ltmain30gt ret
- 0x804843f ltmain31gt nop
- End of assembler dump.
37Analisi 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 0x401100
- visualizza il program counter
- (gdb) x/i pc
- lea 0xffffffe8(ebp),eax
- 0x401100 ltmain30gt
- visualizza listruzione successiva
- (gdb) set sp 4
- modifica il valore dello stack pointer
38Breakpoints
- Sono punti di interruzione del flusso
dellapplicazione, 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 lesecuzione allinizio della funzione
main - es.(gdb) b 7
- Breakpoint 2 at 0x40109e file test.c, line 7.
- inserisce un breakpoint sulla linea 7 (per vedere
il sorgente coi numeri di linea list)
39Breakpoints
- 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
filenamenumlinea - su un indirizzo break indirizzo
- sulla prossima istruzione break
- E possibile inserire breakpoints condizionali
- es. (gdb) b 3 zgt92
- ferma listruzione al breakpoint n. 3 (settato in
precedenza) solo se zgt92 -
40Watchpoints
- Un watchpoint è uno speciale breakpoint che si
attiva quando il valore di unespressione cambia. - es. (gdb) watch expr
- il programma viene interrotto quando cambia il
valore dellespressione indicata.
41Comandi set e call
- Il comando set può essere utilizzato per
modificare il valore di una variabile o di un
registro - es. (gdb) set variable x12
- Il comando call serve per invocare lesecuzione
di una funzione (tipicamente una funzione scritta
per il debugging, per es. per stampare una lista
a puntatori)
42Ricompilazione
- Non occorre uscire dal gdb prima di ricompilare
- Ricompilate in unaltra finestra. Allesecuzione
di r (run), il gdb automaticamente utilizzerà
leseguibile più recente.