讲mtrace之前,我们先讲讲malloc hook。

 

malloc hook

顾名思义,malloc hook就是malloc的钩子函数。用户可以设置钩子函数,从而在用户使用malloc()或者realloc()申请内存,或者使用free()释放内存时会调用钩子函数。

 

glibc里面定义了如下钩子函数:

void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook)(void);
static void* (*__MALLOC_HOOK_VOLATILE __malloc_hook)(void *ptr, const void* caller);
static void (*__MALLOC_HOOK_VOLATILE __free_hook)(void *ptr, const void* caller);
 
__malloc_initialize_hook()指向的函数在程序启动时被调用,一般用于对钩子函数进行初始化。
__malloc_hook()指向的函数在调用malloc()时被调用。
__free_hook()指向的函数在调用free()时被调用。
 
应用程序如果要使用这个特性,首先需要定义一个钩子初始化函数,然后将这个函数地址赋值给__malloc_initialize_hook指针。这样当程序启动时,就会自动调用这个钩子初始化函数。我们可以在这个钩子初始化函数中,将自定义的钩子函数赋值给__malloc_hook和__free_hook。
 
基于这个特性,我们可以在钩子函数中对内存申请和释放进行统计。
 
mtrace
 
mtrace 就是基于malloc hook实现的一个内存诊断工具。使用者只需要在程序入口处调用mtrace()函数,从而在程序开始运行时就开启内存trace。mtrace()会将内存泄漏报告写到环境变量MALLOC_TRACE指向的一个可写的文件路径,然后我们使用mtrace工具来根据这个报告文件以及可执行程序的binary来生成一份可读的报告。
 
因为这个报告文件只有调用地址,所以需要用addr2line工具转化成代码行,所以编译代码时一定要带上-g,从而在binary中带上符号表。
 
使用步骤如下:
  $ cat t_mtrace.c
           #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);
           }


           $ cc -g t_mtrace.c -o t_mtrace
           $ export MALLOC_TRACE=/tmp/t
           $ ./t_mtrace
           $ mtrace ./t_mtrace $MALLOC_TRACE
           Memory not freed:
           -----------------
              Address     Size     Caller
           0x084c9378     0x64  at /home/cecilia/t_mtrace.c:12
           0x084c93e0     0x64  at /home/cecilia/t_mtrace.c:12
           0x084c9448    0x100  at /home/cecilia/t_mtrace.c:16