Linux下用内存管理器的钩子函数跟踪内存泄漏 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/200
2012-01-04 20:24
876 查看
作为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>
static void memory_trace_init(void);
static void memory_trace_deinit(void);
static void *my_malloc_hook (size_t size, const void* ptr);
static void my_free_hook (void* ptr, const void* caller);
static void *my_realloc_hook (void *ptr, size_t size, const void *caller);
static void *my_malloc_hook (size_t size, const void* ptr);
static void my_free_hook (void* ptr, const void* caller);
static void *my_realloc_hook (void *ptr, size_t size, const void *caller);
static void *(*old_malloc_hook)(size_t size, const void* ptr);
static void (*old_free_hook)(void* ptr, const void* caller);
static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);
#define BACK_TRACE_DEPTH 8
#define CACHE_SIZE 512
static FILE* g_memory_trace_fp = NULL;
static int g_memory_trace_cache_used = 0;
/**//*additional 3 items: alloc/free addr size*/
static void* g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH + 3];
static void memory_trace_flush(void);
static void memory_trace_write(int alloc, void* addr, int size);
static void memory_trace_backup(void)
...{
old_malloc_hook = __malloc_hook;
old_free_hook = __free_hook;
old_realloc_hook = __realloc_hook;
return;
}
static void memory_trace_hook(void)
...{
__malloc_hook = my_malloc_hook;
__free_hook = my_free_hook;
__realloc_hook = my_realloc_hook;
return;
}
static void memory_trace_restore(void)
...{
__malloc_hook = old_malloc_hook;
__free_hook = old_free_hook;
__realloc_hook = old_realloc_hook;
return;
}
static void memory_trace_init(void)
...{
if(g_memory_trace_fp == NULL && getenv("MALLOC_TRACE") != NULL)
...{
char file_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;
}
static void memory_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;
static void * my_malloc_hook (size_t size, const void *caller)
...{
void *result = NULL;
memory_trace_restore();
result = malloc (size);
memory_trace_write(1, result, size);
memory_trace_hook();
return result;
}
static void my_free_hook (void *ptr, const void *caller)
...{
memory_trace_restore();
free (ptr);
memory_trace_write(0, ptr, 0);
memory_trace_hook();
return;
}
static void *my_realloc_hook (void *ptr, size_t size, const void *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();
return result;
}
static void memory_trace_flush_one_entry(int index)
...{
int offset = 0;
char buffer[512] = ...{0};
int fd = fileno(g_memory_trace_fp);
int alloc = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH];
void* addr = g_memory_trace_cache[index][BACK_TRACE_DEPTH+1];
int size = (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)
...{
int i = 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;
}
static void memory_trace_flush(void)
...{
int i = 0;
for(i = 0; i < g_memory_trace_cache_used; i++)
...{
memory_trace_flush_one_entry(i);
}
g_memory_trace_cache_used = 0;
return;
}
static void memory_trace_write(int alloc, void* addr, int size)
...{
if(g_memory_trace_cache_used >= CACHE_SIZE)
...{
memory_trace_flush();
}
int i = 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;
}
#ifdef MEMORY_TRACE_TEST
void test(void)
...{
char* p = malloc(100);
p = malloc(123);
free(p);
return;
}
int main(int argc, char* argv[])
...{
malloc(100);
test();
malloc(100);
test();
char* p = malloc(100);
free(p);
return 0;
}
#endif/*MEMORY_TRACE_TEST*/
把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。
再写个程序来分析log文件:
/**//*mtrace.c*/
#include <stdio.h>
#define MAX_ENTRY 1024*1024
int main(int argc, char* argv[])
...{
if(argc != 3)
...{
printf("usage: %s [log file] [out file] ", argv[0]);
return 0;
}
FILE* fp = fopen(argv[1], "r");
FILE* fp_out = fopen(argv[2], "wb+");
if(fp == NULL || fp_out == NULL)
...{
printf("open file failed ");
if(fp != NULL)
...{
fclose(fp);
}
if(fp_out != NULL)
...{
fclose(fp_out);
}
return;
}
int i = 0;
int n = 0;
int skip = 0;
int line_index = 0;
void* addr = 0;
char line[260] = ...{0};
void* addrs_array[MAX_ENTRY] = ...{0};
int lines_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
= addr;
lines_array
= line_index;
n++;
printf("a");
}
else if(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;
}
}
else if(strncmp(line, "free", 4) == 0)
...{
skip = 1;
}
if(!skip)
...{
fputs(line, fp_out);
}
line_index++;
}
fclose(fp);
fclose(fp_out);
return 0;
}
把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到输出文件中,可以看到内存泄漏的地地址,大小和调用关系。
~~end~~
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2009-12/23575.htm
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>
static void memory_trace_init(void);
static void memory_trace_deinit(void);
static void *my_malloc_hook (size_t size, const void* ptr);
static void my_free_hook (void* ptr, const void* caller);
static void *my_realloc_hook (void *ptr, size_t size, const void *caller);
static void *my_malloc_hook (size_t size, const void* ptr);
static void my_free_hook (void* ptr, const void* caller);
static void *my_realloc_hook (void *ptr, size_t size, const void *caller);
static void *(*old_malloc_hook)(size_t size, const void* ptr);
static void (*old_free_hook)(void* ptr, const void* caller);
static void *(*old_realloc_hook)(void *ptr, size_t size, const void *caller);
#define BACK_TRACE_DEPTH 8
#define CACHE_SIZE 512
static FILE* g_memory_trace_fp = NULL;
static int g_memory_trace_cache_used = 0;
/**//*additional 3 items: alloc/free addr size*/
static void* g_memory_trace_cache[CACHE_SIZE][BACK_TRACE_DEPTH + 3];
static void memory_trace_flush(void);
static void memory_trace_write(int alloc, void* addr, int size);
static void memory_trace_backup(void)
...{
old_malloc_hook = __malloc_hook;
old_free_hook = __free_hook;
old_realloc_hook = __realloc_hook;
return;
}
static void memory_trace_hook(void)
...{
__malloc_hook = my_malloc_hook;
__free_hook = my_free_hook;
__realloc_hook = my_realloc_hook;
return;
}
static void memory_trace_restore(void)
...{
__malloc_hook = old_malloc_hook;
__free_hook = old_free_hook;
__realloc_hook = old_realloc_hook;
return;
}
static void memory_trace_init(void)
...{
if(g_memory_trace_fp == NULL && getenv("MALLOC_TRACE") != NULL)
...{
char file_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;
}
static void memory_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;
static void * my_malloc_hook (size_t size, const void *caller)
...{
void *result = NULL;
memory_trace_restore();
result = malloc (size);
memory_trace_write(1, result, size);
memory_trace_hook();
return result;
}
static void my_free_hook (void *ptr, const void *caller)
...{
memory_trace_restore();
free (ptr);
memory_trace_write(0, ptr, 0);
memory_trace_hook();
return;
}
static void *my_realloc_hook (void *ptr, size_t size, const void *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();
return result;
}
static void memory_trace_flush_one_entry(int index)
...{
int offset = 0;
char buffer[512] = ...{0};
int fd = fileno(g_memory_trace_fp);
int alloc = (int)g_memory_trace_cache[index][BACK_TRACE_DEPTH];
void* addr = g_memory_trace_cache[index][BACK_TRACE_DEPTH+1];
int size = (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)
...{
int i = 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;
}
static void memory_trace_flush(void)
...{
int i = 0;
for(i = 0; i < g_memory_trace_cache_used; i++)
...{
memory_trace_flush_one_entry(i);
}
g_memory_trace_cache_used = 0;
return;
}
static void memory_trace_write(int alloc, void* addr, int size)
...{
if(g_memory_trace_cache_used >= CACHE_SIZE)
...{
memory_trace_flush();
}
int i = 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;
}
#ifdef MEMORY_TRACE_TEST
void test(void)
...{
char* p = malloc(100);
p = malloc(123);
free(p);
return;
}
int main(int argc, char* argv[])
...{
malloc(100);
test();
malloc(100);
test();
char* p = malloc(100);
free(p);
return 0;
}
#endif/*MEMORY_TRACE_TEST*/
把以上代码编译成动态库,或者直接编译到功能代码一起。如果设置了MALLOC_TRACE环境变量,分配/释放会被记录到/tmp/$PID_memory.log下。
再写个程序来分析log文件:
/**//*mtrace.c*/
#include <stdio.h>
#define MAX_ENTRY 1024*1024
int main(int argc, char* argv[])
...{
if(argc != 3)
...{
printf("usage: %s [log file] [out file] ", argv[0]);
return 0;
}
FILE* fp = fopen(argv[1], "r");
FILE* fp_out = fopen(argv[2], "wb+");
if(fp == NULL || fp_out == NULL)
...{
printf("open file failed ");
if(fp != NULL)
...{
fclose(fp);
}
if(fp_out != NULL)
...{
fclose(fp_out);
}
return;
}
int i = 0;
int n = 0;
int skip = 0;
int line_index = 0;
void* addr = 0;
char line[260] = ...{0};
void* addrs_array[MAX_ENTRY] = ...{0};
int lines_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
= addr;
lines_array
= line_index;
n++;
printf("a");
}
else if(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;
}
}
else if(strncmp(line, "free", 4) == 0)
...{
skip = 1;
}
if(!skip)
...{
fputs(line, fp_out);
}
line_index++;
}
fclose(fp);
fclose(fp_out);
return 0;
}
把这个段代码编译成一个可执行文件mtrace,以前面记录的LOG为输入,再指定个输出文件,内存泄漏会被写到输出文件中,可以看到内存泄漏的地地址,大小和调用关系。
~~end~~
本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2009-12/23575.htm
相关文章推荐
- 在U-boot下实现自动识别启动Flash的原理 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/201
- Windows XP硬盘安装Ubuntu 11.10双系统全程图解 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com
- Android开发:用getDrawingCache方法获取ImageView中的图像需要注意的问题 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http:/
- Linux下/dev/mem和/dev/kmem的区别 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux
- MySQL字符集设置—MySQL数据库乱码问题 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-01/31531.htm
- Linux之TQ2440通过nfs挂载根文件系统 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/20
- Ubuntu Server 14.04 安装Web服务器(Linux+Apache+MySQL+PHP) 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http
- 在 CentOS7 上安装 Zookeeper-3.4.9 服务 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/L
- Linux内核部件分析 更强的链表klist 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011
- Linux中的spinlock和mutex 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/2011-
- 跨平台(WIndows, Linux, VxWorks) getTickCount实现 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.li
- Ubuntu 10.04地址导航栏修改为显示路径 本篇文章来源于 Linux公社网站(www.linuxidc.com) 原文链接:http://www.linuxidc.com/Linux/20
- CentOS下配置多个Tomcat同时运行 本篇文章来源于 Linux公社网站(www.linuxidc.com)
- Windows让我们养成了什么臭毛病 本篇文章来源于 黑基网-中国最大的网络安全站点 原文链接:http://www.hackbase.com/news/2010-05-10/35154.html
- 如何在Linux中显示和设置主机名 本篇文章来源于 Linux公社网站(www.linuxidc.c
- ORACLE中文排序及在table中指定位置增加字段本篇文章来源于:开发学院 http://edu.codepub.com 原文链接:http://edu.codepub.com/2009/0531/5086.php
- 3G传输网构建浅析 本篇文章来源于 中国协议分析网|www.cnpaf.net 原文链接:http://www.cnpaf.net/Class/otherprotocol/200610/14432.html
- 选择Java接口还是抽象类 本篇文章来自IDC专家网 原文链接:http://www.soidc.net/articles/1213781061058/20001129/1214154133658_1.html
- 解决:type="password" type="text"用户名和密码输入框大小不一样 本篇文章来源于 电脑知识网(www.diannaozs.com) 原文出处:http://www.dianna
- 安装 MySQL与MySQL GUI Tools本篇文章来源于:开发学院 http://edu.codepub.com 原文链接:http://edu.codepub.com/2009/0724/10870.php