在嵌入式开发中,内存泄漏是非常常见而又难以发现的问题,而且对于开发人员来说, 也会经常面临同一套代码在linux,rtos等不同平台进行移植开发的情况。 在基于linux开发时,我们可以借助valgrind、mtrace、dmalloc等工具来发现潜在的问题,这些工具功能强大,需要掌握一些调试使用技巧,对于初学者而言还是需要花费一番功夫的;而在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来定位问题。
写这篇文章时,也参考了如下链接: