LINUX

Cómo utilizar Valgrind para comprobar la pérdida de memoria

Valgrind es una colección de herramientas de línea de comandos que se pueden utilizar para depurar y perfilar ejecutables en Linux. Memcheck es una de las herramientas más populares del conjunto de herramientas Valgrind que se puede utilizar para detectar errores relacionados con la memoria en un programa ejecutable.

En este tutorial, aprenderemos cómo usar valgrind para verificar si hay fugas de memoria usando la herramienta Memcheck a través de algunos ejemplos.

Verifique las pérdidas de memoria con Memcheck

Memcheck de Valgrind es una herramienta poderosa que puede detectar una variedad de problemas relacionados con la memoria como:

  • Un malloc () sin ningún free ()
  • Un malloc () con más de uno libre ()
  • Variables no inicializadas
  • Acceso a memoria no válido por un puntero de pila
  • etc

Valgrind junto con la herramienta Memcheck se pueden utilizar de la siguiente manera:

valgrind --tool=memcheck --leak-check=yes --track-origins=yes [executable-name]

Entonces, solo reemplace [executable-name] con el nombre ejecutable real de Valgrind y Memcheck para probar y mostrar los errores. Ahora, analicemos cómo se puede usar Memcheck para detectar varios problemas relacionados con la memoria.

NOTACompile todos los ejecutables con el indicador -g (cuando use gcc) para que la herramienta Valgrind pueda producir números de línea en su salida.

1. Detectar variables no inicializadas

Suponga que su programa contiene una (o más) variables no inicializadas.

Por ejemplo :

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
int a;
if(a)
a = a+1;
return 0;
}

Ahora, cuando ejecute la herramienta Memcheck de Valgrind en este código, dará como resultado el siguiente resultado:

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable1
==2897== Memcheck, a memory error detector
==2897== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2897== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2897== Command: ./executable1
==2897==
==2897== Conditional jump or move depends on uninitialised value(s)
==2897== at 0x4004F4: main (valgrind_unini_var.c:7)
==2897== Uninitialised value was created by a stack allocation
==2897== at 0x4004EC: main (valgrind_unini_var.c:5)
==2897==
==2897==
==2897== HEAP SUMMARY:
==2897== in use at exit: 0 bytes in 0 blocks
==2897== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==2897==
==2897== All heap blocks were freed -- no leaks are possible
==2897==
==2897== For counts of detected and suppressed errors, rerun with: -v
==2897== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

Entonces puede ver que Valgrind produjo errores (resaltados en negrita) relacionados con la variable no inicializada ‘a’.

2. Detectar fugas de memoria

Suponga que su programa no contiene free () correspondiente a malloc (). Esto daría lugar a pérdidas de memoria. La herramienta Memcheck se puede utilizar para encontrar fácilmente este tipo de fugas.

A continuación, se muestra un ejemplo de código:

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);

return 0;
}

Aquí está el resultado cuando se usó la herramienta Memcheck de Valgrind para ejecutar el ejecutable compilado a partir del código anterior:

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable2
==2934== Memcheck, a memory error detector
==2934== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2934== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2934== Command: ./executable2
==2934==
==2934==
==2934== HEAP SUMMARY:
==2934== in use at exit: 10 bytes in 1 blocks
==2934== total heap usage: 1 allocs, 0 frees, 10 bytes allocated
==2934==
==2934== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2934== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2934== by 0x40053D: main (valgrind_no_free.c:6)
==2934==
==2934== LEAK SUMMARY:
==2934== definitely lost: 10 bytes in 1 blocks
==2934== indirectly lost: 0 bytes in 0 blocks
==2934== possibly lost: 0 bytes in 0 blocks
==2934== still reachable: 0 bytes in 0 blocks
==2934== suppressed: 0 bytes in 0 blocks
==2934==
==2934== For counts of detected and suppressed errors, rerun with: -v
==2934== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

Entonces puede ver que la pérdida de memoria se detectó fácilmente.

3. Detectar doble libre

A veces, los programadores cometen este tonto error de liberar la misma memoria dos veces. La herramienta Memcheck de Valgrind también se puede utilizar para detectar este tipo de problemas.

Aquí hay un código de ejemplo que contiene double free:

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);
free(ptr);
free(ptr);

return 0;
}

Y aquí está el resultado de la herramienta Memcheck de Valgrind para el código anterior:

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable3
==2961== Memcheck, a memory error detector
==2961== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==2961== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==2961== Command: ./executable3
==2961==
==2961== Invalid free() / delete / delete[] / realloc()
==2961== at 0x4C2BA6C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2961== by 0x4005A9: main (valgrind_double_free.c:8)
==2961== Address 0x51fc040 is 0 bytes inside a block of size 10 free'd
==2961== at 0x4C2BA6C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2961== by 0x40059D: main (valgrind_double_free.c:7)
==2961==
==2961==
==2961== HEAP SUMMARY:
==2961== in use at exit: 0 bytes in 0 blocks
==2961== total heap usage: 1 allocs, 2 frees, 10 bytes allocated
==2961==
==2961== All heap blocks were freed -- no leaks are possible
==2961==
==2961== For counts of detected and suppressed errors, rerun with: -v
==2961== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

Entonces puede ver que el segundo free () se identificó con éxito (resaltado en negrita) como una llamada de función no válida.

4. Detectar operaciones en punteros colgantes

Los punteros colgantes son aquellos que apuntan a un recuerdo que ya está liberado. Memcheck de Valgrind puede detectar fácilmente este tipo de problemas.

Aquí hay un código de ejemplo:

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);
free(ptr);

ptr[3] = 'a';

return 0;
}

Entonces puede ver que el código anterior intenta acceder a la memoria señalada por ‘ptr’ incluso después de que se libera. Así es como Memcheck detecta este error:

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable4
==3393== Memcheck, a memory error detector
==3393== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3393== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3393== Command: ./executable4
==3393==
==3393== Invalid write of size 1
==3393== at 0x4005A6: main (valgrind_no_null.c:9)
==3393== Address 0x51fc043 is 3 bytes inside a block of size 10 free'd
==3393== at 0x4C2BA6C: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3393== by 0x40059D: main (valgrind_no_null.c:7)
==3393==
==3393==
==3393== HEAP SUMMARY:
==3393== in use at exit: 0 bytes in 0 blocks
==3393== total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==3393==
==3393== All heap blocks were freed -- no leaks are possible
==3393==
==3393== For counts of detected and suppressed errors, rerun with: -v
==3393== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

Entonces puede ver que la herramienta detectó la operación de escritura en el puntero colgante y la etiquetó como no válida.

5. Detectar acceso a memoria no válido

A veces, el código contiene un error en el que un puntero intenta acceder fuera de la ubicación de la memoria del montón. Memcheck puede detectar fácilmente este tipo de errores relacionados con la memoria.

Aquí hay un ejemplo :

#include<stdio.h>
#include<stdlib.h>

int main(void)
{
char *ptr = (char*)malloc(10);
ptr[11] = 'z';

return 0;
}

En el código anterior, puede ver que el puntero ‘ptr’ está intentando acceder a una ubicación de memoria que está fuera de sus límites. Así es como Memcheck detecta este problema:

$ valgrind --tool=memcheck --leak-check=yes --track-origins=yes ./executable5
==3538== Memcheck, a memory error detector
==3538== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==3538== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==3538== Command: ./executable5
==3538==
==3538== Invalid write of size 1
==3538== at 0x40059A: main (valgrind_inv_mem.c:7)
==3538== Address 0x51fc04b is 1 bytes after a block of size 10 alloc'd
==3538== at 0x4C2CD7B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==3538== by 0x40058D: main (valgrind_inv_mem.c:6)
==3538==
==3538==
==3538== HEAP SUMMARY:
==3538== in use at exit: 0 bytes in 0 blocks
==3538== total heap usage: 1 allocs, 1 frees, 10 bytes allocated
==3538==
==3538== All heap blocks were freed -- no leaks are possible
==3538==
==3538== For counts of detected and suppressed errors, rerun with: -v
==3538== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)

Para que pueda ver que Memcheck detectó fácilmente (resaltado en negrita) el acceso no válido a la memoria.

Limitaciones de la herramienta Memcheck

Estas son algunas de las limitaciones conocidas del uso de Valgrind:

  • Ralentiza el procesamiento general de su programa.
  • No se pueden detectar desbordamientos de búfer en caso de variables de pila.
  • Consume mucha memoria.

Para saber más sobre la herramienta Memcheck de Valgrind, consulte su manual de usuario.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba
Cerrar