valgrind是一款用于内存调试、内存泄漏检测以及性能分析的软件开发工具。
1
valgrind安装
可以到官网下载最新的源码包:valgrind官网下载,也可以直接使用 c_utils/debug/valgrind 目录提供的 valgrind-3.13.0.tar.bz2 源码包。
首先解压源码包
tar xjf valgrind-3.13.0.tar.bz2
进入解压目录,执行配置文件
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
...
配置成功后执行make编译
make
安装即可
make install
2
内存泄漏检测
这是valgrind最常用一个小功能。程序如下,详见 c_utils/debug/valgrind/test1.c
这是一段申请内存但是没有释放的程序,首先编译一下
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
这里我们忘记了对 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
程序中分别存在栈越界和堆越界,很遗憾的是我们运行程序一切正常,只有极少数情况下是直接运行出内存错误的,这非常危险,但是有概率性。
$ ./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
这是个动态分配和释放内存的过程,首先编译出可执行程序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 +----------------------------------------------------------------------->Mi0 0.998Number of snapshots: 83Detailed 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 01 102,636 24 4 20 02 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 04 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 06 166,826 31,464 5,244 26,220 07 179,370 37,608 6,268 31,340 08 191,914 43,752 7,292 36,460 09 204,458 49,896 8,316 41,580 010 217,002 56,040 9,340 46,700 011 229,546 62,184 10,364 51,820 012 242,090 68,328 11,388 56,940 013 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 015 284,426 89,064 14,844 74,220 016 296,970 95,208 15,868 79,340 017 309,514 101,352 16,892 84,460 018 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 020 347,146 119,784 19,964 99,820 021 359,690 125,928 20,988 104,940 022 372,234 132,072 22,012 110,060 023 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 025 409,866 150,504 25,084 125,420 026 422,410 156,648 26,108 130,540 027 434,954 162,792 27,132 135,660 028 447,498 168,936 28,156 140,780 029 460,042 175,080 29,180 145,900 030 472,586 181,224 30,204 151,020 031 485,130 187,368 31,228 156,140 032 497,674 193,512 32,252 161,260 033 510,218 199,656 33,276 166,380 034 522,762 205,800 34,300 171,500 035 535,306 211,944 35,324 176,620 036 547,850 218,088 36,348 181,740 037 560,394 224,232 37,372 186,860 038 572,938 230,376 38,396 191,980 039 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 041 618,850 226,824 37,804 189,020 042 631,454 220,248 36,708 183,540 043 644,058 213,672 35,612 178,060 044 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 046 681,870 193,944 32,324 161,620 047 694,474 187,368 31,228 156,140 048 707,078 180,792 30,132 150,660 049 725,984 170,928 28,488 142,440 050 735,690 165,864 27,644 138,220 051 745,396 160,800 26,800 134,000 052 755,102 155,736 25,956 129,780 053 764,808 150,672 25,112 125,560 054 774,514 145,608 24,268 121,340 055 784,220 140,544 23,424 117,120 056 793,926 135,480 22,580 112,900 057 803,632 130,416 21,736 108,680 058 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 060 832,750 115,224 19,204 96,020 061 842,456 110,160 18,360 91,800 062 852,162 105,096 17,516 87,580 063 861,868 100,032 16,672 83,360 064 871,574 94,968 15,828 79,140 065 881,280 89,904 14,984 74,920 066 890,986 84,840 14,140 70,700 067 900,692 79,776 13,296 66,480 068 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 070 929,810 64,584 10,764 53,820 071 939,516 59,520 9,920 49,600 072 949,222 54,456 9,076 45,380 073 958,928 49,392 8,232 41,160 074 968,634 44,328 7,388 36,940 075 978,340 39,264 6,544 32,720 076 988,046 34,200 5,700 28,500 077 997,752 29,136 4,856 24,280 078 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 080 1,026,870 13,944 2,324 11,620 081 1,036,576 8,880 1,480 7,400 082 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语言
我的知识小密圈
关注公众号,后台回复「1024」获取学习资料网盘链接。
欢迎点赞,关注,转发,在看,您的每一次鼓励,我都将铭记于心~
嵌入式Linux
微信扫描二维码,关注我的公众号