使用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()以后,也会停止记录内存分配、释放活动。

#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();
}
#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文件。

LD_PRELOAD=./libmtrace.so /bin/echo 42

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

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

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