使用valgrind检测内存问题_编程语言

valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。

1

valgrind安装


可以到官网下载最新的源码包:valgrind官网下载,也可以直接使用 c_utils/debug/valgrind 目录提供的 valgrind-3.13.0.tar.bz2 源码包。

  1. 首先解压源码包
tar xjf valgrind-3.13.0.tar.bz2

  1. 进入解压目录,执行配置文件
cd valgrind-3.13.0/
./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking whether to enable maintainer-specific portions of Makefiles... no
checking whether ln -s works... yes
...

  1. 配置成功后执行make编译
make

  1. 安装即可
make install

2

内存泄漏检测


这是valgrind最常用一个小功能。程序如下,详见 c_utils/debug/valgrind/test1.c

使用valgrind检测内存问题_编程语言_02

这是一段申请内存但是没有释放的程序,首先编译一下

gcc test1.c -g -o test1

然后我们使用valgrind工具进行检测

$ valgrind --tool=memcheck --leak-check=full ./test1

==2473== Memcheck, a memory error detector
==2473== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2473== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2473== Command: ./test1
==2473==
==2473==
==2473== HEAP SUMMARY:
==2473== in use at exit: 100 bytes in 1 blocks
==2473== total heap usage: 1 allocs, 0 frees, 100 bytes allocated
==2473==
==2473== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2473== at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==2473== by 0x40053E: main (test1.c:6)
==2473==
==2473== LEAK SUMMARY:
==2473== definitely lost: 100 bytes in 1 blocks
==2473== indirectly lost: 0 bytes in 0 blocks
==2473== possibly lost: 0 bytes in 0 blocks
==2473== still reachable: 0 bytes in 0 blocks
==2473== suppressed: 0 bytes in 0 blocks
==2473==
==2473== For counts of detected and suppressed errors, rerun with: -v
==2473== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

从HEAP SUMMARY下面可以清楚看到提示在test1.c文件的第6行,main函数中有100个字节的空间申请但是未释放。注意只有编译时候加了-g选项才能看到行号文件等信息。

3

误用未初始化变量检测


程序如下,详见 c_utils/debug/valgrind/test2.c

使用valgrind检测内存问题_linux_03

这里我们忘记了对 condition 变量进行初始化,就使用了此变量,首先编译一下

gcc test2.c -g -o test2

然后我们使用valgrind工具进行检测

$ valgrind ./test2

==5814== Memcheck, a memory error detector
==5814== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5814== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==5814== Command: ./test2
==5814==
==5814== Conditional jump or move depends on uninitialised value(s)
==5814== at 0x400539: main (test2.c:8)

提示main (test2.c:8)使用了未初始化的变量作为条件判断依据。

4

内存访问越界检测


程序如下,详见 c_utils/debug/valgrind/test3.c

使用valgrind检测内存问题_java_04

程序中分别存在栈越界和堆越界,很遗憾的是我们运行程序一切正常,只有极少数情况下是直接运行出内存错误的,这非常危险,但是有概率性。

$ ./test3 
buf[0] = a
buf[1] = b
buf[2] = c
buf[3] = d
buf[4] = e
x[10] = a

首先比下面使用valgrind进行检测

$ valgrind ./test3

==8528== Invalid write of size 1
==8528== at 0x40064F: main (test3.c:15)
==8528== Address 0x520448a is 0 bytes after a block of size 10 alloc'd
==8528== at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==8528== by 0x400642: main (test3.c:14)
==8528==
==8528== Invalid read of size 1
==8528== at 0x40065A: main (test3.c:16)
==8528== Address 0x520448a is 0 bytes after a block of size 10 alloc'd
==8528== at 0x4C2DBF6: malloc (vg_replace_malloc.c:299)
==8528== by 0x400642: main (test3.c:14)
==8528==

可以看到检测到了14-16行的堆越界访问问题, 但是遗憾的是并未检测到9-12行的栈越界问题,所以valgrind是不能检测到静态内存问题的

5

massif工具使用


有时候我们的程序比较复杂,没办法很直观的分析出内存使用情况,这时候可以使用valgrind的massif工具来进行动态分析,通过不断的取程序堆的快照来达到监视程序内存分配的目的。程序如下,详见 c_utils/debug/valgrind/test4.c

使用valgrind检测内存问题_python_05

这是个动态分配和释放内存的过程,首先编译出可执行程序test4,然后使用massif工具进行检测,方法如下

$ valgrind --tool=massif ./test4

这里必须指定工具massif,输出信息并没有可用信息

==10727== Massif, a heap profiler
==10727== Copyright (C) 2003-2017, and GNU GPL'd, by Nicholas Nethercote
==10727== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==10727== Command: ./test4
==10727==
==10727==

但是此时使用ls命令会发现当前目录下已经产生了一个名为massif.out.XXX的文件。使用ms_print输出文件分析信息。

ms_print massif.out.10727

如果输出信息过多,可重定向到文件

ms_print massif.out.10727 > log.txt

查看log.txt内容

--------------------------------------------------------------------------------
Command: ./test4
Massif arguments: (none)
ms_print arguments: massif.out.10727
--------------------------------------------------------------------------------


KB
234.4^ #
| :#:
| :::#:::
| :::::#::::@
| ::::::#::::@:
| ::::::::#::::@:::
| ::::::::::#::::@::::
| :::::::::::#::::@::::::
| :::::::::::::#::::@::::::::
| :@:::::::::::::#::::@:::::::::
| ::@:::::::::::::#::::@:::::::::@:
| :::@:::::::::::::#::::@:::::::::@::
| :@:::@:::::::::::::#::::@:::::::::@::::
| :::@:::@:::::::::::::#::::@:::::::::@::::::
| @::::@:::@:::::::::::::#::::@:::::::::@::::::@
| ::@::::@:::@:::::::::::::#::::@:::::::::@::::::@:
| :::@::::@:::@:::::::::::::#::::@:::::::::@::::::@:::
| :::::@::::@:::@:::::::::::::#::::@:::::::::@::::::@:::::
| :::::::@::::@:::@:::::::::::::#::::@:::::::::@::::::@::::::@
| @:::::::@::::@:::@:::::::::::::#::::@:::::::::@::::::@::::::@:
0 +----------------------------------------------------------------------->Mi
0 0.998

Number of snapshots: 83
Detailed snapshots: [2, 4, 13, 18, 23, 39 (peak), 44, 58, 68, 78]

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
0 0 0 0 0 0
1 102,636 24 4 20 0
2 112,338 4,776 796 3,980 0
16.67% (796B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (796B) 0x4005E6: func (test4.c:6)
->16.67% (796B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
3 124,882 10,920 1,820 9,100 0
4 141,738 19,176 3,196 15,980 0
16.67% (3,196B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (3,196B) 0x4005E6: func (test4.c:6)
->16.67% (3,196B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
5 154,282 25,320 4,220 21,100 0
6 166,826 31,464 5,244 26,220 0
7 179,370 37,608 6,268 31,340 0
8 191,914 43,752 7,292 36,460 0
9 204,458 49,896 8,316 41,580 0
10 217,002 56,040 9,340 46,700 0
11 229,546 62,184 10,364 51,820 0
12 242,090 68,328 11,388 56,940 0
13 259,338 76,776 12,796 63,980 0
16.67% (12,796B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (12,796B) 0x4005E6: func (test4.c:6)
->16.67% (12,796B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
14 271,882 82,920 13,820 69,100 0
15 284,426 89,064 14,844 74,220 0
16 296,970 95,208 15,868 79,340 0
17 309,514 101,352 16,892 84,460 0
18 322,058 107,496 17,916 89,580 0
16.67% (17,916B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (17,916B) 0x4005E6: func (test4.c:6)
->16.67% (17,916B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
19 334,602 113,640 18,940 94,700 0
20 347,146 119,784 19,964 99,820 0
21 359,690 125,928 20,988 104,940 0
22 372,234 132,072 22,012 110,060 0
23 384,778 138,216 23,036 115,180 0
16.67% (23,036B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (23,036B) 0x4005E6: func (test4.c:6)
->16.67% (23,036B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
24 397,322 144,360 24,060 120,300 0
25 409,866 150,504 25,084 125,420 0
26 422,410 156,648 26,108 130,540 0
27 434,954 162,792 27,132 135,660 0
28 447,498 168,936 28,156 140,780 0
29 460,042 175,080 29,180 145,900 0
30 472,586 181,224 30,204 151,020 0
31 485,130 187,368 31,228 156,140 0
32 497,674 193,512 32,252 161,260 0
33 510,218 199,656 33,276 166,380 0
34 522,762 205,800 34,300 171,500 0
35 535,306 211,944 35,324 176,620 0
36 547,850 218,088 36,348 181,740 0
37 560,394 224,232 37,372 186,860 0
38 572,938 230,376 38,396 191,980 0
39 593,642 240,000 40,000 200,000 0
16.67% (40,000B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (40,000B) 0x4005E6: func (test4.c:6)
->16.67% (40,000B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
40 606,246 233,400 38,900 194,500 0
41 618,850 226,824 37,804 189,020 0
42 631,454 220,248 36,708 183,540 0
43 644,058 213,672 35,612 178,060 0
44 656,662 207,096 34,516 172,580 0
16.67% (34,516B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (34,516B) 0x4005E6: func (test4.c:6)
->16.67% (34,516B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
45 669,266 200,520 33,420 167,100 0
46 681,870 193,944 32,324 161,620 0
47 694,474 187,368 31,228 156,140 0
48 707,078 180,792 30,132 150,660 0
49 725,984 170,928 28,488 142,440 0
50 735,690 165,864 27,644 138,220 0
51 745,396 160,800 26,800 134,000 0
52 755,102 155,736 25,956 129,780 0
53 764,808 150,672 25,112 125,560 0
54 774,514 145,608 24,268 121,340 0
55 784,220 140,544 23,424 117,120 0
56 793,926 135,480 22,580 112,900 0
57 803,632 130,416 21,736 108,680 0
58 813,338 125,352 20,892 104,460 0
16.67% (20,892B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (20,892B) 0x4005E6: func (test4.c:6)
->16.67% (20,892B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
59 823,044 120,288 20,048 100,240 0
60 832,750 115,224 19,204 96,020 0
61 842,456 110,160 18,360 91,800 0
62 852,162 105,096 17,516 87,580 0
63 861,868 100,032 16,672 83,360 0
64 871,574 94,968 15,828 79,140 0
65 881,280 89,904 14,984 74,920 0
66 890,986 84,840 14,140 70,700 0
67 900,692 79,776 13,296 66,480 0
68 910,398 74,712 12,452 62,260 0
16.67% (12,452B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (12,452B) 0x4005E6: func (test4.c:6)
->16.67% (12,452B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
69 920,104 69,648 11,608 58,040 0
70 929,810 64,584 10,764 53,820 0
71 939,516 59,520 9,920 49,600 0
72 949,222 54,456 9,076 45,380 0
73 958,928 49,392 8,232 41,160 0
74 968,634 44,328 7,388 36,940 0
75 978,340 39,264 6,544 32,720 0
76 988,046 34,200 5,700 28,500 0
77 997,752 29,136 4,856 24,280 0
78 1,007,458 24,072 4,012 20,060 0
16.67% (4,012B) (heap allocation functions) malloc/new/new[], --alloc-fns, etc.
->16.67% (4,012B) 0x4005E6: func (test4.c:6)
->16.67% (4,012B) 0x400648: main (test4.c:23)

--------------------------------------------------------------------------------
n time(i) total(B) useful-heap(B) extra-heap(B) stacks(B)
--------------------------------------------------------------------------------
79 1,017,164 19,008 3,168 15,840 0
80 1,026,870 13,944 2,324 11,620 0
81 1,036,576 8,880 1,480 7,400 0
82 1,046,282 3,816 636 3,180 0

从分析信息中可以动态看到随着时间变化,程序那些地方占用内存较多。

6

开发板上使用valgrind


如果程序只能运行在开发板上,那么此时如果想用valgrind工具,那么只能交叉编译,然后放到开发板上运行。这里只简单罗列下编译过程:

tar xjf valgrind-3.13.0.tar.bz2
cd valgrind-3.13.0/
mkdir install
./configure --host=arm-linux --prefix=$PWD/install
make
make install

make install 以后在 install/bin 目录中就可以看到生成的可执行文件

valgrind-3.13.0/install/bin$ ls
callgrind_annotate cg_annotate cg_merge valgrind valgrind-listener
callgrind_control cg_diff ms_print valgrind-di-server vgdb

拷贝开发板运行即可。

笔者在开发板调试过程中并未遇到网上提到安装目录必须和开发板目录一致导致的问题,反倒在运行时候遇到了如下错误提示:

valgrind:  Fatal error at startup: a function redirection
valgrind: which is mandatory for this platform-tool combination
valgrind: cannot be set up. Details of the redirection are:
valgrind:
valgrind: A must-be-redirected function
valgrind: whose name matches the pattern: strcmp
valgrind: in an object with soname matching: ld-linux-armhf.so.3
valgrind: was not found whilst processing
valgrind: symbols from the object with soname: ld-linux-armhf.so.3
valgrind:
valgrind: Possible fixes: (1, short term): install glibc's debuginfo
valgrind: package on this machine. (2, longer term): ask the packagers
valgrind: for your Linux distribution to please in future ship a non-
valgrind: stripped ld.so (or whatever the dynamic linker .so is called)
valgrind: that exports the above-named function using the standard
valgrind: calling conventions for this platform. The package you need
valgrind: to install for fix (1) is called
valgrind:
valgrind: On Debian, Ubuntu: libc6-dbg
valgrind: On SuSE, openSuSE, Fedora, RHEL: glibc-debuginfo
valgrind:
valgrind: Note that if you are debugging a 32 bit process on a
valgrind: 64 bit system, you will need a corresponding 32 bit debuginfo
valgrind: package (e.g. libc6-dbg:i386).
valgrind:
valgrind: Cannot continue -- exiting now. Sorry.

参考此文章解决:Valgrind for ARM with Linaro Toolchain requiring libc6-dbg with Buildroot


因为笔者开发板库使用strip命令裁剪过,所以只要保证文件系统 /lib 下的库使用未裁剪过的即可。

笔者追踪后发现是​​./ld-linux-armhf.so.3 -> ld-2.21.so​​这个库问题,只需要从交叉编译器拷贝原始未被裁剪过的库替换开发板里的即可解决。

另生成的部分工具不是都在ARM板上运行的,比如上面的​​ms_print​​实际上是一个Perl脚本,运行前可使用 file 命令来确定在哪执行,这里不再一一介绍。

END


推荐阅读:

​专辑|Linux文章汇总​

​专辑|程序人生​

​专辑|C语言​

​我的知识小密圈​

使用valgrind检测内存问题_java_06