随笔 - 107, 文章 - 0, 评论 - 40, 阅读 - 79073
  博客园  :: 首页  :: 新随笔  :: 管理

3.1.3 内存泄漏检测组件

Posted on   wsg_blog  阅读(33)  评论(0编辑  收藏  举报

Linux C/C++服务器

内存泄漏检测组件

调用malloc或者new,没有调用free或delete
内存泄漏检测组件

  • 如何判断是否有内存泄漏
  • 到底在哪行代码泄漏

本文介绍三种内存泄漏检测的方法

  1. mtrace
  2. 宏定义
  3. hook malloc

mtrace

mtrace是系统自带的内存检测接口,引入mcheck.h头文件就可使用,使用也比较简单

#include <stdlib.h>
#include <stdio.h>
#include <mcheck.h>
int main() {
mtrace();
void *p1 = malloc(10);
void *p2 = malloc(15);
void *p3 = malloc(20);
free(p2);
free(p3);
muntrace();
}

执行过程

gcc -o memleak memleak.c -g
export MALLOC_TRACE=./mem.txt
./memleak
cat mem.txt
addr2line -f -e memleak -a 0x400ad0

低版本可行,在高版本中不会生成mem.txt文件

宏定义

只适合在单文件内,把它放在文件的最前面

为什么main函数中的malloc调用的是宏定义,而void _malloc()内调用的却不是宏定义?
预编译:1.把代码的注释取消 2.宏定义展开,把宏定义直接搬过来,所以在打印行号的时候不会出问题
编译:把各个字符转代码文件换成各个二进制文件
链接:把各个文件的代码模块组装起来

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
void *_malloc(size_t size, const char *filename, int line) {
void *p = malloc(size);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
FILE *fp = fopen(buff, "w");
fprintf(fp, "[+]%s:%d, addr: %p, size: %ld\n", filename, line, p, size);
fflush(fp);
fclose(fp);
//printf("[+]%s:%d, %p\n", filename, line, p);
return p;
}
void _free(void *p, const char *filename, int line) {
//printf("[-]%s:%d, %p\n", filename, line, p);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
if (unlink(buff) < 0) { //删除文件
printf("double free: %p\n", p);
return ;
}
return free(p);
}
#define malloc(size) _malloc(size, __FILE__, __LINE__)
#define free(size) _free(size, __FILE__, __LINE__)
int main() {
void *p1 = malloc(10); //_malloc(size, __FILE__, __LINE__)
void *p2 = malloc(15);
void *p3 = malloc(20);
free(p2);
free(p3);
return 0;
}

执行过程

gcc -o memleak memleak.c -g
mkdir mem
./memleak

hook

hook接口在调试追踪代码中使用的较多,主要是通过dlsym()和typedef函数指针类型来实现,在之前死锁检测组件中我们也有使用和详细说明

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
typedef void *(*malloc_t)(size_t size);
typedef void (*free_t)(void *ptr);
malloc_t malloc_f = NULL;
free_t free_f = NULL;
int enable_malloc_hook = 1;
void *malloc(size_t size) {
void *p = NULL;
if(enable_malloc_hook){
enable_malloc_hook = 0;
//printf内部也会调用malloc,如果不加enable_malloc_hook标志位终止条件,将导致递归出现段错误
//printf("size: %ld\n", size);
p = malloc(size);
void *caller = __builtin_return_address(0); //可返回是哪里调用的本函数
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", p);
FILE *fp = fopen(buff, "w");
fprintf(fp, "[+]%p, addr: %p, size: %ld\n", caller, p, size);
fflush(fp);
fclose(fp);
enable_malloc_hook = 1;
}else{
p = malloc(size);
}
return p;
}
void free(void *ptr) {
if(enable_malloc_hook){
enable_malloc_hook = 0;
//printf("ptr: %p\n", ptr);
char buff[128] = {0};
sprintf(buff, "./mem/%p.mem", ptr);
if (unlink(buff) < 0) {
printf("double free: %p\n", ptr);
return ;
}
free_f(ptr);
enable_malloc_hook = 1;
}
}
static void init_hook(void) {
if (malloc_f == NULL) {
malloc_f = (malloc_t)dlsym(RTLD_NEXT, "malloc");
}
if (free_f == NULL) {
free_f = (free_t)dlsym(RTLD_NEXT, "free");
}
}
int main() {
init_hook(); //3.hook
void *p1 = malloc(10);
void *p2 = malloc(15);
void *p3 = malloc(20);
free(p2);
free(p3);
return 0;
}

执行过程

gcc -o memleak memleak.c -g -ldl
mkdir mem
./memleak
cat mem/0x55818bb172a0.mem
addr2line -f -e memleak -a 0x1651

以上只是给出了检测内存泄漏的方法,但实际工程中检测内存泄漏要复杂的多,而且并不能100%的检测出来

相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示