一、前言:
对于Linux开发,valgrind的重要性是不言而喻的。尤其在memory error、memory leak的问题定位和排查方面,发挥着极其重要的作用。
记得刚毕业那会儿,当年的部门领导曾经无数次强调valgrind重要性。曾经在开发过程中,虽然也有使用valgrind,但是从来没有对valgrind这个工具做过完整总结。
近期决定对valgrind做一个总结,也算是偿还以前欠下的债吧。
二、概述:
1. 关于编译选项:
针对memcheck工具,需要注意以下几点:
a. 强烈推荐被调试的目标程序在编译时加入-g参数,这样再运行valgrind memcheck时,可以拿到更为丰富的调试信息,比如行号,调用栈等。
b. 当使用-O0编译目标程序时,valgrind可以保证输出的所有警告、错误提示信息都是准确的,副作用是程序运行会非常慢。
c. 当使用-O1编译目标程序时,valgrind可以保证程序运行速度相对较快,副作用是无法保证错误提示信息100%精确,比如,行号会不精确。(这点也很好理解,因为-O1下编译器会对指令进行重排)
d. valgrind不推荐使用-O2和-O3编译目标程序,此时错误提示信息可能会存在误报,比如,valgrind会报大量uninitialised-value错误,实际这些错误并不真实存在。
e. 在编译目标程序时添加-fno-inline,有利于valgrind生成更为精确的堆栈信息。(非必需)
注意:如果不添加-fno-inline选项,也可以在运行valgrind时添加--read-inline-info=yes,这样valgrind会读取目标程序调试信息,使函数调用链能够正确显示。
f. 在编译目标程序时推荐使用-Wall,打印所有编译器告警信息。因为在较高的编译优化级别下,valgrind可能无法检测出全部编译期告警信息。
其他用于profile相关的工具,比如Cachegrind,通常不受编译优化选项的影响。在测试前,往往需要让程序在正常的编译优化选项下进行编译(比如-O2/-O3),之后再运行valgrind。
2. 关于输出信息:
为了说明valgrind的基本使用,以下是一个简单的示例(来自valgrind官网)。
#include
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;
}
当运行valgrind时,程序会输出以下信息:
[adam040606@localhost test01]$ valgrind demo
==17084== Memcheck, a memory error detector
==17084== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==17084== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==17084== Command: demo
==17084==
==17084== Invalid write of size 4
==17084== at 0x40054E: f (in /home/adam040606/Projects/valgrindTests/test01/demo)
==17084== by 0x40055E: main (in /home/adam040606/Projects/valgrindTests/test01/demo)
==17084== Address 0x51f7068 is 0 bytes after a block of size 40 alloc'd
==17084== at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==17084== by 0x400541: f (in /home/adam040606/Projects/valgrindTests/test01/demo)
==17084== by 0x40055E: main (in /home/adam040606/Projects/valgrindTests/test01/demo)
==17084==
==17084==
==17084== HEAP SUMMARY:
==17084== in use at exit: 40 bytes in 1 blocks
==17084== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==17084==
==17084== LEAK SUMMARY:
==17084== definitely lost: 40 bytes in 1 blocks
==17084== indirectly lost: 0 bytes in 0 blocks
==17084== possibly lost: 0 bytes in 0 blocks
==17084== still reachable: 0 bytes in 0 blocks
==17084== suppressed: 0 bytes in 0 blocks
==17084== Rerun with --leak-check=full to see details of leaked memory
==17084==
==17084== For counts of detected and suppressed errors, rerun with: -v
==17084== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
关于上面这个例子,有几点是值得关注的:
a) valgrind实际上提供了一组默认工具集。如果不指定具体运行哪个工具集,则默认运行memcheck。
在上面的示例中,valgrind demo 实际等价于 valgrind --tool=memcheck demo。
b) 最左侧的数字 ——17084,实是PID。
c) Invalid write of size 4
说明了具体的错误类型,说明发生了写越界。
由于目标程序采用-O0进行编译,可以看到出现错误时完整的堆栈信息。
d) definitely lost: 40 bytes in 1 blocks
上述信息说明代码中存在明确的内存泄漏。
如果在运行时添加了--leak-check=full,则会输出以下信息:
==17543== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==17543== at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==17543== by 0x400541: f (in /home/adam040606/Projects/valgrindTests/test01/demo)
==17543== by 0x40055E: main (in /home/adam040606/Projects/valgrindTests/test01/demo)