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:
Quizás no esté vinculado a
libc
normal, sino a alguna otra biblioteca de C (por ejemplo,uclibc
) o algo más quevalgrind
no esté esperando. La prueba anterior le mostrará exactamente a qué está vinculado. Para quevalgrind
funcione, usaLD_PRELOAD
para envolver las funcionesmalloc()
yfree()
(descripción de la función general que se ajusta here ). Si su sustituto delibc
no es compatible conLD_PRELOAD
o (de alguna manera) elmalloc()
yfree()
) de la biblioteca C no se están usando (con esos nombres), entoncesvalgrind
no va a funcionar. Tal vez podría incluir la línea de enlace utilizada cuando construye su aplicación.Tiene fugas, pero no está asignando memoria usando
malloc()
. Por ejemplo, podría (improbable) estar haciendo sus propias llamadas abrk()
, o (más probablemente) asignar memoria conmmap
. Puedes usar esto para averiguarlo (esto fue un montón decat
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.
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.
Tal vez
valgrind
simplemente no está trabajando en su plataforma. ¿Qué sucede sivalgrind
un programa muy simple como el que se muestra a continuación y ejecutasvalgrind
en él en tu plataforma? Si esto no funciona, debe averiguar por quévalgrind
no funciona correctamente. Tenga en cuenta quevalgrind
en MIPS es bastante nuevo. Here hay un hilo de correo electrónico donde un desarrollador con MIPS y uclibc descubre quevalgrind
no informa ninguna asignación. Su solución es reemplazarntpl
conlinuxthreads
.
.
#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:
-
valgrind
el problema con el propio programa de prueba devalgrind
. - Confirmado el programa de prueba binario está vinculado dinámicamente a
uclibc
- Confirmado
LD_PRELOAD
está trabajando en tu sistema - 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 .