# Linux下程序调试分析工具Valgrind

# 1.基本介绍

Valgrind是开源的Linux程序调试工具,可以用来自动检测程序中存在的内存管理,线程同步的问题,帮助开发人员提升代码调试的效率。

Valgrind还可以输出程序的Profile信息,方便开发者定位程序中的性能问题。

Valgrind是开源的Linux程序,开发者可以根据需要自行修改源码后编译。

Valgrind支持主流的Linux系统,包括x86/Linux, AMD64/LinuxPPC32/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下载 (opens new window)

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 foonew 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)