views:

499

answers:

4

With Linux/GCC/C++, I'd like to record something to stderr whenever malloc/free/new/delete are called. I'm trying to understand a library's memory allocations, and so I'd like to generate this output while I'm running unit tests. I use valgrind for mem leak detection, but I can't find an option to make it just log allocations.

Any ideas? I'm looking for the simplest possible solution. Recompiling the library is not an option.

+5  A: 

This article (scroll down to the bottom) provides a very clear and concise description of how to override the global new and delete operators in C++ (note that it doesn't provide an example for new[], but it's similar in concept).

As far as overriding malloc and free, since you're working on Linux and with GCC, the easiest method is to use malloc_hook and free_hook. Here is a very good description of how these functions work.

Emerick Rogul
Both of these solutions seem to require recompiling the library in question.
Robert Gamble
+1  A: 

I have not tested this myself, but I am pretty sure these would work:

  • Since you do not want to re-compile the library, giving meaningful output (vs. just "new called for 23 bytes") may require getting a stack trace. I remember using functions to navigate the stack, but I cannot find them right now. Maybe a call to system() and pstack(1) can do the trick.

  • You can re-define operator new and delete, and put this new definition ahead of the std c++ library. This may not capture the calls from containers and standard components that the library in question is using. This would require a relink.

  • Use can use LD_PRELOAD to change operator new and delete dynamically. This would not require a re-link if your application is dynamically linked.

Hope these pointers help, I am sorry I do not have a recipe.

coryan
+12  A: 

You can trace calls to malloc/free with ltrace:

#include <stdlib.h>

int main (void)
{
  void *ptr = malloc(10);
  free(ptr);

  return 0;
}


$ g++ test.cpp -o test
$ ltrace -e malloc,free ./test
malloc(10)                                       = 0x804a008
free(0x804a008)                                  = <void>
+++ exited (status 0) +++

To trace new/delete calls without recompiling you will probably need to use something like LD_PRELOAD to override the calls with your own versions, this is precisely what LeakTracer does which might do what you want.

Robert Gamble
+2  A: 

malloc_hook(3) allows you to globally interpose your own malloc function. (There's __realloc_hook __free_hook etc. as well, I've just left them out for simplicity.)

#include <stdio.h>
#include <malloc.h>

static void *(*old_malloc_hook)(size_t, const void *);

static void *new_malloc_hook(size_t size, const void *caller) {
    void *mem;

    __malloc_hook = old_malloc_hook;
    mem = malloc(size);
    fprintf(stderr, "%p: malloc(%zu) = %p\n", caller, size, mem);
    __malloc_hook = new_malloc_hook;

    return mem;
}

static void init_my_hooks(void) {
    old_malloc_hook = __malloc_hook;
    __malloc_hook = new_malloc_hook;
}

void (*__malloc_initialize_hook)(void) = init_my_hooks;
$ cat >mem.c <<'EOF'
(the code above)
EOF
$ cc -fPIC -shared -o mem.so mem.c
$ LD_PRELOAD=./mem.so ls
0x7ffc14931adc: malloc(5) = 0xb40010
0x7ffc1492c6b0: malloc(120) = 0xb40030
0x7ffc1497f61a: malloc(12) = 0xb40010
0x7ffc1492be38: malloc(776) = 0xb400b0
…

printf might call malloc, which is why we undo the hook temporarily. Be careful of this if when you hook malloc in any way.

ephemient