代码配置
首先检测单一文件的内存泄漏。
有以下程序:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main() {
setenv("MALLOC_TRACE", "mem.log", 1);
mtrace();
int cnt = 100;
while (0 != cnt--) {
int *p = (int *)malloc(2 * sizeof(int));
usleep(10);
}
printf("over\n");
return 0;
}
其中mtrace()
依赖于头文件mcheck.h
,其只能检测使用glibc的内存分配函数分配的内存,也就是malloc()
等内存分配函数,而不支持C++中的new
操作符。usleep()
依赖头文件unistd.h
,其休眠1us。
需要export MALLOC_TRACE=mem.log
来指定工作路径,从而指定生成的log。
或者调用setenv("MALLOC_TRACE", "mem.log", 1);
。依赖于头文件stdlib.h
。
使用如下make文件进行编译,重点在于必须加-g
,编译优化选项必须为-O0
(或者默认):1
2
3
4
5test : test.o
gcc -o $@ $^
test.o : test.cpp
gcc -c -O0 $^ -g
查看log
代码运行后生成log,使用命令mtrace progname logname
进行log查看。
其中progname
为二进制文件名,logname
为log的文件名。
如果没有内存泄漏会报No memory leaks.
。而上述程序的输出结果为:1
2
3
4
5
6Memory not freed:
-----------------
Address Size Caller
0x00000000023be6a0 0x8 at /home/zyliu12/Documents/memoryleak/onefile/test.cpp:12
0x00000000023be6c0 0x8 at /home/zyliu12/Documents/memoryleak/onefile/test.cpp:12
...
从而将所有已malloc
而未free
的内存的malloc
处打印出来。
编译生成库文件
首先将内存泄露的函数转移到另一个文件fun.cpp
中:1
2
3
4
5
6
7
8
9
10
11
12
13
int memleak() {
int cnt = 100;
while (0 != cnt--) {
int *p = (int *)malloc(2 * sizeof(int));
usleep(10);
}
int *p2 = (int *)malloc(40 * sizeof(int));
return 0;
}
在main.cpp
中调用该函数:1
2
3
4
5
6
7
8
9
10
11
12
13
int main() {
setenv("MALLOC_TRACE", "taoge.log", 1);
mtrace();
memleak();
printf("over\n");
return 0;
}
这样mtrace()
与内存泄漏的函数不在一个文件中,我们来看看能否检测出内存泄漏的位置。
一步生成二进制文件的makefile为:
1 | TARGET := test |
运行test
并使用命令mtrace test taoge.log
看到以下内容:1
2
3
4
5
6Memory not freed:
-----------------
Address Size Caller
0x000000000098e6a0 0x8 at /home/zyliu12/Documents/memoryleak/staticlib/fun.cpp:9
...
0x000000000098f320 0xa0 at /home/zyliu12/Documents/memoryleak/staticlib/fun.cpp:12
此时能够正常判断内存泄露。
使用以下make文件先将fun.cpp
生成为动态库so文件。
1 | TARGET := test |
在执行二进制文件之前,需要先设置工作路径:
1 | export LD_LIBRARY_PATH=${pwd}:${LD_LIBRARY_PATH} |
运行二进制文件生成log,使用命令mtrace test taoge.log
看到以下内容:1
2
3
40x0000000000b466a0 0x400 at 0x7fd589037185
...
0x0000000000b47690 0x8 at 0x7fd58939377a
0x0000000000b476b0 0x8 at 0x7fd58939377a
可以看到只有地址而没有代码行号。
定位内存泄漏行号
主要是通过addr2line
由地址定位到行号。
为了得到加载库的地址,首先在代码中添加:1
fprintf(stderr, "pid=%d\n", getpid());
这行代码可用于获得程序的pid。在获得程序pid之后,假设pid为7954,进入/proc
目录,执行指令cat 7954/maps
。其输出如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2300400000-00401000 r-xp 00000000 08:01 404531 /home/zyliu12/Documents/memoryleak/staticlib/test
00600000-00601000 r--p 00000000 08:01 404531 /home/zyliu12/Documents/memoryleak/staticlib/test
00601000-00602000 rw-p 00001000 08:01 404531 /home/zyliu12/Documents/memoryleak/staticlib/test
017b2000-017d3000 rw-p 00000000 00:00 0 [heap]
7fb30a05e000-7fb30a21e000 r-xp 00000000 08:01 660718 /lib/x86_64-linux-gnu/libc-2.23.so
7fb30a21e000-7fb30a41d000 ---p 001c0000 08:01 660718 /lib/x86_64-linux-gnu/libc-2.23.so
7fb30a41d000-7fb30a421000 r--p 001bf000 08:01 660718 /lib/x86_64-linux-gnu/libc-2.23.so
7fb30a421000-7fb30a423000 rw-p 001c3000 08:01 660718 /lib/x86_64-linux-gnu/libc-2.23.so
7fb30a423000-7fb30a427000 rw-p 00000000 00:00 0
7fb30a427000-7fb30a428000 r-xp 00000000 08:01 404409 /home/zyliu12/Documents/memoryleak/staticlib/libfun.so
7fb30a428000-7fb30a627000 ---p 00001000 08:01 404409 /home/zyliu12/Documents/memoryleak/staticlib/libfun.so
7fb30a627000-7fb30a628000 r--p 00000000 08:01 404409 /home/zyliu12/Documents/memoryleak/staticlib/libfun.so
7fb30a628000-7fb30a629000 rw-p 00001000 08:01 404409 /home/zyliu12/Documents/memoryleak/staticlib/libfun.so
7fb30a629000-7fb30a64f000 r-xp 00000000 08:01 660690 /lib/x86_64-linux-gnu/ld-2.23.so
7fb30a833000-7fb30a836000 rw-p 00000000 00:00 0
7fb30a84c000-7fb30a84e000 rw-p 00000000 00:00 0
7fb30a84e000-7fb30a84f000 r--p 00025000 08:01 660690 /lib/x86_64-linux-gnu/ld-2.23.so
7fb30a84f000-7fb30a850000 rw-p 00026000 08:01 660690 /lib/x86_64-linux-gnu/ld-2.23.so
7fb30a850000-7fb30a851000 rw-p 00000000 00:00 0
7ffd7c1e6000-7ffd7c207000 rw-p 00000000 00:00 0 [stack]
7ffd7c23b000-7ffd7c23d000 r--p 00000000 00:00 0 [vvar]
7ffd7c23d000-7ffd7c23f000 r-xp 00000000 00:00 0 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
可以看到libfun.so
的加载地址以7fb30a427000
开始。
再次查看mtrace test taoge.log
:1
0x00000000017b26a0 0x8 at 0x7fb30a42771e
0x7fb30a42771e - 0x7fb30a427000
可得0x00000000071e
,这就是出错代码在libfun.so
中的相对位置。
执行指令addr2line -e ./libfun.so 0x00000000071e
,可得出错代码行号:1
/home/zyliu12/Documents/memoryleak/staticlib/fun.cpp:10