Skip to content

Valgrind

Andrea Rossi edited this page Jul 26, 2017 · 37 revisions

Fonte: DocumentazioneDownload

Valgrind è un framework di strumenti per realizzare all'occorrenza strumenti di analisi. Vi sono diverse funzionalità che permettono di rappresentare la gestione della memoria ed analizzare i bug con grande precisione. Ovviamente, Valgrind permette l'analisi delle prestazioni del tuo programma in molti dettagli. Infine, puoi espandere le funzionalità di Valgrind ed adattarle alle tue esigenze.

Attualmente (2017) Valgrind include sei validi strumenti:

  • Rilevatore errori di memoria
  • Due rilevatori per errori allegati ai thread
  • Agente per la misurazione della cache e delle predizioni delle diramazioni (salti condizionati)
  • Agente per la realizzazione grafica degli esiti di misurazione della cache e delle predizioni delle diramazioni
  • Agente per l'analisi dell'area di HEAP

Ed altri tre in fase sperimentale:

  • Rilevatore di sovrascrittura di aree di memoria globale e dello STACK
  • Un "heap profiler" secondario che esamina come i blocchi nell'heap vengono effettivamente usati dal programma
  • un generatore di "SimPoint basic block vector";

Tutorial Valgrind

Tutto ciò che serve per usare valgrind è una singola riga di comando strutturata come segue:

valgrind options your-program

Ricordati di compilare il tuo programma in modalità "debug", ossia con la flag -g del compilatore:

gcc -g my_awesome_program.c -o my_awesome_program

Inoltre ricorda che valgrind è difficilmente compatibile con le flag di ottimizzazione del compilatore (-O1, -O2, -Os e -O3): pertanto non usarlo quando stai compilando in modo ottimizzato.

Non verranno presentate tutte le opzioni disponibili, ma solo alcune: per un elenco completo prova valgrind --help

HelloWorld

Vediamo insieme un set di risultati dati dalla valutazione di valgrind del file compilato del nostro programma.

Sorgente

Il seguente

#include <stdio.h>
int main(){`
    printf("Hello world\n");`
    return 0;`
}

Compilazione ed Output

$:~# gcc -g -fno-stack-protector helloworld.c -o helloworld.out
$:~# ./helloworld.out 
Hello world

Tutto bene!

Output con Valgrind

Lanciando il comando valgrind --leak-check=full ./helloworld.out otteniamo

==18981== Memcheck, a memory error detector
==18981== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18981== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18981== Command: ./a.out
==18981== 
Hello world
==18981== 
==18981== HEAP SUMMARY:
==18981==     in use at exit: 0 bytes in 0 blocks
==18981==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==18981== 
==18981== All heap blocks were freed -- no leaks are possible
==18981== 
==18981== For counts of detected and suppressed errors, rerun with: -v
==18981== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
  • Il numero ad inizio riga indica il PID del processo. Utile in caso rimanesse "appeso" nella nostra macchina (specie se su server).
  • Senza alcuna specifica, Valgrind ha lanciato il tool MemCheck al fine di individuare inconsistenze relative alla memoria, dato che è il tool di default per Valgrind. Al più si può specificarne l'uso con l'opzione --tool=memcheck, in caso di combinazioni particolari.
  • È presente l'output del programma (in questo caso non è presente il PID del processo)
  • All heap blocks were freed -- no leaks are possible identifica che il programma ha correttamente liberato lo spazio utilizzato per l'HEAP, la cui dimensione è visibile qualche riga sopra con il messaggio total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated. Nella fattispecie il messaggio alla completa liberazione dell'heap è la conferma che il programma libera correttamente la memoria. Una ulteriore conferma deriva dal numero di allocs e frees che deve essere identico (a riprova della corretta liberazione della memoria occupata).

Unintialized Value

Dichiariamo due variabili inizializzando la seconda con la prima, senza che venga assegnato alcun valore a quest'ultima.

Sorgente

#include <stdio.h> 

int main(){
    t_1();
    return 0;
}

void t_1() {
    int b;
    printf("b=%d\n", b);
}

Compilazione ed Output

$:~#  gcc -g -fno-stack-protector a_unitialized_value.c -o a.out && valgrind --leak-check=full  ./a.out
a_unitialized_value.c: In function ‘main’:
a_unitialized_value.c:4:9: warning: implicit declaration of function ‘t_1’ [-Wimplicit-function-declaration]
     t_1();
     ^
a_unitialized_value.c: At top level:
a_unitialized_value.c:8:10: warning: conflicting types for ‘t_1’
 void t_1() {
      ^
a_unitialized_value.c:4:9: note: previous implicit declaration of ‘t_1’ was here
     t_1();
     ^

$:~# ./a.out 
b=0

Per scrupolo ho scelto di ripetere due volte l'esecuzione del programma: il valore di b non dipende dall'esecuzione.

Output con Valgrind

	==18973== Memcheck, a memory error detector
==18973== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==18973== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==18973== Command: ./a.out
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E87B83: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Use of uninitialised value of size 8
==18973==    at 0x4E8476B: _itoa_word (_itoa.c:179)
==18973==    by 0x4E8812C: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E84775: _itoa_word (_itoa.c:179)
==18973==    by 0x4E8812C: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E881AF: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E87C59: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E8841A: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E87CAB: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
==18973== Conditional jump or move depends on uninitialised value(s)
==18973==    at 0x4E87CE2: vfprintf (vfprintf.c:1631)
==18973==    by 0x4E8F898: printf (printf.c:33)
==18973==    by 0x400556: t_1 (a_unitialized_value.c:10)
==18973==    by 0x400533: main (a_unitialized_value.c:4)
==18973== 
b=0
==18973== 
==18973== HEAP SUMMARY:
==18973==     in use at exit: 0 bytes in 0 blocks
==18973==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==18973== 
==18973== All heap blocks were freed -- no leaks are possible
==18973== 
==18973== For counts of detected and suppressed errors, rerun with: -v
==18973== Use --track-origins=yes to see where uninitialised values come from
==18973== ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 0 from 0)
  • Le prime 5 righe di output svolgono la funzione di intestazione: ignorabili dato che il sorgente è chiaro e sopratutto è in nostro possesso.
  • ==18973== Use of uninitialised value of size 8 indica la natura del problema: la libreria itoa.c permette di convertire un valore intero in una stringa (array di char terminante con un un null-pointer, ndr). Memcheck solleva l'eccezione durante la prima conversione (si veda la libreria).
  • Con questo messaggio, Valgrind indica una probabile causa di malfunzionamento, ovvero un errore logico. Memcheck osserva il programma durante le fasi di acquisizione dati (input, ndr), senza interagire con esso o con la sua esecuzione. Memcheck (in questo caso) agisce perché si è tentato di utilizzare un dato non inizializzato (che può compromettere l'output del programma!).
  • In questo caso, b non è inizializzata. Memcheck osserva che la variabile b è passata alla funzione \_IO\_printf e successivamente alla \_IO_vfrprintf ma sino a qui (Memcheck) non solleva alcuna osservazione. Di fatto, Memcheck interviene solamente quando in \_IO_Vfprintf si esamina b per convertire il suo valore nel corrispondente ASCII, sollevando l'uso di una variabile utilizzata ma non inizializzata.
  • Prova a lanciare valgrind gcc file.c e confronta l'output con valgrind ./file.o (prima devi lanciare gcc file.c -o file.o). E' senssato? Cosa cambia? (Suggerimento: in un caso stai osservando l'esecuzione del programma).
  • vfprintf (vfprintf.c:1631),printf (printf.c:33),t_1 (a_unitialized_value.c:10) emain (a_unitialized_value.c:4) costituiscono lo stack trace (chiamate a funzione) che ha generato l'osservazione (di Memcheck in questo caso). Ciò è possibile solo grazie alla compilazione con l'opzione -g che permette di ottenere stack trace leggibili per noi umani, altrimenti avremmo indirizzi esadecimali poco comprensibili.
  • grazie allo stack trace possiamo andare a vedere qual'è il punto più profondo dello stack che risiede nel nostro codice (questo perché assumiamo che le librerie di sistema non hanno alcun bug): lì probabilmente ci sarà il bug. In questo caso dobbiamo andare a a_unitialized_value.c:10.

Stack Smashing

Lo stack smashing accade quando (in linea molto generale) il programma scrive al di fuori di un buffer di sua competenza. Maggiori dettagli qui.

Sorgente

#include <stdio.h>

int main(){
	int a[10];
	a[11]=6;
            printf("%d\n",a[11]);
}

Compilazione e Output

$:~# gcc b_stack_smashing.c -g -o b_01.out
$:~# ./b_01.out 
*** stack smashing detected ***: ./b_01.out terminated
6Aborted (core dumped)

Attenzione: l'output potrebbe dipende dalla presenza del flag -fno-strack-protector (riferimento).

Output con Valgrind

==19167== Memcheck, a memory error detector
==19167== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19167== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19167== Command: ./a.out
==19167== 
*** stack smashing detected ***: ./a.out terminated
6==19167== 
==19167== Process terminating with default action of signal 6 (SIGABRT)
==19167==    at 0x4E6F428: raise (raise.c:54)
==19167==    by 0x4E71029: abort (abort.c:89)
==19167==    by 0x4EB17E9: __libc_message (libc_fatal.c:175)
==19167==    by 0x4F5311B: __fortify_fail (fortify_fail.c:37)
==19167==    by 0x4F530BF: __stack_chk_fail (stack_chk_fail.c:28)
==19167==    by 0x4005E0: main (a_unitialized_value.c:8)
==19167== 
==19167== HEAP SUMMARY:
==19167==     in use at exit: 0 bytes in 0 blocks
==19167==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==19167== 
==19167== All heap blocks were freed -- no leaks are possible
==19167== 
==19167== For counts of detected and suppressed errors, rerun with: -v
==19167== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Aborted (core dumped)
  • Process terminating with default action of signal 6 (SIGABRT) segnale sollevato dalla funzione abort(). Per info man 7 signal e man 3 abort.

Conditional Jump

Sorgente

#include <stdio.h>

int main(){
    int tmp[10];
    printf("tmp is %d\n", tmp[11]);
    return 0;
}

Compilazione ed Output

$:~# gcc -g -fno-stack-protector c_conditional_jump.c -o a.out
$:~# ./a.out 
tmp is 0
$:~# 

Output con Valgrind

==96== Memcheck, a memory error detector
==96== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==96== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==96== Command: ./a.out
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87B83: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Use of uninitialised value of size 8
==96==    at 0x4E8476B: _itoa_word (_itoa.c:179)
==96==    by 0x4E8812C: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E84775: _itoa_word (_itoa.c:179)
==96==    by 0x4E8812C: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E881AF: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87C59: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E8841A: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87CAB: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87CE2: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
tmp is 0
==96== 
==96== HEAP SUMMARY:
==96==     in use at exit: 0 bytes in 0 blocks
==96==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==96== 
==96== All heap blocks were freed -- no leaks are possible
==96== 
==96== For counts of detected and suppressed errors, rerun with: -v
==96== Use --track-origins=yes to see where uninitialised values come from
==96== ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 0 from 0)==96== Memcheck, a memory error detector
==96== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==96== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==96== Command: ./a.out
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87B83: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Use of uninitialised value of size 8
==96==    at 0x4E8476B: _itoa_word (_itoa.c:179)
==96==    by 0x4E8812C: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E84775: _itoa_word (_itoa.c:179)
==96==    by 0x4E8812C: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E881AF: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87C59: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E8841A: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87CAB: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
==96== Conditional jump or move depends on uninitialised value(s)
==96==    at 0x4E87CE2: vfprintf (vfprintf.c:1631)
==96==    by 0x4E8F898: printf (printf.c:33)
==96==    by 0x400541: main (c_conditional_jump.c:6)
==96== 
tmp is 0
==96== 
==96== HEAP SUMMARY:
==96==     in use at exit: 0 bytes in 0 blocks
==96==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==96== 
==96== All heap blocks were freed -- no leaks are possible
==96== 
==96== For counts of detected and suppressed errors, rerun with: -v
==96== Use --track-origins=yes to see where uninitialised values come from
==96== ERROR SUMMARY: 8 errors from 8 contexts (suppressed: 0 from 0)
  • ==96== Conditional jump or move depends on uninitialised value(s) "Scorrere" un vettore, implica il salto ad indirizzi specifici, non necessariamente adiacenti; questo è l'effetto dell'ottimizzazione del compilatore. I riferimenti a printf (e vfprintf) sono dovute al medesimo errore già descritto. La causa è l'accesso all'area di memoria non assegnata alla variabile tmp; nello specifico, si sta cercando di accedere ai 2 byte successivi alla suddetta area. Per ulteriori dettagli prova ad aggiungere l'opzione --track-origins=yes durante l'esecuzione con Valgrind. Sarà più lento, ma avrai più possibilità di scovare l'errore.

Memory Leak

Il software non rilascia la memoria inutilizzata. Le memory leak sono uno dei peggiori errori che valgrind è in grado di intercettare. La pericolosità delle memory leak sta nel fatto che, se un programma perde byte in memoria gira per molto tempo, esso verrà brutalmente terminato quando saturerà l'intera memoria. **Tutti i programmi dovrebbero non avere alcuna memory leak.

Nel codice sottostante per risolvere la memory leak è sufficiente aggiungere free(pa) prima del return 0;.

Sorgente

#include <stdio.h>
#include <stdlib.h>
int main(){
   double* pa = malloc(sizeof(double));
   *pa = 5.4;
   return 0;
}

Compilazione ed Output

Nessun output da segnalare.

Output con Valgrind

==12456== Memcheck, a memory error detector
==12456== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12456== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==12456== Command: ./d_01.o
==12456== 
==12456== 
==12456== HEAP SUMMARY:
==12456==     in use at exit: 8 bytes in 1 blocks
==12456==   total heap usage: 1 allocs, 0 frees, 8 bytes allocated
==12456== 
==12456== 8 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12456==    at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==12456==    by 0x400537: main (d_memory_leak.c:4)
==12456== 
==12456== LEAK SUMMARY:
==12456==    definitely lost: 8 bytes in 1 blocks
==12456==    indirectly lost: 0 bytes in 0 blocks
==12456==      possibly lost: 0 bytes in 0 blocks
==12456==    still reachable: 0 bytes in 0 blocks
==12456==         suppressed: 0 bytes in 0 blocks
==12456== 
==12456== For counts of detected and suppressed errors, rerun with: -v
==12456== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  • HEAP SUMMARY: in use at exit: 8 bytes in 1 blocks total heap usage: 1 allocs, 0 frees, 8 bytes allocated alla fine di ogni esecuzione di un qualsiasi programma, l'area di memoria da esso occupata deve sempre essere liberata. In questo riassunto dell'HEAP (memoria assegnata dinamicamente/casualmente, ndr), è facile intuire che si debba rivedere la parte di liberazione della memoria.

  • 8 bytes in 1 blocks are definitely lost in loss record 1 of 1 Memcheck non è in grado di indicare la motivazione per cui dei blocchi di memoria non vengano rilasciati adeguatamente: per esempio non può capire se hai fatto una malloc di troppo o un free in meno. Come già visto per itoa, valgrind però è in grado di dirti l'istruzione che ha generato la memory leak. In generale esistono due tipi di "leak":

    • definitely lost: la perdita di memoria è reale e concreta; devi quindi rivedere il codice per risolvere pienamente il bug. Un programma sano è un programma in cui i byte definitely lost sono 0.
    • probably lost Valgrind non è sicuro del fatto che il tuo programma abbia una memory leak. Questo accade quando il tuo programma fa cose molto strane con i puntatori (funny things with pointers*, ndr) e questi giri strani confondono valgrind: con probably lost Valgrind ti sta dicendo che molto probabilmente ci sono memory leak, ma non ne può essere sicuro al 100%. In questo caso conviene ripetere il test aggiungendo l'operazione --track-origins=yes.

Invalid Free pt.1

Liberare la medesima area di memoria liberata precedentemente.

Sorgente

#include <stdio.h>
#include <stdlib.h>
int main(){
    double* pb = malloc(sizeof(double));
    printf("\n%p\n\n",(void*)pb);
    free(pb);
    free(pb);
    return 0;
}

Compilazione ed Output

compilato con gcc -g -fno-stack-protector

0x1c7e010

*** Error in `./e_01.o': double free or corruption (fasttop): 0x0000000001c7e010 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x777e5)[0x7fe65a6d87e5]
/lib/x86_64-linux-gnu/libc.so.6(+0x8037a)[0x7fe65a6e137a]
/lib/x86_64-linux-gnu/libc.so.6(cfree+0x4c)[0x7fe65a6e553c]
./e_01.o[0x4005fa]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0)[0x7fe65a681830]
./e_01.o[0x4004e9]
======= Memory map: ========
00400000-00401000 r-xp 00000000 00:21 850                                /root/e_01.o
00600000-00601000 r--p 00000000 00:21 850                                /root/e_01.o
00601000-00602000 rw-p 00001000 00:21 850                                /root/e_01.o
01c7e000-01c9f000 rw-p 00000000 00:00 0                                  [heap]
7fe654000000-7fe654021000 rw-p 00000000 00:00 0 
7fe654021000-7fe658000000 ---p 00000000 00:00 0 
7fe65a44b000-7fe65a461000 r-xp 00000000 00:21 178                        /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe65a461000-7fe65a660000 ---p 00016000 00:21 178                        /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe65a660000-7fe65a661000 rw-p 00015000 00:21 178                        /lib/x86_64-linux-gnu/libgcc_s.so.1
7fe65a661000-7fe65a821000 r-xp 00000000 00:21 33                         /lib/x86_64-linux-gnu/libc-2.23.so
7fe65a821000-7fe65aa21000 ---p 001c0000 00:21 33                         /lib/x86_64-linux-gnu/libc-2.23.so
7fe65aa21000-7fe65aa25000 r--p 001c0000 00:21 33                         /lib/x86_64-linux-gnu/libc-2.23.so
7fe65aa25000-7fe65aa27000 rw-p 001c4000 00:21 33                         /lib/x86_64-linux-gnu/libc-2.23.so
7fe65aa27000-7fe65aa2b000 rw-p 00000000 00:00 0 
7fe65aa2b000-7fe65aa51000 r-xp 00000000 00:21 26                         /lib/x86_64-linux-gnu/ld-2.23.so
7fe65ac47000-7fe65ac4a000 rw-p 00000000 00:00 0 
7fe65ac4d000-7fe65ac50000 rw-p 00000000 00:00 0 
7fe65ac50000-7fe65ac51000 r--p 00025000 00:21 26                         /lib/x86_64-linux-gnu/ld-2.23.so
7fe65ac51000-7fe65ac52000 rw-p 00026000 00:21 26                         /lib/x86_64-linux-gnu/ld-2.23.so
7fe65ac52000-7fe65ac53000 rw-p 00000000 00:00 0 
7ffd0fdc9000-7ffd0fdea000 rw-p 00000000 00:00 0                          [stack]
7ffd0fdf5000-7ffd0fdf7000 r--p 00000000 00:00 0                          [vvar]
7ffd0fdf7000-7ffd0fdf9000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted
  • 0x1c7e010 è l'indirizzo di memoria del puntatore (e non a cui fa riferimento, ndr). Utile per leggere l'output di Valgrind.

Output con Valgrind

==12505== Memcheck, a memory error detector
==12505== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==12505== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==12505== Command: ./e_01.o
==12505== 

0x5204040

==12505== Invalid free() / delete / delete[] / realloc()
==12505==    at 0x4C2ECF0: free (vg_replace_malloc.c:530)
==12505==    by 0x4005F9: main (e_invalid_free.c:8)
==12505==  Address 0x5204040 is 0 bytes inside a block of size 8 free'd
==12505==    at 0x4C2ECF0: free (vg_replace_malloc.c:530)
==12505==    by 0x4005ED: main (e_invalid_free.c:7)
==12505==  Block was alloc'd at
==12505==    at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==12505==    by 0x4005C7: main (e_invalid_free.c:5)
==12505== 
==12505== 
==12505== HEAP SUMMARY:
==12505==     in use at exit: 0 bytes in 0 blocks
==12505==   total heap usage: 2 allocs, 3 frees, 1,032 bytes allocated
==12505== 
==12505== All heap blocks were freed -- no leaks are possible
==12505== 
==12505== For counts of detected and suppressed errors, rerun with: -v
==12505== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
  • Invalid free() / delete / delete[] / realloc() Memcheck ha rilevato l'uso improprio di una di queste funzioni, su un'area di memoria a cui non si ha accesso (o che non è stata assegnata al programma).

  • Address 0x5204040 is 0 bytes inside a block of size 8 free'd Associato alla precedente osservazione, risulta ancor più utile avendo visualizzato l'indirizzo del puntatore nell'HEAP (visibile nell'output, ndr).

  • All heap blocks were freed -- no leaks are possible conferma che è stata liberata tutta l'area dell'HEAP assegnata.

Invalid Free pt.2

Sorgente

#include <stdio.h>
 int main(){
        int tmp2[10];
	tmp2[11] = 6;
	free(tmp2);
    }

Compilazione ed Output

a_unitialized_value.c: In function ‘main’:
a_unitialized_value.c:7:2: warning: implicit declaration of function ‘free’ [-Wimplicit-function-declaration]
  free(tmp2);
  ^
a_unitialized_value.c:7:2: warning: incompatible implicit declaration of built-in function ‘free’
a_unitialized_value.c:7:2: note: include ‘<stdlib.h>’ or provide a declaration of ‘free’
a_unitialized_value.c:7:2: warning: attempt to free a non-heap object ‘tmp2’ [-Wfree-nonheap-object]
  free(tmp2);
  ^

Ed esecuzione:

Segmentation fault (core dumped)

Output con Valgrind

==19361== Memcheck, a memory error detector
==19361== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==19361== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==19361== Command: ./a.out
==19361== 
==19361== Invalid free() / delete / delete[] / realloc()
==19361==    at 0x4C2ECF0: free (vg_replace_malloc.c:530)
==19361==    by 0x400540: main (a_unitialized_value.c:7)
==19361==  Address 0x1fff000aa0 is on thread 1's stack
==19361==  in frame #1, created by main (a_unitialized_value.c:2)
==19361== 
==19361== 
==19361== HEAP SUMMARY:
==19361==     in use at exit: 0 bytes in 0 blocks
==19361==   total heap usage: 0 allocs, 1 frees, 0 bytes allocated
==19361== 
==19361== All heap blocks were freed -- no leaks are possible
==19361== 
==19361== For counts of detected and suppressed errors, rerun with: -v
==19361== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Tool Usati

Memcheck

Fonte: Manuale

Memcheck si occupa di individuare gli errori relativi alla memoria, tematica comune per programmi in C e C++; ecco una lista di quali comportamenti potenzialmente nocivi (del programma in esame) sia in grado di individuare:

  • Accessi illeciti ad indirizzi di memoria, causati ad esempio da:
    • gestione diretta un set di blocchi di memoria
    • sovrascrittura aree di memoria non appartenenti al programma
    • accesso a memoria precedentemente liberata
  • Utilizzo di valori non definiti, ad esempio
    • variabili che non son state inizializzate
    • variabili inizializzate da altre contenenti valore indefinito.
  • Liberare non correttamente la memoria, ripetendo l'operazione o liberando aree di memoria appartenenti ad altri programmi, ad esempio : malloc/new/new[] e free/delete/delete[]
  • Sovrascrivere i puntatori src e dst in funzioni come memcpy.
  • Fornire un valore equivoco (negativo ad esempio) al valore per dimensionare la funzione di allocazione memoria.
  • Perdite di memoria

Mantra Memchek: Memchek ha sempre ragione. cit.

Memcheck fornisce anche il profilo della memoria per l'Execution Trees, aggiungendo l'opzione --xtree-memory ed il comando xtmemory.