¿Cómo uso valgrind para encontrar memory leaks?

¿Cómo uso valgrind para encontrar las pérdidas de memoria en un progtwig?

¿Alguien me puede ayudar y describir los pasos para llevar a cabo el procedimiento?

Estoy usando Ubuntu 10.04 y tengo un progtwig de ac favor, ayúdenme.

Prueba esto:

valgrind --leak-check=full -v ./your_program

Siempre que valgrind esté instalado, pasará por su progtwig y le dirá lo que está mal. Puede darle punteros y lugares aproximados donde se pueden encontrar las fugas. Si está segmentando, intente ejecutarlo a través de gdb .

Cómo ejecutar Valgrind

Me gustaría construir una explicación más detallada sobre cómo usar Valgrind de manera efectiva y cómo resolver memory leaks. Para no insultar al OP, pero para aquellos que llegan a esta pregunta y aún son nuevos en Linux, es posible que deba instalar Valgrind en su sistema.

 sudo apt install valgrind # Ubuntu, Debian, etc. sudo yum install valgrind # RHEL, CentOS, Fedora, etc. 

Valgrind se puede usar fácilmente para el código C / C ++, pero incluso se puede usar para otros idiomas cuando se configura correctamente (ver esto para Python).

Para ejecutar valgrind , pasa el ejecutable como un argumento (junto con cualquier parámetro al progtwig).

 valgrind --leak-check=full \ --show-leak-kinds=all \ --track-origins=yes \ --verbose \ --log-file=valgrind-out.txt \ ./executable exampleParam1 

Esto producirá un informe al final de la ejecución de su progtwig que (con suerte) se ve así:

 HEAP SUMMARY: in use at exit: 0 bytes in 0 blocks total heap usage: 636 allocs, 636 frees, 25,393 bytes allocated All heap blocks were freed -- no leaks are possible ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) 

Tengo una filtración, pero ¿ DÓNDE ?

Entonces, tienes una pérdida de memoria, y Valgrind no dice nada significativo. Tal vez, algo como esto:

 5 bytes in 1 blocks are definitely lost in loss record 1 of 1 at 0x4C29BE3: malloc (vg_replace_malloc.c:299) by 0x40053E: main (in /home/Peri461/Documents/executable) 

Echemos un vistazo al código C que también escribí:

 #include  int main() { char* string = malloc(5 * sizeof(char)); //LEAK: not freed! return 0; } 

Bueno, hubo 5 bytes perdidos. ¿Como paso? El informe de error solo dice main y malloc . En un progtwig más grande, sería seriamente problemático cazar. Esto se debe a la forma en que se compiló el ejecutable . De hecho, podemos obtener detalles línea por línea sobre lo que salió mal. Recompile su progtwig con un indicador de depuración (estoy usando gcc aquí):

 gcc -o executable -std=c11 -Wall main.c # suppose it was this at first gcc -o executable -std=c11 -Wall -ggdb3 main.c # add -ggdb3 to it 

Ahora con esta construcción de depuración, Valgrind apunta a la línea exacta de código que asigna la memoria que se filtró. (La redacción es importante: puede que no sea exactamente dónde está tu fuga, pero qué se filtró. La huella te ayuda a encontrar dónde ).

 5 bytes in 1 blocks are definitely lost in loss record 1 of 1 at 0x4C29BE3: malloc (vg_replace_malloc.c:299) by 0x40053E: main (main.c:4) 

Técnicas para depurar memory leaks y errores

  • ¡Haga uso de http://www.cplusplus.com ! Tiene una gran documentación sobre las funciones de C / C ++.
  • Consejos generales para memory leaks:
    • Asegúrese de que su memoria asignada dinámicamente se libere.
    • No asigne memoria y olvide asignar el puntero.
    • No sobrescriba un puntero con uno nuevo a menos que se libere la memoria anterior.
  • Consejos generales para errores de memoria:
    • Acceda y escriba en direcciones e índices que está seguro que le pertenecen. Los errores de memoria son diferentes de las filtraciones; a menudo son solo problemas de tipo IndexOutOfBoundsException .
    • No acceda ni escriba en la memoria después de liberarlo.
  • A veces, Valgrind no siempre apunta exactamente a dónde está tu error. A veces tienes muchos errores y resolver uno de ellos resuelve una cascada de otros.
    • Enumere las funciones en su código que dependen de / son dependientes del código “ofensivo” que tiene el error de memoria. Siga la ejecución del progtwig (tal vez incluso en gdb ) y busque errores de precondición / poscondición.
    • Intente comentar el bloque de código “ofensivo” (dentro de lo razonable, de modo que su código aún comstack). Si el error Valgrind desaparece, has encontrado dónde está.
  • Si todo lo demás falla, intente buscarlo. ¡Valgrind también tiene documentación !

Una mirada a fugas comunes y errores

Mire sus punteros

 60 bytes in 1 blocks are definitely lost in loss record 1 of 1 at 0x4C2BB78: realloc (vg_replace_malloc.c:785) by 0x4005E4: resizeArray (main.c:12) by 0x40062E: main (main.c:19) 

Y el código:

 #include  #include  struct _List { int32_t* data; int32_t length; }; typedef struct _List List; List* resizeArray(List* array) { int32_t* dPtr = array->data; dPtr = realloc(dPtr, 15 * sizeof(int32_t)); //doesn't update array->data return array; } int main() { List* array = calloc(1, sizeof(List)); array->data = calloc(10, sizeof(int32_t)); array = resizeArray(array); free(array->data); free(array); return 0; } 

Como asistente de enseñanza, he visto este error a menudo. El alumno utiliza una variable local y olvida actualizar el puntero original. El error aquí es darse cuenta de que realloc puede mover la memoria asignada a otro lugar y cambiar la ubicación del puntero. A continuación, dejamos resizeArray sin indicar array->data donde se movió la matriz.

Escritura inválida

 1 errors in context 1 of 1: Invalid write of size 1 at 0x4005CA: main (main.c:10) Address 0x51f905a is 0 bytes after a block of size 26 alloc'd at 0x4C2B975: calloc (vg_replace_malloc.c:711) by 0x400593: main (main.c:5) 

Y el código:

 #include  #include  int main() { char* alphabet = calloc(26, sizeof(char)); for(uint8_t i = 0; i < 26; i++) { *(alphabet + i) = 'A' + i; } *(alphabet + 26) = '\0'; //null-terminate the string? free(alphabet); return 0; } 

Tenga en cuenta que Valgrind nos señala la línea de código comentada más arriba. La matriz de tamaño 26 está indexada [0,25], por lo que *(alphabet + 26) es una escritura no válida, está fuera de límites. Una escritura inválida es un resultado común de errores de uno por uno. Mire el lado izquierdo de su operación de asignación.

Lectura inválida

 1 errors in context 1 of 1: Invalid read of size 1 at 0x400602: main (main.c:9) Address 0x51f90ba is 0 bytes after a block of size 26 alloc'd at 0x4C29BE3: malloc (vg_replace_malloc.c:299) by 0x4005E1: main (main.c:6) 

Y el código:

 #include  #include  int main() { char* destination = calloc(27, sizeof(char)); char* source = malloc(26 * sizeof(char)); for(uint8_t i = 0; i < 27; i++) { *(destination + i) = *(source + i); //Look at the last iteration. } free(destination); free(source); return 0; } 

Valgrind nos señala la línea comentada arriba. Mira la última iteración aquí, que es
*(destination + 26) = *(source + 26); . Sin embargo, *(source + 26) está fuera de límites nuevamente, de manera similar a la escritura inválida. Las lecturas no válidas también son un resultado común de errores uno a uno. Mire el lado derecho de su operación de asignación.


The Open Source (U / Dys) topia

¿Cómo puedo saber cuándo la fuga es mía? ¿Cómo puedo encontrar mi fuga cuando estoy usando el código de otra persona? Encontré una filtración que no es mía; debería hacer algo? Todas son preguntas legítimas. Primero, 2 ejemplos del mundo real que muestran 2 clases de encuentros comunes.

Jansson : una biblioteca JSON

 #include  #include  int main() { char* string = "{ \"key\": \"value\" }"; json_error_t error; json_t* root = json_loads(string, 0, &error); //obtaining a pointer json_t* value = json_object_get(root, "key"); //obtaining a pointer printf("\"%s\" is the value field.\n", json_string_value(value)); //use value json_decref(value); //Do I free this pointer? json_decref(root); //What about this one? Does the order matter? return 0; } 

Este es un progtwig simple: lee una cadena JSON y la analiza. En proceso, usamos llamadas de biblioteca para hacer el análisis sintáctico para nosotros. Jansson realiza las asignaciones necesarias dinámicamente ya que JSON puede contener estructuras anidadas de sí mismo. Sin embargo, esto no significa que decref o " decref " la memoria que nos ha dado de cada función. De hecho, este código que escribí arriba arroja una "lectura inválida" y una "escritura inválida". Esos errores desaparecen cuando decref línea de decref por value .

¿Por qué? El value variable se considera una "referencia prestada" en la API de Jansson. Jansson realiza un seguimiento de su memoria para usted, y simplemente tiene que decref estructuras JSON independientes entre sí. La lección aquí: lee la documentación . De Verdad. A veces es difícil de entender, pero te dicen por qué ocurren estas cosas. En cambio, tenemos preguntas existentes sobre este error de memoria.

SDL : una biblioteca de gráficos y juegos

 #include "SDL2/SDL.h" int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_AUDIO) != 0) { SDL_Log("Unable to initialize SDL: %s", SDL_GetError()); return 1; } SDL_Quit(); return 0; } 

¿Qué pasa con este código ? Constantemente gotea ~ 212 KiB de memoria para mí. Tómese un momento para pensarlo. Encendemos SDL y luego apagamos. ¿Responder? No hay nada malo.

Eso puede sonar extraño al principio . A decir verdad, los gráficos son complicados y a veces hay que aceptar algunas filtraciones como parte de la biblioteca estándar. La lección aquí: no necesitas reprimir todas las pérdidas de memoria . Algunas veces solo necesitas suprimir las filtraciones porque son problemas conocidos de los que no puedes hacer nada . (¡Este no es mi permiso para ignorar tus propias filtraciones!)

Respuestas al vacío

¿Cómo puedo saber cuándo la fuga es mía?
Es. (99% seguro, de todos modos)

¿Cómo puedo encontrar mi fuga cuando estoy usando el código de otra persona?
Es probable que alguien más ya lo haya encontrado. Pruebe Google Si eso falla, usa las habilidades que te di más arriba. Si eso falla y la mayoría de las veces ve llamadas API y poco de su propio seguimiento de stack, vea la siguiente pregunta.

Encontré una filtración que no es mía; debería hacer algo?
¡Sí! La mayoría de las API tienen formas de informar errores y problemas. ¡Usalos, usalos a ellos! ¡Ayuda a devolver las herramientas que estás utilizando en tu proyecto!


Otras lecturas

Gracias por quedarte conmigo todo este tiempo. Espero que hayas aprendido algo, ya que intenté atender al amplio espectro de personas que llegaban a esta respuesta. Algunas cosas que espero que haya preguntado en el camino: ¿Cómo funciona el asignador de memoria de C? ¿Qué es en realidad una fuga de memoria y un error de memoria? ¿En qué se diferencian de los segfaults? ¿Cómo funciona Valgrind? Si tiene alguno de estos, por favor alimente su curiosidad:

  • Más sobre malloc , el asignador de memoria de C
  • Definición de una falla de segmentación
  • Definición de una pérdida de memoria
  • Definición de un error de acceso a la memoria
  • ¿Cómo funciona Valgrind?

Tu puedes correr:

 valgrind --leak-check=full --log-file="logfile.out" -v [your_program(and its arguments)]