用内存管理器的钩子函数跟踪内存泄漏

<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>

内存管理器的钩子函数跟踪内存泄漏

载时请注明出处和作者联系方式
作者联系方式:李先静 <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~~


阅读更多

没有更多推荐了,返回首页