usar lost leaks how definitely como c memory-management memory-leaks mips valgrind

c - lost - valgrind ubuntu



Valgrind en los informes MIPS no uso de montón (3)

Estoy usando valgrind (v3.10.0) para buscar una pérdida de memoria en una aplicación compleja (una compilación muy modificada de net-snmp) que se está construyendo como parte de una suite de software más grande. Estoy seguro de que hay una fuga (la huella de memoria de la aplicación crece linealmente sin límite), pero valgrind siempre informa lo siguiente al finalizar.

==1139== HEAP SUMMARY: ==1139== in use at exit: 0 bytes in 0 blocks ==1139== total heap usage: 0 allocs, 0 frees, 0 bytes allocated ==1139== ==1139== All heap blocks were freed -- no leaks are possible

El uso total del montón no puede ser cero: hay muchas, muchas llamadas a malloc y free toda la aplicación. Valgrind todavía es capaz de encontrar errores de "Escritura no válida".

La aplicación en cuestión se está compilando, junto con otros paquetes de software, con una cadena de herramientas uclibc-gcc para el procesador MIPS (uclibc v0.9.29) que se incluirá en un dispositivo integrado que ejecuta una shell de Linux de busybox (v1.17.2). Estoy ejecutando valgrind directamente en el dispositivo. Utilizo las siguientes opciones al iniciar Valgrind:

--tool=memcheck --leak-check=full --undef-value-errors=no --trace-children=yes

Básicamente, Valgrind no detecta ningún uso del montón a pesar de que he usado el montón. ¿Por qué podría ser esto? ¿Alguna de mis suposiciones (abajo) es incorrecta?

Lo que he intentado

Programa de prueba simple

Compilé el programa de prueba simple (usando el mismo objetivo y cadena de herramientas que la aplicación anterior) del tutorial de inicio rápido de Valgrind, para ver si Valgrind detectaría la fuga. La salida final fue la misma que la anterior: sin uso de pila.

Problemas de vinculación?

La documentación de Valgrind tiene lo siguiente que decir en sus preguntas frecuentes :

Si su programa está vinculado estáticamente, la mayoría de las herramientas de Valgrind solo funcionarán bien si son capaces de reemplazar ciertas funciones, como malloc, con sus propias versiones. Por defecto, las funciones malloc enlazadas estáticamente no se reemplazan. Un indicador clave de esto es si Memcheck dice "Se liberaron todos los bloques de almacenamiento dinámico, no hay fugas".

Lo anterior suena exactamente igual a mi problema, así que verifiqué que esté enlazado dinámicamente a las bibliotecas de C que contenían malloc y free . ldd ejecutable ldd personalizado de uclibc toolchain ( no puedo usar el nativo linux ldd ) y la salida incluía las siguientes líneas:

libc.so.0 => not found (0x00000000) /lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x00000000)

(La razón por la que no se encuentran es porque estoy ejecutando esto en el dispositivo host x86; el dispositivo de destino mips no tiene un ejecutable ldd). Según mi entendimiento, malloc y free will estarán en una de estas bibliotecas, y parecen estar vinculados dinámicamente. También readelf y nm en el ejecutable para confirmar que las referencias a malloc y free no están definidas (lo que es característico de un ejecutable enlazado dinámicamente).

Además, intenté lanzar Valgrind con la --soname-synonyms=somalloc=NONE según lo sugerido por las preguntas frecuentes.

Soporte LD_PRELOAD?

Como lo señalaron los comentaristas y los que respondieron, Valgrind depende del uso de LD_PRELOAD. Se ha sugerido que mi cadena de herramientas no es compatible con esta característica. Para confirmar que sí, seguí este ejemplo para crear una biblioteca de prueba simple y cargarla (reemplazé rand() con una función que solo devuelve 42). La prueba funcionó, por lo que parece que mi objetivo es compatible con LD_PRELOAD bien.

Datos de Elf

También readelf información del comando readelf que puede ser útil. En lugar de un volcado gigante, he recortado las cosas para incluir solo lo que puede ser relevante.

Dynamic section Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libnetsnmpagent.so.30] 0x00000001 (NEEDED) Shared library: [libnetsnmpmibs.so.30] 0x00000001 (NEEDED) Shared library: [libnetsnmp.so.30] 0x00000001 (NEEDED) Shared library: [libgcc_s.so.1] 0x00000001 (NEEDED) Shared library: [libc.so.0] 0x0000000f (RPATH) Library rpath: [//lib] Symbol table ''.dynsym'' Num: Value Size Type Bind Vis Ndx Name 27: 00404a40 0 FUNC GLOBAL DEFAULT UND free 97: 00404690 0 FUNC GLOBAL DEFAULT UND malloc


Para confirmar que el ejecutable no está enlazado estáticamente, ejecuté el archivo snmpd

Lo más probable es que su problema no sea que el binario esté estáticamente vinculado (ahora sabe que no lo está), sino que malloc y free estén estáticamente vinculados (quizás esté utilizando una implementación alternativa de malloc, como tcmalloc ).

Cuando creó el caso de prueba simple (en el que Valgrind funcionó correctamente), probablemente no usó la misma línea de comando de enlace (y las mismas bibliotecas) que su aplicación real.

En cualquier caso, es trivial comprobar:

readelf -Ws snmpd | grep '' malloc''

Si esto muestra UND (es decir, no definido), el Valgrind no debería tener problemas para interceptarlo. Pero es probable que muestre FUNC GLOBAL DEFAULT ... malloc lugar, lo que significa que su snmpd es tan bueno como estáticamente vinculado en lo que respecta a valgrind.

Suponiendo que mi suposición es correcta, snmpd vincular snmpd con -Wl,-y,malloc flag. Eso te dirá qué biblioteca define tu malloc . Retírelo del enlace, busque y corrija la fuga, luego decida si tener esa biblioteca vale la pena que le ha causado.


Primero, hagamos una prueba real para ver si algo está vinculado estáticamente.

$ ldd -v /bin/true linux-vdso.so.1 => (0x00007fffdc502000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0731e11000) /lib64/ld-linux-x86-64.so.2 (0x00007f07321ec000) Version information: /bin/true: libc.so.6 (GLIBC_2.3) => /lib/x86_64-linux-gnu/libc.so.6 libc.so.6 (GLIBC_2.3.4) => /lib/x86_64-linux-gnu/libc.so.6 libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6 libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6 libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6 /lib/x86_64-linux-gnu/libc.so.6: ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2 ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2

La segunda línea en la salida muestra que está enlazado dinámicamente a libc , que es lo que contiene malloc .

En cuanto a lo que podría ir mal, puedo sugerir cuatro cosas:

  1. Quizás no esté vinculado a libc normal, sino a alguna otra biblioteca de C (por ejemplo, uclibc ) o algo más que valgrind no esté esperando. La prueba anterior le mostrará exactamente a qué está vinculado. Para que valgrind funcione, usa LD_PRELOAD para envolver las funciones malloc() y free() (descripción de la función general que se ajusta here ). Si su sustituto de libc no es compatible con LD_PRELOAD o (de alguna manera) el malloc() y free() ) de la biblioteca C no se están usando (con esos nombres), entonces valgrind no va a funcionar. Tal vez podría incluir la línea de enlace utilizada cuando construye su aplicación.

  2. Tiene fugas, pero no está asignando memoria usando malloc() . Por ejemplo, podría (improbable) estar haciendo sus propias llamadas a brk() , o (más probablemente) asignar memoria con mmap . Puedes usar esto para averiguarlo (esto fue un montón de cat sí).

.

$ cat /proc/PIDNUMBERHERE/maps 00400000-0040b000 r-xp 00000000 08:01 805303 /bin/cat 0060a000-0060b000 r--p 0000a000 08:01 805303 /bin/cat 0060b000-0060c000 rw-p 0000b000 08:01 805303 /bin/cat 02039000-0205a000 rw-p 00000000 00:00 0 [heap] 7fbc8f418000-7fbc8f6e4000 r--p 00000000 08:01 1179774 /usr/lib/locale/locale-archive 7fbc8f6e4000-7fbc8f899000 r-xp 00000000 08:01 1573024 /lib/x86_64-linux-gnu/libc-2.15.so 7fbc8f899000-7fbc8fa98000 ---p 001b5000 08:01 1573024 /lib/x86_64-linux-gnu/libc-2.15.so 7fbc8fa98000-7fbc8fa9c000 r--p 001b4000 08:01 1573024 /lib/x86_64-linux-gnu/libc-2.15.so 7fbc8fa9c000-7fbc8fa9e000 rw-p 001b8000 08:01 1573024 /lib/x86_64-linux-gnu/libc-2.15.so 7fbc8fa9e000-7fbc8faa3000 rw-p 00000000 00:00 0 7fbc8faa3000-7fbc8fac5000 r-xp 00000000 08:01 1594541 /lib/x86_64-linux-gnu/ld-2.15.so 7fbc8fca6000-7fbc8fca9000 rw-p 00000000 00:00 0 7fbc8fcc3000-7fbc8fcc5000 rw-p 00000000 00:00 0 7fbc8fcc5000-7fbc8fcc6000 r--p 00022000 08:01 1594541 /lib/x86_64-linux-gnu/ld-2.15.so 7fbc8fcc6000-7fbc8fcc8000 rw-p 00023000 08:01 1594541 /lib/x86_64-linux-gnu/ld-2.15.so 7fffe1674000-7fffe1695000 rw-p 00000000 00:00 0 [stack] 7fffe178d000-7fffe178f000 r-xp 00000000 00:00 0 [vdso] ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]

Tenga en cuenta si la dirección final de [heap] realmente está creciendo, o si está viendo entradas de mmap adicionales. Otro buen indicador de si valgrind está funcionando es enviar un SIGSEGV o similar al proceso y ver si ve un montón en uso al salir.

  1. No se está filtrando en el sentido estricto, pero se está filtrando a todos los efectos y propósitos. Por ejemplo, quizás tenga una estructura de datos (como un caché), que crece con el tiempo. Al salir, el programa (correctamente) libera todas las entradas en el caché. Entonces, al salir, nada está en uso en el montón. En este caso, querrás saber qué está creciendo. Esta es una proposición más difícil. Usaría la técnica para matar el programa (arriba), capturar la salida y procesarla posteriormente. Si ve 500 cosas después de 24 horas, 1,000 después de 48 horas y 1,500 después de 72 horas, eso le dará una indicación de lo que está "goteando". Sin embargo, como señala Haris en los comentarios, si bien esto provocaría que la memoria no se muestre como fugas, no explica que el ''uso total del montón'' sea cero, ya que esto describe las asignaciones totales realizadas y liberadas.

  2. Tal vez valgrind simplemente no está trabajando en su plataforma. ¿Qué sucede si valgrind un programa muy simple como el que se muestra a continuación y ejecutas valgrind en él en tu plataforma? Si esto no funciona, debe averiguar por qué valgrind no funciona correctamente. Tenga en cuenta que valgrind en MIPS es bastante nuevo. Here hay un hilo de correo electrónico donde un desarrollador con MIPS y uclibc descubre que valgrind no informa ninguna asignación. Su solución es reemplazar ntpl con linuxthreads .

.

#include <stdio.h> #include <stdlib.h> int main (int argc, char **argv) { void *p = malloc (100); /* does not leak */ void *q = malloc (100); /* leaks */ free (p); exit (0); }


(Agregar otra respuesta ya que la pregunta en sí ha cambiado sustancialmente después de que OP otorgó la primera recompensa)

Según mi comprensión de sus ediciones, ahora tiene:

  1. valgrind el problema con el propio programa de prueba de valgrind .
  2. Confirmado el programa de prueba binario está vinculado dinámicamente a uclibc
  3. Confirmado LD_PRELOAD está trabajando en tu sistema
  4. Confirmado (aunque solo sea usando el programa de prueba) que esto no es una interferencia de símbolos de otra biblioteca

Para mí, eso indica que valgrind tiene un error o es incompatible con su cadena de herramientas. Encontré referencias para decir que debería funcionar con su cadena de herramientas, por lo que eso implica que hay un error de cualquier manera.

Por lo tanto, sugiero que informe un error utilizando el mecanismo descrito here . Tal vez deje de lado su aplicación complicada, y simplemente señale que el programa de prueba simple no funciona. Si aún no lo ha hecho, puede probar la lista de correo de los usuarios como se describe here .