一款跨平台的c语言内存泄漏检测工具

        在嵌入式开发中,内存泄漏是非常常见而又难以发现的问题,而且对于开发人员来说, 也会经常面临同一套代码在linux,rtos等不同平台进行移植开发的情况。 在基于linux开发时,我们可以借助valgrindmtracedmalloc等工具来发现潜在的问题,这些工具功能强大,需要掌握一些调试使用技巧,对于初学者而言还是需要花费一番功夫的;而在rtos等平台开发时,第三方工具往往就无用武之地了。

        本文介绍一种可以跨平台的内存泄漏检测方式,以c语言代码实现,用户也可以根据这种思路转换为c++或其他语言的实现,下面介绍代码实现。 

  my_mem.h

/*******************************************
 * my_mem.h
 ******************************************/

#ifndef __MY_MEM_H__
#define __MY_MEM_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define sys_malloc malloc
#define sys_free free
#define sys_calloc calloc

void debug_print_mem();

#define MEMORY_LEAK_CHECK 1
#if MEMORY_LEAK_CHECK

void *_my_malloc(char * str, unsigned int size);
void *_my_calloc(char * str, unsigned int count, unsigned int size);
void _my_free(char * str, void * point);

extern char log_buf[];
#define my_malloc(size)(\
    memset(log_buf, 0,64),\
    sprintf(log_buf, "%d:%s:%d",1,__FILE__,__LINE__),\
    (void *)_my_malloc(log_buf, size))


#define my_free(ptr)(\
    memset(log_buf, 0,64),\
    sprintf(log_buf, "%d:%s:%d",1,__FILE__,__LINE__),\
    _my_free(log_buf, ptr))

#define mt_calloc(count,size)(\
    memset(log_buf, 0,64),\
    sprintf(log_buf, "%d:%s:%d",1,__FILE__,__LINE__),\
    (void *)_my_calloc(log_buf, count ,  size))
    
#else
#define my_malloc(n) sys_malloc(n)
#define my_calloc(count,size) sys_calloc(count, size)
#define my_free(p)   sys_free(p)

#endif
#endif

  my_mem.c 

/*******************************************
 * my_mem.c
 ******************************************/


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

#if MEMORY_LEAK_CHECK
static unsigned int mem_count = 0;
char log_buf[64] = {0};
#define FILENAME_LEN 25
#define MEM_MANAGER_NUM 20
typedef struct 
{
    char str[FILENAME_LEN+1];
    unsigned int size;
    unsigned int * ptr;
}mem_manager_t;
static mem_manager_t mem_manager[MEM_MANAGER_NUM] = {0};

void debug_print_mem()
{
    int i;
    printf("mem leak size:%d\n",mem_count);
    for(i=0;i<MEM_MANAGER_NUM;i++){
        if(mem_manager[i].ptr != NULL)
            printf("\t0x%x-%d-%s\n", (unsigned int)mem_manager[i].ptr, mem_manager[i].size,mem_manager[i].str);
    }
}

static int get_free_space(void)
{
    int i;
    for(i=0;i<MEM_MANAGER_NUM;i++){
        if( mem_manager[i].ptr == NULL)
            return i;
    }
    return -1;
}

void * _my_malloc(char * str, unsigned int size)
{
    unsigned int  * ptr;
    int len;
    int i;
    ptr = (unsigned int *)sys_malloc(size);
    
    if( ptr== NULL){
        debug_print_mem();
        printf("malloc no free space1:%s:%d...........................\n",str,size);
	return NULL;
    }
    i = get_free_space();
    if( i == -1){
            printf("malloc no free space2:%s:%d...........................\n",str,size);
	    return NULL;
    }
    len = strlen(str);
    if( len > FILENAME_LEN){
        str += len - FILENAME_LEN;
        len = FILENAME_LEN;
    }
    mem_manager[i].ptr = ptr;
    mem_manager[i].size = size;
    memcpy(mem_manager[i].str, str, len);
    mem_count+= size;
    return ptr;
}



void * _my_calloc(char * str, unsigned int count, unsigned int size)
{
    unsigned int  * ptr;
    int len;
    int i;
    ptr = (unsigned int *)sys_calloc(count, size);
    
    if( ptr== NULL){
        debug_print_mem();
        printf("calloc no free space1:%s:%d...........................\n",str,size);
	return NULL;
    }
    i = get_free_space();
    if( i == -1){
            printf("calloc no free space2:%s:%d...........................\n",str,size);
	    return NULL;
    }
    len = strlen(str);
    if( len > FILENAME_LEN){
        str += len - FILENAME_LEN;
        len = FILENAME_LEN;
    }
    mem_manager[i].ptr = ptr;
    mem_manager[i].size = size;
    memcpy(mem_manager[i].str, str, len);
    mem_count+= size;
    return ptr;
}

void _my_free(char * str, void * point)
{
        int i;
    for(i=0;i<MEM_MANAGER_NUM;i++){
            if( point == mem_manager[i].ptr){
                mem_manager[i].ptr = NULL;
                memset(mem_manager[i].str, 0, FILENAME_LEN);
                mem_count -= mem_manager[i].size;
                break;
            }
    }
    if( i >= MEM_MANAGER_NUM){
        printf("free error:%x,%s.....\r\n",(unsigned int)point,str);
    	return;
    }
    sys_free(point);
}

#else

void debug_print_mem()
{
	printf("memory leak is disable\n");
}

#endif

  main.c

#include <stdio.h>
#include "my_mem.h"
int main()
{
	char * invalid_p;
	char *p = my_malloc(64);
	char *q = my_malloc(128);
	char *fq = my_malloc(32);
	debug_print_mem();
	my_free(p);
	printf("-------------------------\n");
	my_free(invalid_p);
	debug_print_mem();
	return 0;
}

编译,运行结果如下:

randolph@ubuntu:~/test/mem_check$ ./a.out 
mem leak size:224
	0x9b5d008-64-1:main.c:6
	0x9b5d050-128-1:main.c:7
	0x9b5d0d8-32-1:main.c:8
-------------------------
free error:8048af1,1:main.c:12.....
mem leak size:160
	0x9b5d050-128-1:main.c:7
	0x9b5d0d8-32-1:main.c:8

  mem leak size代表了系统中未释放内存的总大小,log中还包含了代码文件的名字和行号,我们可以很清晰的定位内存未释放的地点,以及野指针/空指针释放问题。

  实现原理: 采用宏定义方式封装平台的内存操作函数,记录内存操作的位置和结果。当然该方式需要分配空间来保存内存操作记录,对于内存奇缺的系统,可以采用打印的方式,将内存的分配、释放操作记录打印出来,然后通过脚本分析log来定位问题。

写这篇文章时,也参考了如下链接:

https://blog.csdn.net/u012662731/article/details/78652651

猜你喜欢

转载自blog.csdn.net/u011734326/article/details/87970848