[Powered by Google Translate] [Valgrind]
[Nate Hardison, Harvard University]
Questo è CS50, CS50.TV]
Alcuni dei bug più difficili programmi C
provengono dalla cattiva gestione della memoria.
Ci sono un numero enorme di modi per pasticci
tra cui l'assegnazione della quantità errata di memoria,
dimenticare di inizializzare le variabili,
scritte prima o dopo la fine di un tampone,
e liberando mantenere i tempi di memoria più.
I sintomi variano da blocchi intermittenti
ai valori misteriosamente scritti sopra,
spesso in luoghi e tempi lontani da l'errore originale.
Tracciare il problema osservato torna la causa principale di fondo
può essere impegnativo,
ma per fortuna c'è un programma utile chiamato Valgrind
che può fare molto per aiutare.
>> Si esegue un programma in Valgrind per consentire
controllo esteso di allocazioni di memoria heap e accessi.
Quando Valgrind rileva un problema, ti dà immediato,
informazione diretta che consente di
più facile trovare e risolvere il problema.
Valgrind anche relazioni su problemi di memoria meno mortali,
come ad esempio perdite di memoria, l'allocazione di memoria heap,
e dimenticando per liberarlo.
Come il nostro compilatore, Clang, nel nostro debugger, GDB,
Valgrind è software libero, e si è installato l'apparecchio.
Valgrind viene eseguito sul binario eseguibile,
non è il tuo. co. h file di codice sorgente,
in modo da essere sicuro di aver compilato un up-to-data copia del programma
usando Clang O Fai.
Quindi, l'esecuzione del programma in Valgrind può essere
semplice come solo anteponendo il comando standard di programma con il Valgrind parola,
che avvia Valgrind ed esegue il programma all'interno di esso.
Quando si avvia, fa un po 'complessa Valgrind
jiggering per configurare il file eseguibile per le verifiche di memoria,
in modo che possa prendere un po 'per ottenere installato e funzionante.
Il programma sarà poi eseguito normalmente, sia molto più lentamente,
e quando finisce, Valgrind verrà stampata una sintesi del suo utilizzo di memoria.
Se tutto va bene, sarà simile a questa:
In questo caso,. / Clean_program
è il percorso del programma da eseguire.
E mentre questo non si richiede alcun argomento,
se così fosse mi piacerebbe semplicemente virare fino alla fine del comando, come al solito.
Programma di Clean è solo un po 'sciocco programma che ho creato
che alloca spazio per un blocco di interi sul mucchio,
mettere alcuni valori all'interno di essi, e libera l'intero blocco.
Questo è ciò che si sta girando per, senza errori e senza perdite.
>> Un altro dato importante è il numero totale di byte allocati.
A seconda del programma, se le assegnazioni sono in megabyte o superiore,
probabilmente stai facendo qualcosa di sbagliato.
Stai inutilmente memorizzazione duplicati?
Si sta utilizzando l'heap per l'archiviazione, quando sarebbe meglio usare la pila?
Così, errori di memoria può essere veramente male.
Quelli più evidenti causare incidenti spettacolari,
ma anche in questo caso può essere ancora difficile individuare
che cosa ha portato al crollo.
Più insidioso, un programma con un errore di memoria
può ancora compilarlo
e può ancora sembrano funzionare correttamente
perché sei riuscito a ottenere fortuna la maggior parte del tempo.
Dopo diversi "risultati positivi",
si potrebbe anche pensare che un incidente è un colpo di fortuna del computer,
ma il computer non è mai sbagliato.
>> Esecuzione Valgrind può aiutare a rintracciare la causa di errori di memoria visibili
così come trovare in agguato gli errori non è nemmeno ancora a conoscenza.
Ogni volta che Valgrind rileva un problema, viene stampato le informazioni su ciò che osserva.
Ogni elemento è abbastanza conciso -
la riga di origine dell'istruzione offendere, qual è il problema,
e un po 'di informazioni sulla memoria coinvolto -
ma spesso è abbastanza informazioni per dirigere la vostra attenzione nel posto giusto.
Ecco un esempio di Valgrind programma in esecuzione su un buggy
che fa una lettura valida di memoria heap.
Vediamo presenti errori o avvisi di compilazione.
Uh-oh, il riassunto di errore dice che ci sono due errori -
due letture non valido di dimensione 4 - byte, che è.
Sia cattiva legge verificato nella funzione principale di invalid_read.c,
la prima linea 16 e la seconda linea 19.
Diamo un'occhiata al codice.
Sembra che la prima chiamata a printf tenta di leggere un int oltre la fine del nostro blocco di memoria.
Se guardiamo indietro all'uscita Valgrind,
vediamo che Valgrind ci ha detto esattamente questo.
L'indirizzo che stiamo cercando di leggere inizia 0 byte
oltre la fine del blocco di misura 16 byte -
quattro a 32 bit interi che noi assegnati.
Vale a dire, l'indirizzo stavamo cercando di leggere inizia proprio alla fine del nostro blocco,
proprio come si vede nella nostra chiamata printf male.
Ora, non valida letture potrebbe non sembrare così grande di un affare,
ma se si sta utilizzando i dati per controllare il flusso del programma -
per esempio, come parte di un'istruzione if o loop -
allora le cose possono andare male in silenzio.
Guarda come posso eseguire il programma invalid_read
e nulla di straordinario accade.
Spaventoso, eh?
>> Ora, diamo un'occhiata a un po 'di più tipi di errori che si possono verificare nel codice,
e vedremo come Valgrind li rileva.
Abbiamo appena visto un esempio di un invalid_read,
così ora diamo un'occhiata un invalid_write.
Anche in questo caso, non ci sono errori o avvisi di compilazione.
Ok, Valgrind dice che ci sono due errori in questo programma -
e invalid_write e un invalid_read.
Diamo un'occhiata a questo codice.
Sembra che abbiamo un esempio del classico strlen più un bug.
Il codice non malloc un byte extra di spazio
per il carattere / 0,
così quando str copia è andato a scrivere a ssubstrlen "CS50 rocks!"
ha scritto 1 byte oltre la fine del nostro blocco.
Il invalid_read arriva quando facciamo la nostra chiamata alla printf.
Printf finisce la lettura di memoria non valida quando si legge il / 0 caratteri
come appare alla fine di questa stringa E è la stampa.
Ma niente di tutto questo sfuggito Valgrind.
Vediamo che lo prese il invalid_write come parte della copia str
sulla linea 11 del principale, e il invalid_read è parte di printf.
Rock on, Valgrind.
Ancora una volta, questo potrebbe non sembrare un grande affare.
Siamo in grado di eseguire questo programma più e più volte al di fuori di Valgrind
e non vedere alcun sintomo di errore.
>> Tuttavia, diamo un'occhiata a una leggera variazione di questo per vedere
come le cose possono ottenere davvero male.
Quindi, ha concesso, stiamo abusando le cose più che un po 'in questo codice.
Stiamo solo allocare spazio sul mucchio per due stringhe
la lunghezza del CS50 rocce,
questa volta, ricordando il / 0 caratteri.
Ma poi ci buttiamo in un super-lunga stringa nel blocco di memoria
che S sta puntando.
Quali saranno gli effetti che hanno sul blocco di memoria che punta a T?
Beh, se i punti di T alla memoria questo è solo adiacente a S,
venuta solo dopo,
allora potremmo aver scritto su una parte di T.
Corriamo questo codice.
Guardate quello che è successo.
Le stringhe che abbiamo memorizzato nei nostri blocchi di heap entrambi sembravano essere stampati in modo corretto.
Nulla sembra male a tutti.
Comunque, torniamo nel nostro codice e
commentare la riga in cui copiamo CS50 rocce
nel secondo blocco di memoria, puntata da t.
Ora, quando si esegue questo codice dovremmo
solo vedere il contenuto del primo blocco di memoria stampare.
Ehi, anche se non abbiamo str copia
tutti i caratteri nel blocco heap secondo, quello puntato da T,
si ottiene una stampa.
Infatti, la stringa che abbiamo farcito nel nostro primo blocco
sorpassato il primo blocco e nel secondo blocco,
facendo sembrare tutto normale.
Valgrind, però, ci racconta la vera storia.
Ecco fatto.
Tutti quelli non valido di lettura e scrittura.
>> Vediamo un esempio di un altro tipo di errore.
Qui facciamo qualcosa di spiacevole.
Prendiamo lo spazio per un int sul mucchio,
e inizializzare un puntatore a int - p - per puntare a quello spazio.
Tuttavia, mentre il nostro puntatore è inizializzato,
i dati che esso punta a poco tutto ciò che è spazzatura è in quella parte del mucchio.
Così, quando si caricano i dati in int i,
abbiamo tecnicamente i inizializzazione,
ma lo facciamo con i dati spazzatura.
La chiamata di affermare, che è una macro di debug a portata di mano
definito nel nome appropriato affermare biblioteca,
verrà interrotto il programma se la sua condizione di test ha esito negativo.
Cioè, se i non è 0.
A seconda di cosa è stato nello spazio heap, puntato da p,
questo programma potrebbe funzionare a volte e non in altri momenti.
Se funziona, siamo solo fortunati.
Il compilatore non prenderà questo errore, ma Valgrind volontà sicuro.
Ci si vede l'errore derivante dal nostro uso di tali dati spazzatura.
>> Quando si alloca memoria heap ma non rilasciare o liberarlo,
che è chiamata una perdita.
Per un piccolo, breve programma che gira ed esce immediatamente,
le perdite sono abbastanza innocui,
ma per un progetto di grandi dimensioni e / o longevità,
anche una piccola perdita può aggravare in qualcosa di importante.
Per CS50, ci aspettiamo di
prendersi cura di liberare tutta la memoria heap di allocare,
dal momento che vogliamo voi per costruire la capacità di gestire correttamente il processo manuale
richiesto da C.
Per fare ciò, il programma dovrebbe avere una esatta
uno-a-uno corrispondenza tra malloc e chiamate gratuite.
Fortunatamente, Valgrind può aiutare con perdite di memoria troppo.
Ecco un programma che perde chiamato perdite.C che alloca
spazio sul mucchio, scrive ad esso, ma non lo libera.
Abbiamo compilarlo con Make ed eseguirlo sotto Valgrind,
e vediamo che, mentre non abbiamo errori di memoria,
noi abbiamo uno perdita.
Ci sono 16 byte definitivamente perduti,
il che significa che il puntatore che la memoria non era in campo quando il programma è terminato.
Ora, Valgrind non ci dà un sacco di informazioni sulla perdita,
ma se seguiamo questa piccola nota che dà verso il fondo della sua relazione
eseguire di nuovo con - perdita-check = full
per vedere i dettagli di perdita di memoria,
avremo ulteriori informazioni.
Ora, nel riassunto mucchio,
Valgrind ci dice dove la memoria che è stato perso è stata inizialmente attribuita.
Così come sappiamo dal guardare nel codice sorgente,
Valgrind ci informa che si perdeva la memoria
assegnato con una chiamata a malloc sulla linea 8 del perdite.C
nella funzione principale.
Piuttosto carino.
>> Valgrind classifica perdite usando questi termini:
Sicuramente ha perso - questa è heap memoria allocata
in cui il programma non ha più un puntatore.
Valgrind sa che una volta avuto il puntatore, ma da allora hanno perso le tracce di esso.
Questa memoria è sicuramente trapelato.
Indirettamente perso - questa è heap memoria allocata
a cui solo i puntatori alle anche vengono persi.
Ad esempio, se hai perso il puntatore al primo nodo di una lista concatenata,
quindi il primo nodo stessa sarebbe definitivamente perduto,
mentre tutti i nodi successivi sarebbe indirettamente persa.
Forse ha perso - questa è heap memoria allocata
a cui Valgrind non può essere sicuri che vi sia un puntatore o meno.
Ancora raggiungibile è heap memoria allocata
in cui il programma ha ancora un puntatore in uscita,
il che significa in genere che un punto variabile globale ad esso.
Per verificare la presenza di queste fughe di notizie, avrete anche la possibilità di includere
- Ancora raggiungibile = yes
nella tua invocazione di Valgrind.
>> Questi casi possono richiedere diverse strategie diverse per la pulizia in su,
ma le perdite devono essere eliminate.
Purtroppo, fissando le perdite possono essere difficile da fare,
poiché quelle non corrette per liberare far saltare in aria il vostro programma.
Per esempio, se guardiamo alla invalid_free.c,
vediamo un esempio di deallocazione brutto ricordo.
Quale dovrebbe essere una singola chiamata per liberare l'intero blocco
di memoria puntato da int_block,
è invece diventato un tentativo di liberare ogni int imprese sezione
della memoria individuale.
In questo modo non catastrofico.
Boom! Nei un errore.
Questo non è sicuramente buona.
Se sei bloccato con questo tipo di errore, però, e non si sa dove guardare,
ripiegare sul tuo nuovo migliore amico.
Avete indovinato - Valgrind.
Valgrind, come sempre, sa esattamente cosa succede.
I conteggi alloc e libero non coincidono.
Abbiamo ottenuto 1 alloc e 4 libera.
E Valgrind ci dice anche dove la prima chiamata cattivo libero -
quella che ha attivato l'ingrandimento - proviene -
linea 16.
Come potete vedere, le chiamate cattivi per liberare sono davvero male,
quindi si consiglia di lasciare il vostro programma di perdita di
mentre si sta lavorando per ottenere la corretta funzionalità.
Iniziare la ricerca di perdite solo dopo che il programma funziona correttamente,
senza altri errori.
>> E questo è tutto quello che abbiamo per questo video.
Ora, che cosa stai aspettando?
Vai eseguire Valgrind sui programmi al momento.
Il mio nome è Nate Hardison. Questo è CS50. [CS50.TV]