Some time ago, there was a merge request to encapsulate all malloc() calls in order to log the amount of bytes that are requested. I had my reservations, as the change set was huge for something that seems rather common to implement: for sure, OAI developers are not the first wanting to trace all memory allocations.
I finally found some time to dig into this, and there seem to be at least two possibilities to achieve the same without large code changes. All code examples can also be found in this github repository.
Replace malloc() with a custom implementation
It is possible to replace malloc() with a custom implementation as described
in the glibc
manual.
This seems to be the easiest way if one wants to simply log the amount of bytes
allocated. For this, one has to “reimplement” malloc() by providing a
definition that will be preferred over the custom allocator. This one in turn
can then call the libc implementation of malloc(), like so:
extern void *__libc_malloc(size_t);
void *malloc(size_t size)
{
void *caller = __builtin_return_address(0);
fprintf(stderr, "%p %ld\n", caller, size);
return __libc_malloc(size);
}
The above gets the callers address, logs it to stderr together with the
amount of bytes, then calls the lib malloc. This code needs to be compiled
into a shared library, and then pre-loaded before starting the process to log
all allocations:
LD_PRELOAD=libmymalloc.so <executable>
will then print <address> <size> each time malloc() is called. In order to
convert an address to a human-readable string, one can use addr2line:
addr2line -p -f -s -e <executable> <address>
Using the glibc memory tracing
glibc also has a memory tracing functionality. However, it seems rather complicated to use if we only want to see the amount of bytes, as some postprocessing is needed.
First, the executable needs to call mtrace() before tracing. Further, we have
to provide a writeable file path (say, mtrace.log), and preload the malloc
debugging library, like so:
MALLOC_TRACE=mtrace.log LD_PRELOAD=/usr/lib64/libc_malloc_debug.so.0 <executable>
(libc_malloc_debug.so.0 might not be installed by default; on Fedora, I had
to install glibc-utils first.)
This will log (de-)allocations in the log file, like
@ ./build/example:[0x4c2] + 0x243e0310 0xc
With the glibc provided mtrace tool, it’s then easy to see memory leaks. On
the other hand, it is not made to print the places where allocations are made
in a human-readable format. The problem for me arised then, though, that it
was necessary to post-process this information and parse an address like
0x4c2 while interpreting it relative to an offset. Thus, it would not be as
easy to get the exact address as in the “custom allocator implementation”.
Nevertheless, once this post-processing exists, it would be easier to get all
output, and not accidently forget something.