利用打桩机制定位内存泄漏

Linux 连接器支持一个很强大的技术,称为 库打桩(library interpositioning),它允许你截获对共享库函数的调用,取而代之执行自己的代码。使用打桩机制,你可以追踪对某个特殊库函数的调用次数,验证或追踪它的输入输出值,或者甚至把它替换成一个完全不同的实现。以下使用打桩机制来检测 C 程序是否存在内存泄漏。

编写包装函数打印 malloc 调用

—malloc.h—

1
2
3
4
#define malloc(size) Malloc(size)
#define free(ptr) Free(ptr)
void *Malloc(size_t size);
void Free(void* ptr);

—Malloc.c—

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifdef TRACEHEAP
#include<stdio.h>
#include<stdlib.h>
void* Malloc(size_t size) {
void* ptr = malloc(size);
printf("Malloc(%d)=%p\n", (int)size, ptr);
return ptr;
}
void Free(void* ptr) {
free(ptr);
printf("free(%p)\n", ptr);
}
#endif

编译时打桩

—int.c—

1
2
3
4
5
6
7
#include<stdio.h>
#include<malloc.h>
int main() {
int* p = (int*) malloc(32);
free(p);
return 0;
}

1
2
➜  gcc -DTRACEHEAP -c Malloc.c
➜ gcc -I. -o intc int.c Malloc.o

运行程序会得到如下追踪信息:

1
2
3
➜  ./intc
Malloc(32) = 0x7f9fef402700
Free(0x7f9fef402700)

由于有 -I. 参数,所以会进行打桩。它告诉C预处理器在搜索通常的系统目录之前,先在当前目录中查找malloc.h

监测内存泄漏的思路

监测内存泄漏的关键是要能截获住对分配内存和释放内存的调用。截获住两个函数,我们就能跟踪每一块内存的生命周期。比如每成功分配一块内存后,把它的指针加入一个全局的 list;每当释放一块内存,再把它的指针从list中删除。当程序结束时,list中剩余的指针就是指向那些没有被释放的内存。通过上述的打桩机制,我们可以实现内存泄漏的监测。

参考:- 《深入理解计算机系统》