Trace memory alloction

2026/03/02


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.