# Linux下程序调试分析工具Valgrind
# 1.基本介绍
Valgrind
是开源的Linux
程序调试工具,可以用来自动检测程序中存在的内存管理,线程同步的问题,帮助开发人员提升代码调试的效率。
Valgrind
还可以输出程序的Profile
信息,方便开发者定位程序中的性能问题。
Valgrind
是开源的Linux
程序,开发者可以根据需要自行修改源码后编译。
Valgrind
支持主流的Linux
系统,包括x86/Linux
, AMD64/Linux
和 PPC32/Linux
。
Valgrind
功能强大,是大型复杂程序调试和系统profile
分析的必备工具。
Valgrind
适用于各种程序,包括桌面应用,库文件,数据库,游戏,网络浏览器,网络服务器,分布式控制系统,虚拟现实框架,事物服务器,编译器,虚拟机,嵌入式软件等。
Valgrind
直接于程序的二进制文件交互,因此支持分析任何语言所编写的程序,不过Valgrind
工具主要是面向C/C++
程序的,因为C/C++
语言需要手动的管理内存,更容易出问题。
Valgrind 100%
覆盖用户空间的代码。
Valgrind
开发者社区也比较活跃,Valgrind
也在不断推出新版本。截至当前,Valgrind
的最新版本是20231031
发布的valgrind-3.22.0
。
# 2.下载编译安装
Valgrind
官方没有发布二进制文件,需要开发者自己下载源码在对应的平台上编译。
- 1)下载源码
一种方式是下载
*.tar.bz2
源码压缩包
https://valgrind.org/downloads/current.html (opens new window)
解压:
tar -jxvf valgrind 3.22.0.tar.bz2
git clone https://sourceware.org/git/valgrind.git
下载后编译安装:
cd valgrind
./autogen.sh
./configure --prefix=[installation_dir]
make
make install
# Valgrind C++
程序内存扫描示例
示例C++
程序:
// memory_leak.cpp
struct Foo {
int *p_ = nullptr;
Foo() {
p_ = new int[10];
}
};
int main(int argc, char **argv)
{
Foo foo;
return 0;
}
以上非常简单的一段代码,在类Foo
的构造函数中使用new
操作符在堆上分配了内存,而在使用结束后并没有使用delete
操作符释放。
使用g++
的-g
参数保留调试符号。
g++ memory_leak.cpp -g -o leak
使用gdb
调试上面代码,发现可以正确执行:
gdb leak -tui
start
b 12
next
c
使用valgrind
扫描这段代码生成的可执行文件leak
,发现存在内存泄漏:
valgrind --leak-check=yes ./leak
输出为:
# ==291190== Memcheck, a memory error detector
# ==291190== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
# ==291190== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
# ==291190== Command: ./leak
# ==291190==
# ==291190==
# ==291190== HEAP SUMMARY:
# ==291190== in use at exit: 40 bytes in 1 blocks
# ==291190== total heap usage: 2 allocs, 1 frees, 73,768 bytes allocated
# ==291190==
# ==291190== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
# ==291190== at 0x483E617: operator new[](unsigned long) (vg_replace_malloc.c:725)
# ==291190== by 0x401168: Foo::Foo() (valgrind.cpp:4)
# ==291190== by 0x401140: main (valgrind.cpp:11)
# ==291190==
# ==291190== LEAK SUMMARY:
# ==291190== definitely lost: 40 bytes in 1 blocks
# ==291190== indirectly lost: 0 bytes in 0 blocks
# ==291190== possibly lost: 0 bytes in 0 blocks
# ==291190== still reachable: 0 bytes in 0 blocks
# ==291190== suppressed: 0 bytes in 0 blocks
# ==291190==
# ==291190== For lists of detected and suppressed errors, rerun with: -s
# ==291190== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
上面的报告中,292153
是进程ID
在堆上分配了两块内存,Foo foo
和new int[10]
,但是只释放了一块,40
个字节的new int
数组所占的内存并没有释放。因此,程序结束后发生了内存泄漏。
# ==291190== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
# ==291190== at 0x483E617: operator new[](unsigned long) (vg_replace_malloc.c:725)
# ==291190== by 0x401168: Foo::Foo() (valgrind.cpp:4)
# ==291190== by 0x401140: main (valgrind.cpp:11)
上面的堆栈信息告诉我们程序是在哪里发生了内存泄漏。其中:
definitely lost
意味着内存泄漏,必须修复。probably lost
程序可能发生了内存泄漏,除非开发者明确知道自己对指针的操作,否则应该修复。
修复上面问题最简单的就是在析构函数中添加delete
操作
// memory_leak.cpp
struct Foo {
int *p_ = nullptr;
Foo() {
p_ = new int[10];
}
~Foo() {
delete[] p_;
}
};
int main(int argc, char **argv)
{
Foo foo;
return 0;
}
重新编译后,Valgrind
的扫描结果为:
# ==292153== Memcheck, a memory error detector
# ==292153== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.
# ==292153== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info
# ==292153== Command: ./leak
# ==292153==
# ==292153==
# ==292153== HEAP SUMMARY:
# ==292153== in use at exit: 0 bytes in 0 blocks
# ==292153== total heap usage: 2 allocs, 2 frees, 73,768 bytes allocated
# ==292153==
# ==292153== All heap blocks were freed -- no leaks are possible
# ==292153==
# ==292153== For lists of detected and suppressed errors, rerun with: -s
# ==292153== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
更多使用方法可以参考https://valgrind.org/docs/manual/manual-intro.html (opens new window)
# reference
1.https://valgrind.org/docs/manual/manual-intro.html (opens new window)