美文网首页
内存泄漏检测

内存泄漏检测

作者: 欧阳_z | 来源:发表于2020-08-26 18:05 被阅读0次

1、使用 mtrace 工具
测试代码如下:

#include <stdlib.h>
#include <stdio.h>
#include <mcheck.h>

int main(void)
{
    setenv("MALLOC_TRACE", "mtrace.log", 1); // set log filename
    mtrace();

    char * p1 = malloc(100);
    free(p1);
    char * p2 = malloc(200);
    char * p3 = malloc(300);
    char * p4 = malloc(400);
    free(p4);

    muntrace();
    return 0;
}

测试结果如下,打印出了内存泄漏的文件名和行号:

$ gcc -g demo.c 
$ ./a.out 
$ mtrace a.out mtrace.log

Memory not freed:
-----------------
           Address     Size     Caller
0x000000000256d5f0     0xc8  at /.../demo.c:12
0x000000000256d6c0    0x12c  at /.../demo.c:13

2、使用 valgrind 工具

下载:
wget https://sourceware.org/pub/valgrind/valgrind-3.16.1.tar.bz2
安装:

tar jxvf valgrind-3.16.1.tar.bz2
cd valgrind-3.16.1/
./configure
make
sudo make install

查看是否安装成功:
valgrind --version

valgrind 测试,有打印出内存泄漏的文件名、函数名、行号 :

$ valgrind --tool=memcheck --leak-check=full ./a.out
==22557== Memcheck, a memory error detector
==22557== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==22557== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==22557== Command: ./a.out
==22557== 
==22557== 
==22557== HEAP SUMMARY:
==22557==     in use at exit: 1,012 bytes in 3 blocks
==22557==   total heap usage: 9 allocs, 6 frees, 2,344 bytes allocated
==22557== 
==22557== 200 bytes in 1 blocks are definitely lost in loss record 1 of 3
==22557==    at 0x4C2DF66: malloc (vg_replace_malloc.c:307)
==22557==    by 0x40068A: main (demo.c:12)
==22557== 
==22557== 300 bytes in 1 blocks are definitely lost in loss record 2 of 3
==22557==    at 0x4C2DF66: malloc (vg_replace_malloc.c:307)
==22557==    by 0x400698: main (demo.c:13)
==22557== 
==22557== 512 bytes in 1 blocks are definitely lost in loss record 3 of 3
==22557==    at 0x4C2DF66: malloc (vg_replace_malloc.c:307)
==22557==    by 0x4EC3F57: mtrace (mtrace.c:296)
==22557==    by 0x400666: main (demo.c:8)
==22557== 
==22557== LEAK SUMMARY:
==22557==    definitely lost: 1,012 bytes in 3 blocks
==22557==    indirectly lost: 0 bytes in 0 blocks
==22557==      possibly lost: 0 bytes in 0 blocks
==22557==    still reachable: 0 bytes in 0 blocks
==22557==         suppressed: 0 bytes in 0 blocks
==22557== 
==22557== For lists of detected and suppressed errors, rerun with: -s
==22557== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

3、自己实现一个简单的检查内存泄漏的代码
思路是把 mallocfree 函数用宏定义替换成自己的函数,然后保存每次申请和释放的情况,最终输出没有释放的地址,并打印出文件名、行号、函数名。
这里只写了一个简单的 demo,只写了mallocfree,还需要再加上 callocrealloc
测试结果,可以打印出内存泄漏位置在 debug_demo.c 的第 15 行的 test_malloc_only 函数内:

$ make
rm -f *.out
gcc debug_malloc.c debug_demo.c -g -D MALLOC_DEBUG
./a.out | uniq
$ ./a.out | uniq
addr2line 0x4007ab -e a.out -fsp; 
$ addr2line 0x4007ab -e a.out -fsp;
test_malloc_only at debug_demo.c:15

代码如下:
debug_malloc.c:

#include <stdio.h>
#include <stdlib.h>
#include <execinfo.h>

#define N 100
static void * call_p[N];
static void * malloc_p[N];
static int malloc_i = 0;

void* debug_malloc(size_t size)
{
    malloc_p[malloc_i] = malloc(size);
    if (NULL == malloc_p[malloc_i])
    {
        exit(-1);
    }
    call_p[malloc_i] = __builtin_return_address(0);
    malloc_i++;
    return malloc_p[malloc_i - 1];
}

void debug_free(void *p)
{
    size_t size = 0;
    int i = 0;
    for (i = 0; i < N; ++i)
    {
        if (malloc_p[i] == p)
        {
            call_p[i] = NULL;
            malloc_p[i] = NULL;
            break;
        }
    }
    free(p);
}

void debug_log()
{
    int i = 0;
    for (i = 0; i < N; ++i)
    {
        if (call_p[i])
        {
            printf("addr2line %p -e a.out -fsp; \n", call_p[i]);
        }
    }
}

debug_demo.c:

#include <stdio.h>
#include <stdlib.h>

#ifdef MALLOC_DEBUG
    extern void* debug_malloc(size_t size);
    extern void* debug_free(void *p);
    extern void debug_log();
    #define malloc(size) debug_malloc(size)
    #define free(p) debug_free(p)
#endif

#define LEN 32
void test_malloc_only()
{
    char *p = (char *)malloc(LEN);
    snprintf(p, LEN, "%s %d", "hello", 666 );
}

void test_malloc_free()
{
    char *p = (char *)malloc(LEN);
    snprintf(p, LEN, "%s %d", "hello", 0 );
    free(p);
}

int main(void)
{
    int i;
    for(i = 0; i < 50; i++)
    {
        test_malloc_only();
        test_malloc_free();
    }
#ifdef MALLOC_DEBUG
    debug_log();
#endif
    return 0;
}

Makefile:

all:
    rm -f *.out
    gcc debug_malloc.c debug_demo.c -g -D MALLOC_DEBUG
    @echo "./a.out | uniq"

也可以考虑把文件名、函数名、行号当做参数传入:

extern void* debug_malloc(size_t size, const char *file, const char *fun, int line);
extern void* debug_free(void *p, const char *file, const char *fun, int line);
#define malloc(size) debug_malloc(size, __FILE__, __FUNCTION__, __LINE__)
#define free(p) debug_free(p, __FILE__, __FUNCTION__, __LINE__)

相关文章

网友评论

      本文标题:内存泄漏检测

      本文链接:https://www.haomeiwen.com/subject/hjefsktx.html