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);
}
}
浙公网安备 33010602011771号