loop_in_codes

低调做技术__欢迎移步我的独立博客 codemaro.com 微博 kevinlynx

自己写内存泄露检测库

author: kevin lynx

这个内存泄露工具最基本的原理就是利用宏替换掉标准的malloc、free(暂不考虑其他内存分配函数,
如realloc、strdup),记录下每次内存分配和释放动作。因为宏的处理发生在预处理阶段,所以可以
很容易地用你自己的malloc函数替换掉标准的malloc。例如:

/* lib.h */
#define malloc my_malloc
#define free my_free 

/* lib.c */
/* disable these macro in this compile unit */
#undef malloc
#undef free 

static int count = 0

void *my_malloc( size_t size )
{
    
++count;
    
return malloc( size );
}
 

void my_free( void *a )
{
    
--count;
    free( a );
}
 


要使用以上代码,用户在使用时就需要包含lib.h,从而可以使用宏将用户自己写的malloc替换
为my_mallo。当程序退出时,如果count大于0,那么可以肯定的是有内存泄露。当然,如果
count为负数,则很可能对同一个指针进行多次free。

但是以上代码的功能太局限了。一个真正的内存泄露检测库(工具),至少需要报告泄露的代码
文件、函数、行数等信息。当然,如果能报告调用堆栈,就更好了。不过这就依赖于具体的平台,
需要使用特定的系统接口才可以获取出。

要实现以上功能也很简单,只需要在每次调用malloc的时候,通过编译器预定义宏__FILE__、
__LINE__、__FUNCTION__(__func__)就可以得到文件名、函数、行号等信息。将这些信息保存
起来,然后在free的时候移除相应的信息即可。

最简单的实现方式,就是保存一个表,表里记录着每次分配内存的信息:

 

struct memRecord
{
    
char file[MAX_FILE_NAME];
    
char func[MAX_FUNC_NAME];
    size_t lineno;
    
void *address;
    size_t size;
}


struct memRecord mem_record[MAX_RECORD]; 

 

但是,通过单单一个free函数的void*参数,如何获取出对应的分配记录呢?难道:

for( size_t i = 0; i < MAX_RECORD; ++ i )
{
    
if( address == mem_record[i].address ) 
    
{
        
/* shit */
    }

}
 

 

虽然可行,但是很stupid。这里提供一个小技巧:

 

void *my_malloc( size_t size )
{
    
void *= malloc( size + sizeof( size_t ) );
    
return (char*)a + sizeof( size_t );
}
 

void my_free( void *a )
{
    
/* actually, 'a' is not the real address */
    
char *= ((char*)a - sizeof( size_t ) );    
    free( p );
}

 

意思就是说,我多分配了4字节内存(sizeof( size_t ) ),用于保存这次分配记录在mem_record
中被保存的索引。在释放内存的时候,通过一些地址偏移计算,就可以获取出真正的系统malloc
返回的地址,从而安全释放(别给我说这里的计算存在平台和编译器的限制,没认真看文章的SB才说)。

另一个问题是,我们如何处理每次分配释放时,对于分配记录那个数据结构,也就是mem_record。
每一次free的时候,移除的记录可能位于mem_record的任何位置。一定时间后,mem_record内部
将出现很多漏洞(已经没用的数组位置)。解决这个问题最直接当然还是最stupid的方法,就是每次
free移除记录时重新整理一遍mem_record。如果你这样做了,那你的malloc/free比微软的还慢。

我的解决方法是:
size_t free_index[MAX_RECORD];
用于保存这些出现漏洞的index。每一次free移除记录时,就把这个记录对应的inex保存进来,表示
这个index指向的mem_record[]可用。每一次malloc的时候,先从这里取index,如果这里没有,那
可以直接从mem_record的末尾取。

具体细节就不阐述了,典型的空间换时间方法。整个库很简单,代码100来行。我也只进行过粗略的
测试。我肯定这100来行代码是有问题的,相信自己的代码存在问题是对bug的一种觉悟,哈哈哈。

这个东西只检测C语言的内存泄露,其实要检测C++的也很简单,只需要重载new和delete就可以了。

要放春节假了,在公司的最后几个小时实在无聊,才做了这个东西,前后花了1个多小时,写起来感觉
不错。

 

 

代码下载

 

posted on 2009-01-23 17:43 Kevin Lynx 阅读(4384) 评论(5)  编辑 收藏 引用 所属分类: c/c++

评论

# re: 自己写内存泄露检测库 2009-01-23 22:53 jims

本人一直博主学习  回复  更多评论   

# re: 自己写内存泄露检测库 2009-01-23 23:04 adon

char *str = (char*) malloc( 100 );
char *str2 = (char*) malloc( 111 );
str = str2;
free(str);
free(str);
getchar();

Memory leak report by cmlc, a tiny c memory leak checking library.
Detected 0 memory leaks.

似乎在free上出了点问题  回复  更多评论   

# re: 自己写内存泄露检测库 2009-01-23 23:28 adon

static void cmlc_remove_record( size_t index )
{
if( fi_tail < MAX_RECORD - 1 )
{
free_index[fi_tail++] = index;
free(mem_record[index].address);
mem_record[index].address = 0; /* to identify this record has been removed */
}
}

void cmlc_free( void *address )
{
struct memory *mem = (struct memory*)( (char*)address - sizeof( size_t ) );
cmlc_remove_record( mem->index );
// free( mem ); //注释的free,移到cmlc_remove_record中
}  回复  更多评论   

# re: 自己写内存泄露检测库 2009-01-23 23:55 万连文

需要考虑多线程,否则太不安全了,没有实际意义。  回复  更多评论   

# re: 自己写内存泄露检测库 2009-01-24 20:55 陈梓瀚(vczh)

微软的编译器仅需在cpp开始的地方:
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

当你认为东西都是放完了以后调用
_CrtDumpMemoryLeaks();

东西就回到output窗口去了。  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理