GDB通过设置断点查找内存泄漏

在GDB中, 可以通过break命令设置断点, 然后通过commands命令让断点被触发时自动执行一组命令

用法:

1
2
3
4
5
b function name
commands
... // 任何有效的gdb命令, 每一行一个命令
end

我们使用这个功能来查找C/C++程序中的内存泄漏, 而不用修改编译任何代码

具体思路:

  1. malloc设置断点, 在触发断点后, 记录log到文件
  2. 对free设置断点, 在触发断点后, 记录log到文件
  3. 对比log日志, malloc申请内存的次数大于free释放内存的次数, 说明存在内存泄漏

测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <string>
void test() {
for (int i = 0; i < 3; ++i) {
int * p = (int*)malloc(sizeof(int));
free(p);
}
}

int main() {
test();
return 0;
}

GDB命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建.gdbinit文件, gdb启动时会自动加载该文件
# 在文件内定义命令
define call_malloc
printf "call malloc :\n"
# 打印堆栈
bt
# 继续执行程序
continue
end

define call_free
printf "call free :\n"
# 打印堆栈
bt
# 继续执行程序
continue
end

启动gdb, 开始调试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
gdb a.out
set pagination off # 输出信息太多时不会暂停输出
set logging on log.txt # 指定log文件路径

b malloc # 对malloc设置断点
commands # 执行一组命令
call_malloc # .gdbinit中的自定义命令
end # 命令组结束

b free # 对free设置断点
commands # 执行一组命令
call_free # .gdbinit中的自定义命令
end # 命令组结束

run # 开始运行

等待程序运行结束后查看log.txt文件即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Breakpoint 1, __GI___libc_malloc (bytes=4) at malloc.c:2894
2894 {
call malloc :
#0 __GI___libc_malloc (bytes=4) at malloc.c:2894
#1 0x00000000004005b8 in test () at main.cpp:10
#2 0x00000000004005dd in main () at main.cpp:16

Breakpoint 1, __GI___libc_malloc (bytes=4) at malloc.c:2894
2894 {
call malloc :
#0 __GI___libc_malloc (bytes=4) at malloc.c:2894
#1 0x00000000004005b8 in test () at main.cpp:10
#2 0x00000000004005dd in main () at main.cpp:16

Breakpoint 2, __GI___libc_free (mem=0x602010) at malloc.c:2930
2930 = force_reg (__free_hook);
call free :
#0 __GI___libc_free (mem=0x602010) at malloc.c:2930
#1 0x00000000004005c8 in test () at main.cpp:11
#2 0x00000000004005dd in main () at main.cpp:16

Breakpoint 1, __GI___libc_malloc (bytes=4) at malloc.c:2894
2894 {
call malloc :
#0 __GI___libc_malloc (bytes=4) at malloc.c:2894
#1 0x00000000004005b8 in test () at main.cpp:10
#2 0x00000000004005dd in main () at main.cpp:16

Breakpoint 2, __GI___libc_free (mem=0x602010) at malloc.c:2930
2930 = force_reg (__free_hook);
call free :
#0 __GI___libc_free (mem=0x602010) at malloc.c:2930
#1 0x00000000004005c8 in test () at main.cpp:11
#2 0x00000000004005dd in main () at main.cpp:16

Breakpoint 1, __GI___libc_malloc (bytes=4) at malloc.c:2894
2894 {
call malloc :
#0 __GI___libc_malloc (bytes=4) at malloc.c:2894
#1 0x00000000004005b8 in test () at main.cpp:10
#2 0x00000000004005dd in main () at main.cpp:16

Breakpoint 2, __GI___libc_free (mem=0x602010) at malloc.c:2930
2930 = force_reg (__free_hook);
call free :
#0 __GI___libc_free (mem=0x602010) at malloc.c:2930
#1 0x00000000004005c8 in test () at main.cpp:11
#2 0x00000000004005dd in main () at main.cpp:16
[Inferior 1 (process 14425) exited normally]