使用mtrace监控堆内存的分配与释放

应用程序中经常会使用malloc来分配堆内存,mtrace是glibc中提供的用于追踪堆内存分配与释放的功能。

头文件mcheck.h包含了mtrace()和muntrace()的声明。一旦使用了mecheck.h中声明的函数,编译器就会就将程序所使用的malloc()、realloc()、free()、memalign()函数,都会指向mtrace定义的malloc()、realloc()、free()、memalign()函数。调用mtrace()以后,这些函数会尝试将内存分配和释放的行为记录到MALLOC_TRACE环境变量所指定的文件中,并且调用被替换掉的分配、释放函数。如果MALLOC_TRACE环境变量没有被指定,或者指向的不是有效文件,那么mtrace()就不会有任何效果。调用muntrace()以后,也会停止记录内存分配、释放活动。

libmtrace.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <mcheck.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
 
#include "../common.h"
 
void __mtracer_on()     __attribute__((constructor));
void __mtracer_off()    __attribute__((destructor));
 
void __mtracer_on()
{
    char *p = getenv("MALLOC_TRACE");
    /*
     * Format of tracebuf is:
     *  Filename prefix of maximum length of MAX_STR_LEN;
     *  A DOT ".";
     *  Process index with maximum length of MAX_IDX_LEN;
     */
    char tracebuf[MAX_STR_LEN + sizeof(char) + MAX_IDX_LEN + 1];
 
    if (!p)
    {
        p = "mtrace";
    }
 
    sprintf(tracebuf,
        "%." STRINGIFY(MAX_STR_LEN) "s.%." STRINGIFY(MAX_IDX_LEN) "d",
        p, getpid());
    setenv("MALLOC_TRACE", tracebuf, 1);
 
    atexit(&__mtracer_off);
    mtrace();
}
 
void __mtracer_off()
{
    muntrace();
}
common.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#ifndef __COMMON_H__
#define __COMMON_H__
 
#define MAX_STR_LEN     31
#define MAX_IDX_LEN     5
 
/*
 * This is a technique to convert a number defined by macro into literal
 *  string.
 *
 * For example:
 *  STRINGIFY(MAX_STR_LEN)
 * turns into
 *  "31"
 */
#define __STRINGIFY(x)  #x
#define STRINGIFY(x)    __STRINGIFY(x)
 
#endif /* __COMMON_H__ */

用gcc libmtrace.c -fPIC -shared -o libmtrace.so进行编译,产生libmtrace.so文件。

mtrace_me.sh
1
LD_PRELOAD=./libmtrace.so /bin/echo 42

用上面的命令调用echo程序,但是libmtrace会在echo的主函数开始运行之前,就调用mtrace()开始记录内存分配、释放活动。

然后可以使用一个叫做mtrace的Perl脚本小工具,分析产生的日志文件,找到所有没有释放的内存分配。

如果程序包含了编译信息,那么mtrace得到的结果就更详细,可以包含内存泄漏发生的源文件及行号信息。