【原创】Valgrind 基础

  1. 云栖社区>
  2. 博客>
  3. 正文

【原创】Valgrind 基础

摩云飞 2016-05-12 10:55:26 浏览3123
展开阅读全文

Valgrind 概述

Valgrind 是一种用于构建动态分析工具集的框架; 
Valgrind 工具能够自动探测许多种类的内存管理 bug 和线程 bug ,能够帮助你在细微处进行程序调优; 
你可以基于 Valgrind 构建新工具集; 

Valgrind 当前发布版包含了六种产品质量相关工具: 
  • 一种内存错误探测器
  • 两种线程错误探测器
  • 一种 cache 和分支预测分析工具
  • 一种 call-graph generating cache 和分支预测分析工具
  • 一种堆分析工具
Valgrind 还包含了三种实验性工具: 
  • heap/stack/global 数组越界探测器
  • a second heap profiler that examines how heap blocks are used
  • a SimPoint basic block vector generator
Valgrind 与 CPU 、操作系统,以及编译器和基础 C 库都是强相关的;移植 Valgrind 是比较困难的。 
尽管如此,Valgrind 已经支持如下各种平台: 
  • - X86/Linux
  • - AMD64/Linux
  • - PPC32/Linux
  • - PPC64/Linux
  • - ARM/Linux
  • - x86/MacOSX
  • - AMD64/MacOSX
  • - S390X/Linux
  • - MIPS32/Linux
  • - MIPS64/Linux
Note that AMD64 is just another name for x86_64 , and Valgrind runs fine on Intel processors.   
Also note that the core of MacOSX is called " Darwin " and this name is used sometimes. 

Valgrind is licensed under the GNU General Public License, version 2. 
However: if you contribute code, you need to make it available as GPL version 2 or later, and not 2-only. 

Valgrind  源码安装

基于 tar.bz2 源码包的安装: 
  1. Run ./configure, with some options if you wish.  The only interesting one is the usual --prefix=/where/you/want/it/installed.
  2. Run "make".
  3. Run "make install", possibly as root if the destination permissions require that.
  4. See if it works.  Try "valgrind ls -l".  Either this works, or it bombs out with some complaint. 
Important !  Do not move the valgrind installation into a place different from that specified by --prefix at build time.  
This will cause things to break in subtle ways, mostly when Valgrind handles fork/exec calls. 

Valgrind 工具套装

The Valgrind distribution includes the following debugging and profiling tools: 

Memcheck 
Memcheck 用于探测程序中存在的内存管理问题,主要针对 C 和 C++ 程序; 
当程序运行在 Memcheck 监控之下时,所有针对内存的读写操作都将被检查,所有 malloc/new/free/delete 调用都会被拦截; 
因此,Memcheck 能够探测到你的程序是否存在以下的问题: 
  • Accesses memory it shouldn't (areas not yet allocated, areas that have been freed, areas past the end of heap blocks, inaccessible areas of the stack).
    访问了不该访问的内存(尚未分配过的区域;已经被释放掉的区域;超过堆块边界的区域;栈中不可访问的区域)
  • Uses uninitialised values in dangerous ways.
    以危险方式使用未初始化的值
  • Leaks memory.
    存在内存泄露
  • Does bad frees of heap blocks (double frees, mismatched frees).
    针对堆块执行了错误的 free 行为(double free 或 mismatched free)
  • Passes overlapping source and destination memory blocks to memcpy() and related functions.
    基于 memcpy() 或其他相关函数操作了存在重叠的源和目的地址所指向的内存块;
Memcheck 会在遇到上述错误的时候立即进行报告,同时给出错误发生对应的源码行号,以及触发该错误时对应的函数调用栈; 
Memcheck 是基于字节进行寻址跟踪,基于比特进行 value 的初始化跟踪;所以,其可以探测出单独一比特未初始化数据的使用,而不会作为比特运行错误进行报告; 
基于 Memcheck 运行程序时将会比常规运行慢 10-30 倍; 

Cachegrind 
Cachegrind 是一种 cache 分析工具; 
Cachegrind 能够针对 CPU 进行 I1, D1 和 L2 cache 的详细模拟,并精确的定位出代码中导致 cache miss 的地方; 
Cachegrind 能够确定出 cache miss 的次数、内存引用情况,以及会触发 cache miss 的每一行代码,每一个函数,每一个模块,并给出整个程序的情况摘要; 
Cachegrind 对于任何语言编写的程序都非常有用; 
基于 Cachegrind 运行程序时将会比常规运行慢 20-100 倍; 

Callgrind 
Callgrind 是针对 Cachegrind 的扩展; 
Callgrind 提供了 Cachegrind 所能提供的全部信息,还额外提供了关于 callgraphs 的信息; 
Callgrind 在 Valgrind 的 3.2.0 主发布版中被加入; 
另外还有一个名为 KCachegrind 的可视化工具,可以对 Callgrind 收集到的信息尽心更好的展示; 

Massif 
Massif 是一种堆分析工具; 
Massif 通过对程序堆获取常规快照,进而展开详细堆分析; 
Massif 能够生成随时间变化的堆使用图,其中会给出程序的哪些部分进行了绝大部分的内存分配操作; 
作为图的补充,通过 text 或 HTML 文件能够确定分配内存最多地方的更多信息; 
基于 Massif 运行程序时将会比常规运行慢 20 倍; 

Helgrind 
Helgrind 是一种线程调试器,用于查找多线程程序中的数据竞争; 
Helgrind 会查出特定类型内存的位置:被多于一个(POSIX p-)thread 所访问,但是却没有发现保证访问一致性的锁; 
这类位置已经隐式表明了多线程同步的缺失,将会导致非常难于发现的时序问题; 
Helgrind 对于任何使用 pthreads 的程序都非常有用; 

DRD 
DRD 是一种检测多线程 C 和 C++ 程序错误的工具; 
DRD 对于任何使用 POSIX 线程原语的程序有效;或对于任何构建与 POSIX 线程原语之上的、使用线程概念的程序有效; 
尽管 Helgrind 能够检测出锁定顺序违反等问题,但对于大部分程序来说,DRD 在进行分析时将花费更少的内存; 

Lackey, Nulgrind 
Lackey and Nulgrind are also included in the Valgrind distribution. They don't do very much, and are there for testing and demonstrative purposes. 

Other Tools 
Several other Valgrind tools have been created: you can find a list of them on the Variants / Patches page. 

FAQ 摘译

1.1. How do you pronounce "Valgrind"? 
The "Val" as in the word "value". The "grind" is pronounced with a short 'i' -- ie. "grinned" (rhymes with "tinned") rather than "grined" (rhymes with "find"). 

4.4. My program crashes normally, but doesn't under Valgrind, or vice versa. What's happening? 
基于 valgrind 运行目标程序和直接运行目标程序两种方式存在些许不同;例如,内存布局会不同线程调度方式会不同 

      在大多数情况下,上述差异不会导致什么问题,但当你的程序本身存在 bug 的时候,可能会遇到一些问题。例如,如果你的程序原本会崩溃于错误的访问了非法内存地址的情况,而在基于 valgrind 运行时可能,该地址可能就是合法的内存地址了。另一种情况是,如果你的程序存在数据竞争,但在 valgrind 下可能不会显现出来。 

      对于上面的情况,你是无法做出任何应对的,因为这就是 valgrind 的工作方式,即 valgrind 无法精确的复制出可执行程序的本地执行环境。当出现你的程序崩溃于内存错误,但在 valgrind 下无法出现时,大多数情况下,Memcheck 工具应该都能检测出错误的内存操作。 

4.5. Memcheck doesn't report any errors and I know my program has errors. 
产生上述问题可能有两种原因。 

      首先,默认情况下,valgrind 只会跟踪 top-level 进程,因此,如果你的程序会创建子进程,则默认情况下,子进程 不会被 valgrind 所跟踪。同样的,如果你的程序时通过 shell 脚本、Perl 脚本,或者其他类似的脚本启动的,那么 valgrind 将会跟踪 shell 或 Perl 解析器,或其他类似的东西。 

为了跟踪子进程,需要使用  --trace-children=yes 选项。 

      如果你打算跟踪的进程是由很多进程构成,最好的方式是将输出结构通过网络发送到某处。可以通过指定  --log-socket=127.0.0.1:12345 选项(将输出结果发送到本地 12345 端口)达成此目的。 你可以使用  valgrind-listener 程序建立该端口的监听。 
valgrind-listener 12345
当然,你必须事先启动监听进程才能正常工作。 

      其次,大多数 valgrind 工具的工作模式为,通过替换程序中的某些函数,例如 malloc ,为自定义版本,来达成探测和分析的目的 ,如果你的程序是静态链接的 ,则不会进程这种替换,从而无法进程有效的检测。例如,可能从 Memcheck 输出中看到输出 
All heap blocks were freed -- no leaks are possible
而实际上,你明确知道自己调用了 malloc 。解决办法就是使用  --soname-synonyms=somalloc=NONE 设置,或者在目标程序中避免使用静态链接。 

      还存在另外一种无法进行替换的情况,即使用了另外一种 malloc 库,例如 tcmalloc 或 jemalloc 等。在这种情况下,需要通过指定  --soname-synonyms=somalloc=zzzz 选项(其中 zzzz 为替换用的  malloc 库的 soname),以允许 valgrind 进行相应替换。 

4.6. Why doesn't Memcheck find the array overruns in this program? 
int static[5];

int main(void)
{
    int stack[5];

    static[5] = 0;
    stack [5] = 0;
          
    return 0;
}
非常不幸的是,Memcheck 无法针对全局数组或栈数组进行边界检查。我们很想做到,但是目前尚未找到一种合理的实现方式,令这种检查与 Memcheck 的工作方式相符,所以,非常抱歉。 
然而,实验工具  SGcheck 能够探测出这种错误。 你可以通过指定  --tool=exp-sgcheck 选项运行 valgrind 进程相关检查,唯一需要明确的是,该工具可能没有 Memcheck 那么健壮。 

5.2. With Memcheck's memory leak detector, what's the difference between "definitely lost", "indirectly lost", "possibly lost", "still reachable", and "suppressed"? 
详细信息可以参见用户手册中的 Memcheck 章节。 
简要说明如下: 
  • "definitely lost" 意味着你的程序一定存在内存泄露;
  • "indirectly lost" 意味着你的程序一定存在内存泄露,并且泄露情况和指针结构相关(例如,如果二叉树的根节点被判定为"definitely lost",则其所有子节点将被判定为"indirectly lost");如果你正确修复了类型为 "definitely lost" 的泄露,那么类型为 "indirectly lost" 的泄露应该会随着消失;
  • "possibly lost" 意味着你的程序一定存在内存泄露,除非你是故意进行着不符合常规的操作,例如将指针指向某个已分配内存块的中间位置。参见用户手册查询一些可能的情况。可以指定 --show-possibly-lost=no 选项屏蔽掉这类报告信息。
  • "still reachable" 意味着你的程序可能是没问题的,但确实没有释放掉一些本可以释放的内存。这种情况是很常见的,并且通常基于合理的理由。 可以通过 --show-reachable=yes 选项控制是否输出相应的报告信息。
  • "suppressed" 意味着有些泄露信息被压制了。在默认的 suppression 文件中可以看到一些 suppression 相关设置。理论上讲,你可以忽略这类错误。

5.4. Is it possible to attach Valgrind to a program that is already running? 
不可以,因为 valgrind 为程序运行使用了完全不同的环境设置,例如采用了不同的内存布局方案等。所以 valgrind 需要在程序启动伊始就进行完全控制。

It is possible to achieve something like this by running your program without any instrumentation (which involves a slow-down of about 5x, less than that of most tools), and then adding instrumentation once you get to a point of interest. Support for this must be provided by the tool, however, and Callgrind is the only tool that currently has such support. See the instructions on the callgrind_control program for details. 


实验输出

[root@Betty ~]# 
[root@Betty ~]# valgrind ls -l
==23285== Memcheck, a memory error detector
==23285== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23285== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==23285== Command: ls -l
==23285== 
总用量 504
-rw-------.  1 root root   1676 310 2014 anaconda-ks.cfg
...
==23285== 
==23285== HEAP SUMMARY:
==23285==     in use at exit: 20,296 bytes in 33 blocks
==23285==   total heap usage: 266 allocs, 233 frees, 99,073 bytes allocated
==23285== 
==23285== LEAK SUMMARY:
==23285==    definitely lost: 0 bytes in 0 blocks
==23285==    indirectly lost: 0 bytes in 0 blocks
==23285==      possibly lost: 0 bytes in 0 blocks
==23285==    still reachable: 20,296 bytes in 33 blocks
==23285==         suppressed: 0 bytes in 0 blocks
==23285== Rerun with --leak-check=full to see details of leaked memory
==23285== 
==23285== For counts of detected and suppressed errors, rerun with: -v
==23285== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
[root@Betty ~]# 
[root@Betty ~]# 

网友评论

登录后评论
0/500
评论
摩云飞
+ 关注