src/README.mtrace
Built-in memory tracing/profiling
The basilisk preprocessor (qcc
) can generate code to
trace all calls to malloc()
, calloc()
,
realloc()
, strdup()
and free()
.
This can be used to track the evolution of the amount of memory
dynamically allocated by the program and can automatically detect common
errors such as invalid pointers, double free’s and memory leaks. It is
similar to the mtrace()
functionality implemented in the GNU C library, however it will only
trace calls made by code compiled with qcc
(i.e. it is not
a library implementation). This can be quite useful on systems where
either mtrace()
is not available, or where
mtrace()
does not play well with other libraries (MPI in
particular).
To turn on memory tracing you need to set the macro
MTRACE
to something different from zero, for example
using:
qcc -DMTRACE=1 mycode.c -o mycode -lm
or if you are using makefiles:
CFLAGS=-DMTRACE=1 make bump2D.tst
In addition to the program outputs, the tracing function will output a summary on standard error looking like:
*** MTRACE: max resident set size: 17137664 bytes
*** MTRACE: max traced memory size: 4398337 bytes (tracing overhead 0.1%)
max bytes function file
3567536 mempool_alloc /home/popinet/basilisk/src/grid/mempool.h:52
796672 cache_append /home/popinet/basilisk/src/grid/tree.h:300
495616 cache_level_shrink /home/popinet/basilisk/src/grid/tree.h:290
196608 cache_level_append /home/popinet/basilisk/src/grid/tree.h:273
194400 new_refarray /home/popinet/basilisk/src/grid/tree.h:89
131072 matrix_new /home/popinet/basilisk/src/utils.h:226
5600 realloc_scalar /home/popinet/basilisk/src/grid/tree.h:1234
...
*** MTRACE: To get a graph use: tail -n 2 mtrace | gnuplot -persist
*** MTRACE: No memory leaks
The first line is the maximum system memory usage (in bytes), as
reported by getrusage().ru_maxrss
(if this function is available on your system). The second line is the
maximum amount of memory allocated by the functions traced by code
compiled with qcc
. A breakdown of this amount according to
which function made the call (with the corresponding file and line
numbers) then follows.
Trace file
In addition to the summary profiling information, a file tracing all
calls is also generated. It is called mtrace
by default,
but this can be changed using the MTRACE
environment
variable. For example
MTRACE=/tmp/mtrace CFLAGS=-DMTRACE=1 make bump2D.tst
will use /tmp/trace
instead. When using MPI,
mtrace
will be used for process 0 and
mtrace-1
, mtrace-2
etc… for the other
processes.
There are only two types of records in the file:
- Index definition, something like
@ 20 mempool_new /home/popinet/basilisk/src/grid/mempool.h 31
where @
indicates that this is an index record,
20
is the index number, mempool_new
is the
name of the function containing the call, followed by the file and line
number where the call is made. An index record always precedes the first
use of this index
- Memory function call, something like:
+ 20 3640 2096433 384 17137664
where +
is the type of memory operation, 20
is the index number (i.e. where is the call made), 3640
is
the record number (starting from zero), 2096433
is the
total amount of allocated memory (in bytes) after the call,
384
is the total amount of memory allocated up-to-now by
this specific call (as identified by its index) and
17137664
is the ru_maxrss
value (in bytes) at
this point in time.
The memory operations corresponds to the functions being traced i.e.
+
: memory allocation (i.e. malloc()
,
calloc()
, strdup()
)
-
: memory deallocation (i.e. free()
)
>
: memory allocation by realloc()
<
-
memory deallocation by
realloc()
The last two lines of the file contain commands which can be used by
gnuplot
to get a graph, for example using
gnuplot> set term x11
gnuplot> load "< tail -n2 mtrace"
which will give something like

By default only functions representing more than 1% of the total
allocated memory are displayed. The initial amount of memory reported by
ru_maxrss
(here 1.7e7 bytes) is substracted from the
corresponding curve in the graph.
Memory leak reporting
Note that in the previous graph, all curves (ru_maxrss
excepted) should converge to zero at the end of the trace, otherwise
memory leaks are present. If this the case, a message like this will be
displayed:
...
/home/popinet/basilisk-octree/src/common.h:825: error: 34960 bytes leaked here
/home/popinet/basilisk/src/Makefile.defs:33: recipe for target 'bump2D.tst' failed
make: *** [bump2D.tst] Error 1
(the program exits with an error status of one). The number of bytes leaked is given together with the location of where the leaked memory was allocated.
Other memory errors
Trying to free memory twice or passing invalid pointers to
free()
can also be detected and will lead to the program
aborting. A debugger can then
be used to track the cause of the problem.
Controlling outputs
The degree of verbosity of the profiling can be controlled by
increasing the value of MTRACE
.
MTRACE=1
: Profiling output and memory trace.
MTRACE=2
: Profiling output.
MTRACE=3
: Memory leak errors only.
Accessing the profiling summary
The profiling summary is contained in
struct {
FILE * fp; // trace file
size_t total, max; // current and maximum allocated memory
size_t overhead, maxoverhead; // current and maximum profiling overhead
size_t nr; // current number of records
size_t startrss, maxrss; // starting and maximum system ressource usage
char * fname; // trace file name
} pmtrace;