Valgrind es un debuger de memoria, es capas de detectar fugas donde nuestro programa se come la memora o (memory leaks), violaciones de segmento y varias otras pestes que los programadores solemos meter en el código.
Es compatible con varias arquitecturas: X86/Linux, AMD64/Linux, ARM/Linux, PPC32/Linux, PPC64/Linux, S390X/Linux, ARM/Android (2.3.x), X86/Darwin and AMD64/Darwin (Mac OS X 10.6 and 10.7).
La idea principal de este post es mostrar dos casos de bugs muy comunes en el código de los tantos que hay y como se pueden debuguear con esta herramienta.
Primero vamos a crear un sencillo programa que imprimirá dos lineas en la consola:
int main()
{
int edad = 27;
char nombre[]="Dario Clavijo";
printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);
return 0;
}
No vemos ni errores ni warnings
-Wall indica que se tienen que imprimir todos los warnings
-g indica que se tienen la información de depuración durante la compilación
Ejecutamos:
Hola, soy Dario Clavijo.
y tengo 27 anios.
==2992== Memcheck, a memory error detector
==2992== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==2992== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==2992== Command: ./prueba
==2992==
Hola, soy Dario Clavijo.
y tengo 27 anios.
==2992==
==2992== HEAP SUMMARY:
==2992== in use at exit: 0 bytes in 0 blocks
==2992== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==2992==
==2992== All heap blocks were freed -- no leaks are possible
==2992==
==2992== For counts of detected and suppressed errors, rerun with: -v
==2992== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 5 from 3)
int main()
{
int edad = 27;
char nombre[]="Dario Clavijo";
printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);
return 0;
}
prueba.c: In function ‘main’:
prueba.c:8:7: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat]
prueba.c:6:12: warning: unused variable ‘nombre’ [-Wunused-variable]
Hola, soy ����.
Y tengo 27 anios.
==3050== Memcheck, a memory error detector
==3050== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==3050== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==3050== Command: ./prueba
==3050==
Hola, soy ����.
Y tengo 27 anios.
==3050==
==3050== HEAP SUMMARY:
==3050== in use at exit: 0 bytes in 0 blocks
==3050== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3050==
==3050== All heap blocks were freed -- no leaks are possible
==3050==
==3050== For counts of detected and suppressed errors, rerun with: -v
==3050== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 5 from 3)
Ahora vamos a romper mas todavía nuestro código utilizando una variable no inicializada:
int main()
{
int edad;
char nombre[]="Dario Clavijo";
printf("Hola, soy %s.\n");
printf("Y tengo %d anios.\n", edad);
return 0;
}
prueba.c: In function ‘main’:
prueba.c:8:7: warning: format ‘%s’ expects a matching ‘char *’ argument [-Wformat]
prueba.c:6:12: warning: unused variable ‘nombre’ [-Wunused-variable]
prueba.c:9:13: warning: ‘edad’ is used uninitialized in this function [-Wuninitialized]
Hola, soy ����.
Y tengo 268353524 anios.
==3062== Memcheck, a memory error detector
==3062== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==3062== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==3062== Command: ./prueba
==3062==
Hola, soy �� �.
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE72688: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Use of uninitialised value of size 4
==3062== at 0xFE6CA50: _itoa_word (_itoa.c:195)
==3062== by 0xFE70577: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6CA58: _itoa_word (_itoa.c:195)
==3062== by 0xFE70577: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F6A4: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F710: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F9A8: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F754: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
==3062== Conditional jump or move depends on uninitialised value(s)
==3062== at 0xFE6F788: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
==3062==
Y tengo 268042228 anios.
==3062==
==3062== HEAP SUMMARY:
==3062== in use at exit: 0 bytes in 0 blocks
==3062== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==3062==
==3062== All heap blocks were freed -- no leaks are possible
==3062==
==3062== For counts of detected and suppressed errors, rerun with: -v
==3062== Use --track-origins=yes to see where uninitialised values come from
==3062== ERROR SUMMARY: 24 errors from 8 contexts (suppressed: 5 from 3)
Si nos fijamos en esta porción del dump de valgrind:
==3062== at 0xFE72688: vfprintf@@GLIBC_2.4 (vfprintf.c:1619)
==3062== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==3062== by 0x100004F3: main (prueba.c:9)
En la primera linea nos indica cual es el problema y en la cuarta linea que se trata de el archivo prueba.c y que linea es donde se encuentra el error es la 9.
Este error lo vamos a ver repetidas veces como se utilice la variable no inicializada o sucia por eso vemos los mensajes de "Conditional jump or move depends on uninitialised value(s)" se repiten varias veces."
Ahora vamos a probar con otro error mas severo, partiendo del siguiente codigo:
int main()
{
int edad=27;
char nombre[]="Dario";
char apellido[]="Clavijo";
char letra = 'G';
printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);
printf("Mi nombre completo es %s %c %s.\n",nombre,letra,apellido);
return 0;
}
Incluso al ejecutarlo podemos ver que todo esta correcto:
Hola, soy Dario.
Y tengo 27 anios.
Mi nombre completo es Dario G Clavijo.
int main()
{
int edad=27;
char nombre[]="Dario";
char apellido[]="Clavijo";
char letra = 'G';
printf("Hola, soy %s.\n",nombre);
printf("Y tengo %d anios.\n", edad);
printf("Mi nombre completo es %s %c %s.\n",letra,letra,apellido);
return 0;
}
prueba.c: In function ‘main’:
prueba.c:12:7: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat]
Hola, soy Dario.
Y tengo 27 anios.
Violación de segmento
Obtenemos un error feo (una violación de segmento)
Ahora vamos a debuguear nuestro error feo y tan odiado por valgrind:
==4012== Memcheck, a memory error detector
==4012== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==4012== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==4012== Command: ./prueba
==4012==
Hola, soy Dario.
Y tengo 27 anios.
==4012== Invalid read of size 1
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== Address 0x47 is not stack'd, malloc'd or (recently) free'd
==4012==
==4012==
==4012== Process terminating with default action of signal 11 (SIGSEGV)
==4012== Access not within mapped region at address 0x47
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== If you believe this happened as a result of a stack
==4012== overflow in your program's main thread (unlikely but
==4012== possible), you can try to increase the size of the
==4012== main thread stack using the --main-stacksize= flag.
==4012== The main thread stack size used in this run was 8388608.
Mi nombre completo es ==4012==
==4012== HEAP SUMMARY:
==4012== in use at exit: 0 bytes in 0 blocks
==4012== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==4012==
==4012== All heap blocks were freed -- no leaks are possible
==4012==
==4012== For counts of detected and suppressed errors, rerun with: -v
==4012== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 5 from 3)
Violación de segmento
El mensaje "Address 0x47 is not stack'd, malloc'd or (recently) free'd" nos indica que la dirección donde intento acceder no estaba ni reservada ni liberada.
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== Address 0x47 is not stack'd, malloc'd or (recently) free'd
En la segunda linea nos indica que el programa trato de acceder a una región de memoria no mapeada y por eso se dio un stack overflow.
==4012== Access not within mapped region at address 0x47
==4012== at 0xFFB9A34: strlen (in /usr/lib/valgrind/vgpreload_memcheck-ppc32-linux.so)
==4012== by 0xFE700FF: vfprintf@@GLIBC_2.4 (vfprintf.c:1620)
==4012== by 0xFE78843: printf@@GLIBC_2.4 (printf.c:35)
==4012== by 0x10000543: main (prueba.c:12)
==4012== If you believe this happened as a result of a stack
==4012== overflow in your program's main thread (unlikely but
==4012== possible), you can try to increase the size of the
==4012== main thread stack using the --main-stacksize= flag.
==4012== The main thread stack size used in this run was 8388608.
Mi nombre completo es ==4012==


