A memory check tool like memlink

    when I see the source code of memleak(a tool checking  memory leak ), I find it's not efficient.  So I rewrite it myself,  I use a hash while the memleak use a list to manage the  meta data. 

the following is my code :

 

my_memleak.h

#ifndef H_MY_MEMLEAK_H
#define H_MY_MEMLAEK_H

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

extern void *dbg_malloc(size_t size);
extern char * dbg_file_name;
extern int dbg_line_number;

extern void dbg_free(void *ptr);
extern void printHash();


#define FILE_LINE dbg_file_name=__FILE__, dbg_line_number=__LINE__
#define malloc(s) (FILE_LINE, dbg_malloc(s))
#define free(p) (FILE_LINE,dbg_free(p))

#endif

  

 

my_memleak.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define 	M 	 4 
#define  CACHELEVEL0 	 337  //cache[0]	的默认大小
#define 	CACHEMAX 	20 
int cacheFlag=0;  //待要分配的那一层
 


struct head{
 	void  *addr;  //看一个结构体是否可用就看该变量是否为NULL
    size_t size;
  	char *file;
  	unsigned line;
    struct { struct head*prev, *next; } list;
};



struct fileLine{
	int isOK;
	char * fileName;
	int lineNum;
};
struct head *  cache[CACHEMAX];
 
#define HASHNUM     23
struct head  hashHead[HASHNUM];
 
int firstCall=1; // 刚开始肯定为真 ,在malloc和free的时候判断一下 ,若在malloc为真,则进行一些初始化,若在free为真,说明有错
 
char *dbg_file_name;
int dbg_line_number;
 
 
 /**
 =====================================================================
 以下是为了预防内存越界的函数
 **/
  char *  memZone="qRhP"; //用来填充的随机字母
  int   memZoneSize=4;//防止越界的字符数目
  
  void setMemZone(void * p){
 	char *  ptr =(char*)p;
 	int  i=-1;
 	if(ptr==NULL){return ;}
 	for(i=0;i<memZoneSize;i++){
        	ptr[i]=memZone[i%memZoneSize];	
 	}
 }
 
 int checkMemZone(void  *p){
	char * ptr =(char*)p;
	int i=-1;
 	if(ptr==NULL){return 0;}
 	for(i=0;i<memZoneSize;i++){
 		if(ptr[i] !=memZone[i%memZoneSize]){return 0;}
 	}
 	return 1;
 }	
 
 
 /***
 =====================================================================
 以下是缓存的处理
 **/
 
 //单个 的结构体初始化
void initHead(struct head * h){
	if(h==NULL){return;}
	h->addr=NULL;
	h->size=0;
	h->file=NULL;
	h->line=0;
	h->list.prev=h->list.next=NULL;
}
 
 //将给定的cache[i]初始化成一个链表
void linkCache(struct head** h,int s){
	struct head * headPtr=*h;
	int i=0;
	if(h==NULL||*h==NULL){return ;}
	
	initHead(headPtr);
	for(i=1;i<s;i++){
		(*h)[i].list.prev =headPtr;
		(*h)[i].list.next=NULL;
		(*h)[i].file =(*h)[i].addr=NULL;
		headPtr->list.next=&((*h)[i]);
		headPtr++;//指向下一个struct 结构体
	}
}
 
 
//为cache[level]分配空间,并初始化成一个链表
struct head *   initCache(int level){
	struct head * tmp=NULL;
	int num=0;
	if(level<0){return NULL;}
	num=getLevelSize(level);
	//printf("-------1--------num -----%d\n",num);
	tmp =  (struct head *)malloc(sizeof(struct head)*num );//是否分配成功交给使用的地方来判断 
	//printf("-----2-----sizeof(struct head)*num = %d---*tmp =  %d \n\n",sizeof(struct head)*num,(long int)(tmp));
	linkCache(&tmp,num);//将tmp指向的结构体初始化成一个链表
	return  tmp;
}


//从缓存分配一个head结构体
struct head *   getFromCache(){
	struct head*  h=NULL;
	int level =getValideCaches();
	if(level<0 || level>=CACHEMAX){return NULL;}
	h= cache[level];
	cache[level]=cache[level]->list.next;
	if(cache[level]!=NULL){	cache[level]->list.prev=NULL;}
	initHead(h);//貌似这个初始化木有必要。。。。。。
	return h;
}

void  addToCache(struct head * h){
	//直接放在第一个cache里面
	if(h==NULL){return ;}
	if(cache[0]==NULL){
		initHead(h); //加上这一句话以后,BUG 木有拉 !!
		cache[0]=h;  //这块木有初始化的话将会导致下一次走else的时候出现问题 !!!!这就是BUG !!折腾这么长时间哈 !!
	}else{ 
		h->list.prev=cache[0];
		h->list.next=cache[0]->list.next;
		if(cache[0]->list.next!=NULL){
			cache[0]->list.next->list.prev=h;
		}
		cache[0]->list.next=h;
	}
}

//找到一个有可用结构体的cache[i]的层数
int getValideCaches(){
	int i=0;
	for(i=0;i<cacheFlag;i++){//因为cacheFlag 是从下标0开始的层数,所以加一
		if(cache[i]!=NULL){return i;}
	}
	//如果没有合适的,并且cache[]还有层没有使用,则初始化新的cache层
	if(cacheFlag<CACHEMAX){
		//cacheFlag++;
		cache[cacheFlag]=initCache(cacheFlag);
		cacheFlag++;  
		return cacheFlag-1;
	}
	return -1;//缓存用完了,出错
}

//cache[level]应该分配的结构体个数
int  getLevelSize(int level){
	if(level<0){return -1;}
	if (level==0){return  CACHELEVEL0 ;}
	return M *getLevelSize(level-1);
}

/**
=========================================================================
一下是哈希表的处理
**/


//哈希表的初始化, 木有必要 ,全局变量自己已经初始化了
void initHash(){
	int i=0;
	for(i=0;i<HASHNUM;i++){
		initHead(&hashHead[i]);
	}  
}

//删除一个节点struct head,其实就是把该节点从原来的链表中去出来,并重新连接原来的链表
//从红黑书删除的话应该不是这样的。。。。
struct head * delFromHash(struct head * h){
	if(h==NULL){return NULL;}
	if(h->list.prev!=NULL){	h->list.prev->list.next=h->list.next;}
	if(h->list.next!=NULL){	h->list.next->list.prev=h->list.prev;}
	return h;
}


//来了一个新的malloc操作
int  add(void * buf , size_t  s ){
	int pos=-1;
	struct head * tmp=NULL;
	if(buf==NULL|| s<=0){return 0;}
	
	//如果是第一次调用,进行必要的初始化
	//if(firstCall){
		//initHash();
		//firstCall=0;
	//}
	//首先哈希处理
	pos =((long int)buf)%HASHNUM; //这里是buf呢还是*buf ? 应该是buf
	if(hashHead[pos].addr==NULL){
		//将这个malloc的信息放在这块
		hashHead[pos].addr=buf;
		hashHead[pos].size=s;
		hashHead[pos].file=dbg_file_name;
		hashHead[pos].line=dbg_line_number;
	}else{//从缓存取出一个块
		tmp = getFromCache();
		if(tmp==NULL){
			return  0;
		}
		tmp->list.next=hashHead[pos].list.next;
		tmp->list.prev=&hashHead[pos];
		if(hashHead[pos].list.next!=NULL){
			hashHead[pos].list.next->list.prev=tmp;
		}	
		hashHead[pos].list.next=tmp;
		
		tmp->addr=buf;
		tmp->size=s;
		tmp->file=dbg_file_name;
		tmp->line=dbg_line_number;
		
		//adjustRBTree(tmp); //调整红黑树
	}
	
	return 1;
}


//找出这个buf在属于哪个struct head *管理 ,并从中取出来 放入缓存
//一般这个函数的使用场景貌似只有在free的时候用
//当flag 大于0的时候输出mallo的相关信息
int   delHead (void * buf  ){
	int pos=-1;
	int f=0;//调试的时候用的,木有实质意义
	int size=0;
	struct head * tmp=NULL;
	if(buf==NULL){return  0;}
	pos= ((long int )buf)%HASHNUM;
	if(hashHead[pos].addr==NULL){return  0;}//说明这个哈希的位置没有元素
	if(hashHead[pos].addr==buf) {  //哈希表里面存的就是
	        //delFromHash(&hashHead[pos]);//从哈希里面删除这个节点
	        
		if(hashHead[pos].list.next==NULL){ //没有后续链表的话,哈希节点初始化
			size=hashHead[pos].size;//记录下内存块的大小
			initHead(&hashHead[pos]);
		}else{
			size=hashHead[pos].size;//记录下内存块的大小	
			
			//把链表的第一个节点的内容挪到哈希表里面
			tmp =hashHead[pos].list.next;
			hashHead[pos].addr=tmp->addr;
			hashHead[pos].size=tmp->size;
			hashHead[pos].file=tmp->file;
			hashHead[pos].line=tmp->line;
			
			if(tmp->list.next!=NULL){
				tmp->list.next->list.prev=&hashHead[pos];
			}
			
			hashHead[pos].list.next=tmp->list.next;			
			
			addToCache(tmp); //将释放出来的节点放入缓存
		}
		return  size;
	}else{
		tmp =hashHead[pos].list.next;
		while(tmp!=NULL){
			if(tmp->addr==buf){
				break;
			}
			tmp=tmp->list.next;
		}
		
		if(tmp==NULL){ return 0;}
		
		size=tmp->size;//记录下内存块的大小
		
		//printf("----------------3-------------3-------------------\n");
		tmp->list.prev->list.next=tmp->list.next;
		if(tmp->list.next!=NULL){
				tmp->list.next->list.prev=tmp->list.prev;	
		}

		addToCache(tmp); //将释放出来的节点放入缓存
		//printf("----------------5-------------5-------------------\n");
	 	//delFromList();
		//tmp= findInRBTree(hashHead[pos].list.next); //哈希表附属的红黑树里面的
		//delFromRBTree(tmp);//从红黑树中删除
		return size;
	}
	//printf("------------------------delHead ----------------------no-----------\n");
	//f++;
	return 0;
}




void printHead(struct head * h){
	printf("没有释放的malloc操作 : %s , %d \n",h->file,h->line);
}

//将哈希中的所有节点信息输出,在程序结束的时候使用,这个时候在哈希里面的都是泄露的
void printHash(){
	int i=0;
	int flag=0;
	struct head * tmp=NULL;
	for(i=0;i<HASHNUM;i++){
		if(hashHead[i].addr!=NULL){  //如果里面为空
			if(flag==0){flag++;}
			tmp=&hashHead[i];
			while(tmp!=NULL){
				printHead(tmp);
				tmp=tmp->list.next;
			}
		}
	}
	if(flag==0){
		printf("所有空间都已经释放 !!\n");
	}
}

/**
=====================================================================================
malloc         free
**/
//下面这两个变量调试的时候用
int mallocNUM;
int freeNUM;

void * dbg_malloc(size_t s){
	void  * ptr=NULL;
	void * buf =malloc(s+memZoneSize*2); //额外的分配用来防止地址越界的空间
	if(buf){
		//初始化头部的防越界空间
		setMemZone(buf);
		buf=buf+memZoneSize;
		ptr=buf+s;
		setMemZone(ptr);		//初始化尾部的防越界空间
		if(add(buf,s)){   //加入的只是实际使用的内存头指针
			mallocNUM++;
			printf("-----------malloc-----------%d \n ",mallocNUM);
			return buf;
		}else
		{
			free(buf);
			return NULL;
		}
	}
	  fprintf(stderr, "%s:%u: dbg_malloc: not enough memory\n", dbg_file_name, dbg_line_number);
 	  return NULL;
		
}

/**/
//来了一个新的free操作
//Q: 取出来,加进去之前需不需要初始化h ?没有有必要? 木有必要
/**
	因为踩地址的几率比较小,所以先判断是否越界,再决定是否返回malloc时候的位置信息
**/
void  dbg_free(void  * buf){
	void *ptr =buf;
	int  isMemFlow=0;
	int size=delHead(buf); //从哈希里面取出来   

	
	//检查尾部的验证空间
	if(size){
		//检查头部的验证空间
		ptr=ptr-memZoneSize;
		if(!checkMemZone(ptr)){//返回0表示已经被踩了,给出提示,并继续执行下面的代码
			//printf("内存越界:  ");
			isMemFlow++;
		}
	
		ptr= ptr+ size+ memZoneSize;
		if(!checkMemZone(ptr)){//返回0表示已经被踩了,给出提示,并继续执行下面的代码
			//printf("内存越界: "); 
			isMemFlow++;
		}
		
		freeNUM++;
		printf("-----free--------------count   =%d \n",freeNUM);
		
		if(isMemFlow>0){
			printf("\n%s ,%d  此处释放的地址存在地址越界\n",dbg_file_name,dbg_line_number);
		}
		free(buf-memZoneSize);
		buf=NULL;  //预防野指针
	}else{
		printf("%s,%d 访问了非法地址 ,该地址已被释放或者指针未初始化\n", dbg_file_name,dbg_line_number);
	}

}

  

 

posted on 2013-05-02 21:15  aitilang  阅读(204)  评论(0)    收藏  举报

导航