A memory leak occurs when memory is allocated but not freed when it is no longer needed. Memory leakage is by no means unique to embedded systems, but it becomes an issue partly because targets don't have much memory in the first place, and partly because they often run for long periods of time without rebooting, allowing the leaks to become a large puddle.
You will realize that there is a leak when you run free
or top
and see that free memory is continually going down, even if you drop caches, as shown in the preceding section. You will be able to identify the culprit (or culprits) by looking at the Uss and Rss per process.
There are several tools for identifying memory leaks in a program. I will look at two: mtrace
and Valgrind
.
mtrace
is a component of glibc that traces calls to malloc(3)
, free(3)
, and related functions, and identifies areas of memory not freed when the program exits. You need to call the mtrace()
function from within the program to begin tracing and then at runtime, write a path name to the MALLOC_TRACE
environment variable in which the trace information is written. If MALLOC_TRACE
does not exist or of the file cannot be opened, mtrace
hooks are not not installed. While the trace information is written in ASCII, it is usual to use the mtrace
command to view it.
Here is an example:
#include <mcheck.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char *argv[]) { int j; mtrace(); for (j = 0; j < 2; j++) malloc(100); /* Never freed:a memory leak */ calloc(16, 16); /* Never freed:a memory leak */ exit(EXIT_SUCCESS); }
Here is what you might see when running the program and looking at the trace:
$ export MALLOC_TRACE=mtrace.log $ ./mtrace-example $ mtrace mtrace-example mtrace.log Memory not freed: ----------------- Address Size Caller 0x0000000001479460 0x64 at /home/chris/mtrace-example.c:11 0x00000000014794d0 0x64 at /home/chris/mtrace-example.c:11 0x0000000001479540 0x100 at /home/chris/mtrace-example.c:15
Unfortunately, mtrace
does not tell you about leaked memory while the program runs. It has to terminate first.
Valgrind is a very powerful tool for discovering memory problems including leaks, and other things besides. One advantage is that you don't have to recompile the programs and libraries that you want to check, although it does work better if they have been compiled with the -g
option so that they include debug symbol tables. It works by running the program in an emulated environment and trapping execution at various points. This leads to the big downside of Valgrind, which is that the program runs at a fraction of normal speed which makes it less useful for testing anything with real-time constraints.
Incidentally, the name is often mispronounced: it says in the Valgrind FAQ that the grind is pronounced with a short i -- as in grinned (rhymes with tinned) rather than grined (rhymes with find). The FAQ, documentation and downloads are available at http://valgrind.org.
Valgrind contains several diagnostic tools:
You can select the tool you want with the -tool
option. Valgrind runs on the major embedded platforms: ARM (Cortex A), PPC, MIPS, and x86 in 32- and 64-bit variants. It is available as a package in both the Yocto Project and Buildroot.
To find our memory leak, we need to use the default memcheck
tool, with the option --leakcheck=full
to print out the lines where the leak was found:
$ valgrind --leak-check=full ./mtrace-example ==17235== Memcheck, a memory error detector ==17235== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==17235== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info ==17235== Command: ./mtrace-example ==17235== ==17235== ==17235== HEAP SUMMARY: ==17235== in use at exit: 456 bytes in 3 blocks ==17235== total heap usage: 3 allocs, 0 frees, 456 bytes allocated ==17235== ==17235== 200 bytes in 2 blocks are definitely lost in loss record 1 of 2 ==17235== at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-linux.so) ==17235== by 0x4005FA: main (mtrace-example.c:12) ==17235== ==17235== 256 bytes in 1 blocks are definitely lost in loss record 2 of 2 ==17235== at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-linux.so) ==17235== by 0x400613: main (mtrace-example.c:14) ==17235== ==17235== LEAK SUMMARY: ==17235== definitely lost: 456 bytes in 3 blocks ==17235== indirectly lost: 0 bytes in 0 blocks ==17235== possibly lost: 0 bytes in 0 blocks ==17235== still reachable: 0 bytes in 0 blocks ==17235== suppressed: 0 bytes in 0 blocks ==17235== ==17235== For counts of detected and suppressed errors, rerun with: -v ==17235== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)