I'm using valgrind (v3.10.0) to hunt down a memory leak in a complex application (a heavily modified build of net-snmp) that is being built as part of a bigger software suite. I am sure there is a leak (the memory footprint of the application grows linearly without bound), but valgrind always reports the following upon termination.
==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
The total heap usage cannot be zero -- there are many, many calls to malloc
and free
throughout the application. Valgrind is still capable of finding "Invalid Write" errors.
The application in question is being compiled, along with other software packages, with a uclibc-gcc toolchain for the MIPS processor (uclibc v0.9.29) to be flashed onto an embedded device running a busybox (v1.17.2) linux shell. I am running valgrind directly on the device. I use the following options when launching Valgrind:
--tool=memcheck --leak-check=full --undef-value-errors=no --trace-children=yes
Basically, Valgrind doesn't detect any heap usage even though I've used the heap. Why might this be? Are any of my assumptions (below) wrong?
What I've Tried
Simple Test Program
I compiled the simple test program (using the same target and toolchain as the application above) from the Valgrind quick-start tutorial, to see if Valgrind would detect the leak. The final output was the same as above: no heap usage.
Linking Issues?
Valgrind documentation has the following to say on their FAQ:
If your program is statically linked, most Valgrind tools will only work well if they are able to replace certain functions, such as malloc, with their own versions. By default, statically linked malloc functions are not replaced. A key indicator of this is if Memcheck says "All heap blocks were freed -- no leaks are possible".
The above sounds exactly like my problem, so I checked to see that it's dynamically linked to the C libraries that contained malloc
and free
. I used the uclibc toolchain's custom ldd
executable (I can't use the native linux ldd
) and the output included the following lines:
libc.so.0 => not found (0x00000000)
/lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x00000000)
(The reason they're not found is because I'm running this on the x86 host device; the mips target device doesn't have an ldd executable.) Based on my understanding, malloc
and free
will be in one of these libraries, and they seem to be dynamically linked. I also did readelf
and nm
on the executable to confirm that the references to malloc
and free
are undefined (which is characteristic of a dynamically linked executable).
Additionally, I tried launching Valgrind with the --soname-synonyms=somalloc=NONE
option as suggested by the FAQ.
LD_PRELOAD support?
As pointed out by commenters and answerers, Valgrind depends upon usage of LD_PRELOAD. It has been suggested that my toolchain doesn't support this feature. In order to confirm that it does, I followed this example to create a simple test library and load it (I replaced rand()
with a function that just returns 42). The test worked, so it would seem that my target supports LD_PRELOAD just fine.
Elf Data
I'll also include some information from the readelf
command which may be useful. Rather than a giant dump, I've trimmed things down to include only what may be relevant.
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