Valgrind Quick Start Guide
该篇参考官方文档,提供基本使用 Memcheck 检查内存使用出错的方法,详细介绍查看 User manual 。
准备自己的程序
- 使用 -g 参数,以便定位打印错误信息。
- 可以使用 -O0 ( gcc/g++ [-Olevel] ), 但是结果会导致编译速度变慢。
- -O2 及以上级别不推荐使用。
关于 -Olevel , 可以参考: https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html https://www.zhihu.com/question/27090458 作者:XlousZeng 链接:https://www.zhihu.com/question/27090458/answer/137944410 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 1.-O,-O1:这两个命令的效果是一样的,目的都是在不影响编译速度的前提下,尽量采用一些优化算法降低代码 大小和可执行代码的运行速度。并开启如下的优化选项:-fauto-inc-dec -fbranch-count-reg -fcombine-stack-adjustments -fcompare-elim -fcprop-registers -fdce -fdefer-pop -fdelayed-branch -fdse -fforward-propagate -fguess-branch-probability -fif-conversion2 -fif-conversion -finline-functions-called-once -fipa-pure-const -fipa-profile -fipa-reference -fmerge-constants -fmove-loop-invariants -freorder-blocks -fshrink-wrap -fshrink-wrap-separate -fsplit-wide-types -fssa-backprop -fssa-phiopt -fstore-merging -ftree-bit-ccp -ftree-ccp -ftree-ch -ftree-coalesce-vars -ftree-copy-prop -ftree-dce -ftree-dominator-opts -ftree-dse -ftree-forwprop -ftree-fre -ftree-phiprop -ftree-sink -ftree-slsr -ftree-sra -ftree-pta -ftree-ter -funit-at-a-time 2. -O2该优化选项会牺牲部分编译速度,除了执行-O1所执行的所有优化之外,还会采用几乎所有的目标配置支持 的优化算法,用以提高目标代码的运行速度。-fthread-jumps -falign-functions -falign-jumps -falign-loops -falign-labels -fcaller-saves -fcrossjumping -fcse-follow-jumps -fcse-skip-blocks -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively -fexpensive-optimizations -fgcse -fgcse-lm -fhoist-adjacent-loads -finline-small-functions -findirect-inlining -fipa-cp -fipa-cp-alignment -fipa-bit-cp -fipa-sra -fipa-icf -fisolate-erroneous-paths-dereference -flra-remat -foptimize-sibling-calls -foptimize-strlen -fpartial-inlining -fpeephole2 -freorder-blocks-algorithm=stc -freorder-blocks-and-partition -freorder-functions -frerun-cse-after-loop -fsched-interblock -fsched-spec -fschedule-insns -fschedule-insns2 -fstrict-aliasing -fstrict-overflow -ftree-builtin-call-dce -ftree-switch-conversion -ftree-tail-merge -fcode-hoisting -ftree-pre -ftree-vrp -fipa-ra 3. -O3该选项除了执行-O2所有的优化选项之外,一般都是采取很多向量化算法,提高代码的并行执行程度, 利用现代CPU中的流水线,Cache等。 -finline-functions // 采用一些启发式算法对函数进行内联 -funswitch-loops // 执行循环unswitch变换 -fpredictive-commoning // -fgcse-after-reload //执行全局的共同子表达式消除 -ftree-loop-vectorize // -ftree-loop-distribute-patterns -fsplit-paths -ftree-slp-vectorize -fvect-cost-model -ftree-partial-pre -fpeel-loops -fipa-cp-clone options 这个选项会提高执行代码的大小,当然会降低目标代码的执行时间。 4. -Os这个优化标识和-O3有异曲同工之妙,当然两者的目标不一样,-O3的目标是宁愿增加目标代码的大小, 也要拼命的提高运行速度,但是这个选项是在-O2的基础之上,尽量的降低目标代码的大小,这对于存储容量很 小的设备来说非常重要。为了降低目标代码大小,会禁用下列优化选项,一般就是压缩内存中的对齐空白 (alignment padding)-falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -freorder-blocks-algorithm=stc -freorder-blocks-and-partition -fprefetch-loop-arrays 5. -Ofast:该选项将不会严格遵循语言标准,除了启用所有的-O3优化选项之外,也会针对某些语言启用部分优化。 如:-ffast-math ,对于Fortran语言,还会启用下列选项:-fno-protect-parens -fstack-arrays 6.-Og:该标识会精心挑选部分与-g选项不冲突的优化选项,当然就能提供合理的优化水平,同时产生较好的可调试 信息和对语言标准的遵循程度。
执行程序
若你的程序执行格式是:
./myapp arg1 arg2
则你可以使用以下命令调试:
valgrind --leak-check=yes ./myapp arg1 arg2
Memcheck 是默认检测工具, 选项--leak-check
将开启详细的内存泄露检查。
执行检查将使得程序运行变慢,占用的内存变多。
valgrind Memcheck 输出解读
示例程序example.c, 其中有内存泄露和错误
#include <stdlib.h> void f(void) { int* x = malloc(10 * sizeof(int)); x[10] = 0; // problem 1: heap block overrun } // problem 2: memory leak -- x not freed int main(void) { f(); return 0; }
下面是一则错误信息:
==19182== Invalid write of size 4 ==19182== at 0x804838F: f (example.c:6) ==19182== by 0x80483AB: main (example.c:11) ==19182== Address 0x1BA45050 is 0 bytes after a block of size 40 alloc’d ==19182== at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130) ==19182== by 0x8048385: f (example.c:5) ==19182== by 0x80483AB: main (example.c:11)
解读要点:
- 每条错误信息都包含了大量的信息,需仔细斟酌。
- 上面的 19182 是线程 ID, 通常不是关键信息。
- 第一行中的:
Invalid write of size 4
表明了具体的错误类型。 此处表明程序非法写入了 4 字节导致堆块溢出。- 第二行第三行的栈跟踪信息表明了错误发生的位置, 括号内为文件名和行号。该信息可能很多,特别是使用了 C++ STL 库的时候。
你应该自下而上的读。如果栈跟踪信息不够详细,使用--num-callers
选项,可以使它变得更详细。- 代码地址如:
0x804838F
通常并不重要, 除非你在调试一个奇怪的bug。(可以想一想怎么用)- 该错误信息的第二部分,即第4行,描述了错误相关的内存信息。此处表明:
example.c
第 5 行x[10] = 0;
所写的内存超过了malloc()
分配的40字节内存。- 建议按报告的顺序修复错误,因为后面的错误可能是由前面的错误引起的连锁反应。
内存泄露的错误信息:
==19182== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==19182== at 0x1B8FF5CD: malloc (vg_replace_malloc.c:130) ==19182== by 0x8048385: f (example.c:5) ==19182== by 0x80483AB: main (example.c:11)
解读要点:
- 栈跟踪信息指明何处分配的内存泄露了。位置信息由下往上变得详细具体。以上指示了 main()函数中(example.c 第 10 行)
调用函数f ,在f函数中(example.c 第 5 行)处,进行了内存分配。遗憾的是并不能告知内存泄露的具体原因。 括号中的
(vg_replace_malloc.c:130)
表示malloc的具体实现细节,可以无视掉。- 内存泄露的需要特别重视的情况:
definitely lost
:必须重视和修复,程序有确切的内存泄露,通常是未释放动态分配的内存。
probably lost
: 程序存在内存泄露检测未初始化的值:
- 通常提示:
Conditional jump or move depends on uninitialised value(s)
- 找到以上错误的根源是个难点,使用
--track-origins=yes
获取额外信息, 但会导致检测变慢。 但会节约查找问题的时间。- 注意,未初始化的变量和空间有可能引起比较严重的问题。
关于内存检测错误详细的解读请参看 Valgrind 用户手册的 Explanation of error messages from Memcheck 章节。
附加说明
Memcheck 并不完美:
- 偶尔会有错误的提示。valgrind 提供错误屏蔽机制,参见用户手册 2.5.Suppressing errors。
- Memcheck 可能会报一些你引用的库的信息,但是你无法修改这些库(通常问题也不在这里),你可以用屏蔽机制屏蔽。
- Memcheck 并不能查找出所有的程序内存错误,例如无法探测出对静态分配或者是在栈上分配的数组的越界读写操作。
gdb对core文件的back trace可以互补。- 尽可能的让你的程序无内存报错。
- 更多详细信息参见用户手册。
网友评论