<meta content="text/html; charset=utf-8" http-equiv="CONTENT-TYPE">
<meta content="OpenOffice.org 2.3 (Unix)" name="GENERATOR">
<style type="text/css">
<!--
@page { size: 21cm 29.7cm; margin: 2cm }
P { margin-bottom: 0.21cm }
-->
</style>

/**//*memory_trace.c*/

#include<execinfo.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<malloc.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

staticvoidmemory_trace_init(void);
staticvoidmemory_trace_deinit(void);
staticvoid*my_malloc_hook(size_tsize,constvoid*ptr);
staticvoidmy_free_hook(void*ptr,constvoid*caller);
staticvoid*my_realloc_hook(void*ptr,size_tsize,constvoid*caller);

staticvoid*my_malloc_hook(size_tsize,constvoid*ptr);
staticvoidmy_free_hook(void*ptr,constvoid*caller);
staticvoid*my_realloc_hook(void*ptr,size_tsize,constvoid*caller);

staticvoid*(*old_malloc_hook)(size_tsize,constvoid*ptr);
staticvoid(*old_free_hook)(void*ptr,constvoid*caller);
staticvoid*(*old_realloc_hook)(void*ptr,size_tsize,constvoid*caller);

#defineBACK_TRACE_DEPTH8
#defineCACHE_SIZE512

staticFILE*g_memory_trace_fp=NULL;
staticintg_memory_trace_cache_used=0;

/**//*additional3items:alloc/freeaddrsize*/
staticvoid*g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH+3];
staticvoidmemory_trace_flush(void);
staticvoidmemory_trace_write(intalloc,void*addr,intsize);

staticvoidmemory_trace_backup(void)

...{
old_malloc_hook=__malloc_hook;
old_free_hook=__free_hook;
old_realloc_hook=__realloc_hook;

return;
}

staticvoidmemory_trace_hook(void)

...{
__malloc_hook=my_malloc_hook;
__free_hook=my_free_hook;
__realloc_hook=my_realloc_hook;

return;
}

staticvoidmemory_trace_restore(void)

...{
__malloc_hook=old_malloc_hook;
__free_hook=old_free_hook;
__realloc_hook=old_realloc_hook;

return;
}

staticvoidmemory_trace_init(void)

...{
if(g_memory_trace_fp==NULL&&getenv("MALLOC_TRACE")!=NULL)

...{

charfile_name[260]=...{0};
snprintf(file_name,sizeof(file_name),"/tmp/%d_memory.log",getpid());
if((g_memory_trace_fp=fopen(file_name,"wb+"))!=NULL)

...{
memory_trace_backup();
memory_trace_hook();
}

atexit(memory_trace_deinit);
}

return;
}

staticvoidmemory_trace_deinit(void)

...{
if(g_memory_trace_fp!=NULL)

...{
memory_trace_restore();
memory_trace_flush();
fclose(g_memory_trace_fp);
g_memory_trace_fp=NULL;
}

return;
}

void(*__malloc_initialize_hook)(void)=memory_trace_init;

staticvoid*my_malloc_hook(size_tsize,constvoid*caller)

...{
void*result=NULL;
memory_trace_restore();
result=malloc(size);
memory_trace_write(1,result,size);
memory_trace_hook();

returnresult;
}

staticvoidmy_free_hook(void*ptr,constvoid*caller)

...{
memory_trace_restore();
free(ptr);
memory_trace_write(0,ptr,0);
memory_trace_hook();

return;
}

staticvoid*my_realloc_hook(void*ptr,size_tsize,constvoid*caller)

...{
void*result=NULL;

memory_trace_restore();
memory_trace_write(0,ptr,0);
result=realloc(ptr,size);
memory_trace_write(1,result,size);
memory_trace_hook();

returnresult;
}

staticvoidmemory_trace_flush_one_entry(intindex)

...{
intoffset=0;

charbuffer[512]=...{0};
intfd=fileno(g_memory_trace_fp);
intalloc=(int)g_memory_trace_cache[index][BACK_TRACE_DEPTH];
void*addr=g_memory_trace_cache[index][BACK_TRACE_DEPTH+1];
intsize=(int)g_memory_trace_cache[index][BACK_TRACE_DEPTH+2];
void**backtrace_buffer=g_memory_trace_cache[index];

snprintf(buffer,sizeof(buffer),"%s%p%d ",alloc?"alloc":"free",addr,size);
if(!alloc)

...{
write(fd,buffer,strlen(buffer));
return;
}

char**symbols=backtrace_symbols(backtrace_buffer,BACK_TRACE_DEPTH);
if(symbols!=NULL)

...{
inti=0;
offset=strlen(buffer);
for(i=0;i<BACK_TRACE_DEPTH;i++)

...{
if(symbols[i]==NULL)

...{
break;
}
char*begin=strchr(symbols[i],'(');
if(begin!=NULL)

...{
*begin=' ';
char*end=strchr(begin,')');
if(end!=NULL)

...{
strcpy(end," ");
}
strncpy(buffer+offset,begin,sizeof(buffer)-offset);
offset+=strlen(begin);
}
}
write(fd,buffer,offset);
free(symbols);
}

return;
}

staticvoidmemory_trace_flush(void)

...{
inti=0;
for(i=0;i<g_memory_trace_cache_used;i++)

...{
memory_trace_flush_one_entry(i);
}
g_memory_trace_cache_used=0;

return;
}

staticvoidmemory_trace_write(intalloc,void*addr,intsize)

...{
if(g_memory_trace_cache_used>=CACHE_SIZE)

...{
memory_trace_flush();
}

inti=0;

void*backtrace_buffer[BACK_TRACE_DEPTH]=...{0};
backtrace(backtrace_buffer,BACK_TRACE_DEPTH);

for(i=0;i<BACK_TRACE_DEPTH;i++)

...{
g_memory_trace_cache[g_memory_trace_cache_used][i]=backtrace_buffer[i];
}
g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH]=(void*)alloc;
g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+1]=addr;
g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+2]=(void*)size;

g_memory_trace_cache_used++;

return;
}

#ifdefMEMORY_TRACE_TEST
voidtest(void)

...{
char*p=malloc(100);
p=malloc(123);

free(p);

return;
}
intmain(intargc,char*argv[])

...{
malloc(100);
test();
malloc(100);
test();
char*p=malloc(100);
free(p);

return0;
}
#endif/*MEMORY_TRACE_TEST*/
把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。
再写个程序来分析log文件:

/**//*mtrace.c*/
#include<stdio.h>

#defineMAX_ENTRY1024*1024

intmain(intargc,char*argv[])

...{
if(argc!=3)

...{
printf("usage:%s[logfile][outfile] ",argv[0]);
return0;
}

FILE*fp=fopen(argv[1],"r");
FILE*fp_out=fopen(argv[2],"wb+");

if(fp==NULL||fp_out==NULL)

...{
printf("openfilefailed ");
if(fp!=NULL)

...{
fclose(fp);
}
if(fp_out!=NULL)

...{
fclose(fp_out);
}
return;
}

inti=0;
intn=0;
intskip=0;
intline_index=0;
void*addr=0;

charline[260]=...{0};

void*addrs_array[MAX_ENTRY]=...{0};

intlines_array[MAX_ENTRY]=...{0};


while(fgets(line,sizeof(line),fp)!=NULL)

...{
if(line[0]!='a'&&line[0]!='f')

...{
line_index++;
continue;
}

addr=NULL;
if(strncmp(line,"alloc",5)==0&&n<MAX_ENTRY)

...{
sscanf(line,"alloc%p",&addr);
addrs_array[n]=addr;
lines_array[n]=line_index;
n++;

printf("a");
}
elseif(strncmp(line,"free",4)==0)

...{
sscanf(line,"free%p",&addr);
for(i=0;i<n;i++)

...{
if(addrs_array[i]==addr)

...{
lines_array[i]=-1;
break;
}
}

printf("f");
}
line_index++;
}

printf(" ");
fseek(fp,0,0);

i=0;
line_index=0;
while(fgets(line,sizeof(line),fp)!=NULL)

...{
if(strncmp(line,"alloc",5)==0)

...{
if(lines_array[i]==line_index)

...{
printf("leak%s",line);
fprintf(fp_out,"*");
skip=0;
i++;
}
else

...{
skip=1;
}
}
elseif(strncmp(line,"free",4)==0)

...{
skip=1;
}

if(!skip)

...{
fputs(line,fp_out);
}

line_index++;
}

fclose(fp);
fclose(fp_out);

return0;
}
把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到输出文件中,可以看到内存泄漏的地地址,大小和调用关系。
~~end~~
用内存管理器的钩子函数跟踪内存泄漏
载时请注明出处和作者联系方式
作者联系方式:李先静 <xianjimli at hotmail dot com>
作为Linux下的C程序员,我总是习惯在单元测试通过之后,再用valgrind把程序跑一下,看看有没有内存泄漏和内存越界等问题。可惜的是,有时valgrind并不能很好的工作,像基于DirectFB的多进程程序在valgrind下是跑不起的, 这时我们可以通过内存管理器的钩子函数来跟踪内存泄漏。
glibc提供的内存管理器的钩子函数让你可以监控/改变内存管理函数的行为。其实glibc已经利用这个机制实现了内存泄漏检测的功能,提供了mtrace/muntrace两个函数和mtrace工具,只是不太好用,一是速度慢,二是没有backtrace。更惨的是在Fedora 7上再也找不到它了,只好自己写一个:
先记录分配/释放操作:

/**//*memory_trace.c*/
#include<execinfo.h>
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<malloc.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
staticvoidmemory_trace_init(void);
staticvoidmemory_trace_deinit(void);
staticvoid*my_malloc_hook(size_tsize,constvoid*ptr);
staticvoidmy_free_hook(void*ptr,constvoid*caller);
staticvoid*my_realloc_hook(void*ptr,size_tsize,constvoid*caller);
staticvoid*my_malloc_hook(size_tsize,constvoid*ptr);
staticvoidmy_free_hook(void*ptr,constvoid*caller);
staticvoid*my_realloc_hook(void*ptr,size_tsize,constvoid*caller);
staticvoid*(*old_malloc_hook)(size_tsize,constvoid*ptr);
staticvoid(*old_free_hook)(void*ptr,constvoid*caller);
staticvoid*(*old_realloc_hook)(void*ptr,size_tsize,constvoid*caller);
#defineBACK_TRACE_DEPTH8
#defineCACHE_SIZE512
staticFILE*g_memory_trace_fp=NULL;
staticintg_memory_trace_cache_used=0;
/**//*additional3items:alloc/freeaddrsize*/
staticvoid*g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH+3];
staticvoidmemory_trace_flush(void);
staticvoidmemory_trace_write(intalloc,void*addr,intsize);
staticvoidmemory_trace_backup(void)
...{
old_malloc_hook=__malloc_hook;
old_free_hook=__free_hook;
old_realloc_hook=__realloc_hook;
return;
}
staticvoidmemory_trace_hook(void)
...{
__malloc_hook=my_malloc_hook;
__free_hook=my_free_hook;
__realloc_hook=my_realloc_hook;
return;
}
staticvoidmemory_trace_restore(void)
...{
__malloc_hook=old_malloc_hook;
__free_hook=old_free_hook;
__realloc_hook=old_realloc_hook;
return;
}
staticvoidmemory_trace_init(void)
...{
if(g_memory_trace_fp==NULL&&getenv("MALLOC_TRACE")!=NULL)
...{
charfile_name[260]=...{0};
snprintf(file_name,sizeof(file_name),"/tmp/%d_memory.log",getpid());
if((g_memory_trace_fp=fopen(file_name,"wb+"))!=NULL)
...{
memory_trace_backup();
memory_trace_hook();
}
atexit(memory_trace_deinit);
}
return;
}
staticvoidmemory_trace_deinit(void)
...{
if(g_memory_trace_fp!=NULL)
...{
memory_trace_restore();
memory_trace_flush();
fclose(g_memory_trace_fp);
g_memory_trace_fp=NULL;
}
return;
}
void(*__malloc_initialize_hook)(void)=memory_trace_init;
staticvoid*my_malloc_hook(size_tsize,constvoid*caller)
...{
void*result=NULL;
memory_trace_restore();
result=malloc(size);
memory_trace_write(1,result,size);
memory_trace_hook();
returnresult;
}
staticvoidmy_free_hook(void*ptr,constvoid*caller)
...{
memory_trace_restore();
free(ptr);
memory_trace_write(0,ptr,0);
memory_trace_hook();
return;
}
staticvoid*my_realloc_hook(void*ptr,size_tsize,constvoid*caller)
...{
void*result=NULL;
memory_trace_restore();
memory_trace_write(0,ptr,0);
result=realloc(ptr,size);
memory_trace_write(1,result,size);
memory_trace_hook();
returnresult;
}
staticvoidmemory_trace_flush_one_entry(intindex)
...{
intoffset=0;
charbuffer[512]=...{0};
intfd=fileno(g_memory_trace_fp);
intalloc=(int)g_memory_trace_cache[index][BACK_TRACE_DEPTH];
void*addr=g_memory_trace_cache[index][BACK_TRACE_DEPTH+1];
intsize=(int)g_memory_trace_cache[index][BACK_TRACE_DEPTH+2];
void**backtrace_buffer=g_memory_trace_cache[index];
snprintf(buffer,sizeof(buffer),"%s%p%d ",alloc?"alloc":"free",addr,size);
if(!alloc)
...{
write(fd,buffer,strlen(buffer));
return;
}
char**symbols=backtrace_symbols(backtrace_buffer,BACK_TRACE_DEPTH);
if(symbols!=NULL)
...{
inti=0;
offset=strlen(buffer);
for(i=0;i<BACK_TRACE_DEPTH;i++)
...{
if(symbols[i]==NULL)
...{
break;
}
char*begin=strchr(symbols[i],'(');
if(begin!=NULL)
...{
*begin=' ';
char*end=strchr(begin,')');
if(end!=NULL)
...{
strcpy(end," ");
}
strncpy(buffer+offset,begin,sizeof(buffer)-offset);
offset+=strlen(begin);
}
}
write(fd,buffer,offset);
free(symbols);
}
return;
}
staticvoidmemory_trace_flush(void)
...{
inti=0;
for(i=0;i<g_memory_trace_cache_used;i++)
...{
memory_trace_flush_one_entry(i);
}
g_memory_trace_cache_used=0;
return;
}
staticvoidmemory_trace_write(intalloc,void*addr,intsize)
...{
if(g_memory_trace_cache_used>=CACHE_SIZE)
...{
memory_trace_flush();
}
inti=0;
void*backtrace_buffer[BACK_TRACE_DEPTH]=...{0};
backtrace(backtrace_buffer,BACK_TRACE_DEPTH);
for(i=0;i<BACK_TRACE_DEPTH;i++)
...{
g_memory_trace_cache[g_memory_trace_cache_used][i]=backtrace_buffer[i];
}
g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH]=(void*)alloc;
g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+1]=addr;
g_memory_trace_cache[g_memory_trace_cache_used][BACK_TRACE_DEPTH+2]=(void*)size;
g_memory_trace_cache_used++;
return;
}
#ifdefMEMORY_TRACE_TEST
voidtest(void)
...{
char*p=malloc(100);
p=malloc(123);
free(p);
return;
}
intmain(intargc,char*argv[])
...{
malloc(100);
test();
malloc(100);
test();
char*p=malloc(100);
free(p);
return0;
}
#endif/*MEMORY_TRACE_TEST*/把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。
再写个程序来分析log文件:

/**//*mtrace.c*/
#include<stdio.h>
#defineMAX_ENTRY1024*1024
intmain(intargc,char*argv[])
...{
if(argc!=3)
...{
printf("usage:%s[logfile][outfile] ",argv[0]);
return0;
}
FILE*fp=fopen(argv[1],"r");
FILE*fp_out=fopen(argv[2],"wb+");
if(fp==NULL||fp_out==NULL)
...{
printf("openfilefailed ");
if(fp!=NULL)
...{
fclose(fp);
}
if(fp_out!=NULL)
...{
fclose(fp_out);
}
return;
}
inti=0;
intn=0;
intskip=0;
intline_index=0;
void*addr=0;
charline[260]=...{0};
void*addrs_array[MAX_ENTRY]=...{0};
intlines_array[MAX_ENTRY]=...{0};

while(fgets(line,sizeof(line),fp)!=NULL)
...{
if(line[0]!='a'&&line[0]!='f')
...{
line_index++;
continue;
}
addr=NULL;
if(strncmp(line,"alloc",5)==0&&n<MAX_ENTRY)
...{
sscanf(line,"alloc%p",&addr);
addrs_array[n]=addr;
lines_array[n]=line_index;
n++;
printf("a");
}
elseif(strncmp(line,"free",4)==0)
...{
sscanf(line,"free%p",&addr);
for(i=0;i<n;i++)
...{
if(addrs_array[i]==addr)
...{
lines_array[i]=-1;
break;
}
}
printf("f");
}
line_index++;
}
printf(" ");
fseek(fp,0,0);
i=0;
line_index=0;
while(fgets(line,sizeof(line),fp)!=NULL)
...{
if(strncmp(line,"alloc",5)==0)
...{
if(lines_array[i]==line_index)
...{
printf("leak%s",line);
fprintf(fp_out,"*");
skip=0;
i++;
}
else
...{
skip=1;
}
}
elseif(strncmp(line,"free",4)==0)
...{
skip=1;
}
if(!skip)
...{
fputs(line,fp_out);
}
line_index++;
}
fclose(fp);
fclose(fp_out);
return0;
}
把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到输出文件中,可以看到内存泄漏的地地址,大小和调用关系。
~~end~~
本文介绍了一种在Linux环境下使用glibc内存管理器的钩子函数来跟踪和检测内存泄漏的方法。通过实现自定义的malloc/free/realloc钩子函数,并结合backtrace获取调用堆栈,能够有效定位内存泄漏发生的位置。
1081

被折叠的 条评论
为什么被折叠?



