ZoyaPatel

The Valgrind Quick Start Guide(Valgrind 快速开始指南)

SohaniSharma

 

前言

关于The Valgrind Quick Start Guide 说明

之所以我要翻译的这篇文章,主要有两个目的。第一是加深我对Valgrind 工具的理解。第二方便后期学习和复习,以及对中文社区作出一点自己的贡献(尽管我发现在CSDN等社区,已经有了相关的翻译内容-ZHOU YANG)

来源以及说明

这篇文章是对The Valgrind Quick Start Guide 的中文翻译。The Valgrind Quick Start Guide 是官方给出的关于Valgrind 工具指导内容。

1. 介绍

Valgrind 工具套件提供了许多调试和分析工具,可帮助您使程序更快、更正确。这些工具中最受欢迎一个被称之为 Memcheck。它可以检测 C 和 C++ 程序中常见与内存相关的错误,这些错误可能导致崩溃和不可预知的行为(crashes and unpredictable behaviour)。

本指南的内容包含使用 Memcheck 检测程序检测内存错误所需的最少需要了解的信息。关于Memcheck 和其他工具的完整文档,请阅读用户手册。

2. 准备一个程序

使用-g编译程序来包含调试信息,以便 Memcheck 的错误消息包含准确的行号。如果您可以容忍执行过程中减速,使用 -O0 也是一个好主意。使用-O1编译,有可能导致错误消息中的行号可能不准确。一般来说在编译的代码时候使用-O1编译,对于运行 Memcheck效果比较好 ,并且与运行速度相对于-O0提升明显。不建议使用 -O2及以上,因为 Memcheck 偶尔会报告实际上并不存在的未初始化值错误。

总结

  1. 使用-g来编译程序,来使用Memcheck ,使输出的错误消息可以包含准确的行号
  2. 编译时候开启-O0在运行的过程中比较慢,所以这对于较大的程序来说是不友好的
  3. 编译的时候开启-O1优点是对于Memcheck 执行效果比较好,同时速度比-O0更快。缺点是输出的错误信息,行号可能不准确(理解为不利于定位错误点)
  4. 开启-O2以及以上,这是不推荐的,因为可能会报告==未初始化值错误==

3. 在Memcheck下运行程序

如果程序是这样运行的(C/C++ 程序执行)

  myprog arg1 arg2

应该使用这样的命令行

valgrind --leak-check=yes myprog arg1 arg2

--leak-check 表示内存检查。命令表示:使用valgrind 工具对程序myporg 传入参数arg1 arg2 开始运行,在运行过程开启内存泄漏的检查。

程序会比正常运行的时候慢(比如:慢20倍或者30倍)并且会使用更多的内存。在执行完后,Memcheck 会发布有关检测到的内存错误和泄漏的消息。

4. 内存泄漏检测输出结果

这是一个C案例程序,文件名==a.c== 它包含内存错误(memory error)和内存泄漏(memory leak)

  #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 内存没有被free 释放掉

 int main(void)
{
    f();
    return 0;
}

大多数错误信息如下所示,描述了问题 1,堆块溢出(heap block overrun)

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

注意事项

  1. 每一个错误信息都包含大量的内容,请仔细阅读
  2. 19182是程序的进程编号(Process ID ),通常不太重要
  3. 第一行("Invalid write...")告诉你错误的类型。这里是,因为堆块溢出,程序写入了一些不该写入的内存地址。
  4. 在第一行的下面,是一个堆栈追踪(stack track),告知程序出现错误的位置。堆栈跟踪可能会变得非常大,并且是令人困惑的(why?),尤其是使用了C++ STL 的时候。如果堆栈跟踪内存不是足够的(详细),使用--num-callers选项会使得更大。
  5. 代码地址(例如 0x804838F)通常不重要,但有时对于追踪特别奇怪的错误至关重要。
  6. 有些错误消息有第二个部分,它描述了所涉及的内存地址。比如:写入的内存刚刚超过在 example.c 的第 5 行使用 malloc() 分配的块的末尾(Address 0x1BA45050 is 0 bytes after a block of size 40 alloc'd)。

按照报告的顺序修复错误是值得的,因为后面的错误可能是由早期的错误引起的。未能做到这一点是 Memcheck 遇到困难的常见原因。

注入:valgrind 错误解决方式,需要从头到尾按照顺序来解决。因为:前面错误会导致后面的错误的出现。不按照顺序的话,在解决错误的时候,会出现一些问题。遇到困难

内存泄漏(memory leak )消息如下所示:

 ==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 (a.c:5)
==19182==   by 0x80483AB: main (a.c:11)

堆栈跟踪会告诉您泄漏内存的==分配位置==(allocated )。不幸的是,Memcheck 无法告诉您内存泄漏的原因。(Ignore the "vg_replace_malloc.c", that's an implementation detail.)忽略“vg_replace_malloc.c”,这是一个实现细节。

有几种泄漏;两个最重要的类别是:

  • "definitely lost": your program is leaking memory -- fix it!
  • "probably lost":您的程序正在泄漏内存,除非您正在使用指针做一些有趣的事情(例如将它们移动到指向堆块的中间)。

Memcheck 还报告==未初始化值(uses of uninitialised values)==的使用,最常见的是“条件跳转或移动取决于未初始化的值("Conditional jump or move depends on uninitialised value(s))”。很难确定这些错误的根本原因。尝试使用 --track-origins=yes 来获取额外信息,可能会导致Memcheck 运行速度变慢,但获得的额外信息通常可以节省大量时间来确定未初始化值的来源。

如果您不理解错误消息,请参阅 Valgrind 用户手册Memcheck 错误消息的解释, 其中包含 Memcheck 产生的所有错误消息的示例。

5. 注意事项

Memcheck 并不完善;它偶尔会产生误报,并且有机制来抑制这些错误误报情况(请参阅 Valgrind 用户手册中的抑制错误)。它通常在 99% 的情况下都是正确的,因此您应该小心忽略它的错误消息。毕竟,您不会忽略编译器生成的警告消息,对吧?如果 Memcheck 报告了许多在库代码中错误,这些错误无法更改,通常抑制机制可以有效的抑制这类消息。默认的抑制设置隐藏了很多这种错误。我们常常会遇到这些,但通常Memecheck 会将这些错误隐藏掉。

Memcheck 无法检测您的程序存在的每个内存错误。例如,它无法检测对静态分配或堆栈上的数组,发生超出范围的读取或写入的情况。但它应该检测到许多可能使您的程序崩溃的错误(例如,产生分段错误segmentation fault)

试着让你的程序保持干净,也就是 Memcheck 不会报告任何错误。一旦实现Memcheck clean 状态,不输出任何错误,那么一旦程序发生了更改,就会很容易看见什么地方的更改导致Memcheck 报告错误。多年使用 Memcheck 的经验表明,即使是大型程序也可以以干净的方式运行 Memcheck。例如,KDE、OpenOffice.org 和 Firefox 的大部分都是 Memcheck-clean,或者非常接近它。

6. 更多信息

请查阅Valgrind 常见问题解答Valgrind 用户手册,其中包含更多信息。在Valgrind 的发行版本中,有一些其他工具,我们可以通过--tool 来调用。

7.参考


总结

GCC 编译选项对于Memcheck 设置

GCC Compiler Options 补充说明

OptionsDetails
-O0explicitly turns off all optimizations. 关闭所有的优化
-O1enables a few optimizations, but isn't very useful overall. 启用了一些优化,但总体上不是很有用。
-O2is the most commonly used optimization flag.是最常用的优化标志。
-O3enable even more optimizations, but can actually harm the runtime of your code by making it take up more space (so it doesn't fit into cache anymore).
-Os针对小型二进制文件进行优化。

总结

-O0等选项,对于编译器执行的是优化选项,但是对于程序内存检测调试来说,是有害的。Memcheck 推荐使用-O1启动了部分优化,但是不会有太多问题,只是会导致行错误不准确,之所以不关闭所有优化,是因为Memcheck 检测时候,程序会存在过慢问题。

这里很明显是一个取舍问题

程序小的话,不开启优化-O0,使用Memcheck 执行检测。
程序过大的话,开启部分优化-O1,只是会导致一些行报告错误的准确,但是会提高程序的运行效率,有利于测试。
在任何情况下都不要开启-O2及以上,过度优化,对于Memcheck 检测来说是有害的。

前面错误会导致后面的错误的出现。所以我们需要从头开始一步一步解决Valgrind 报告的问题。

最常见和最多的内存泄漏错误

  • "definitely lost": your program is leaking memory -- fix it!

  • "probably lost":您的程序正在泄漏内存,除非您正在使用指针做一些有趣的事情(例如将它们移动到指向堆块的中间)。

误报的可能性很低很低,所以Memcheck 所有错误都可以解决。
引用的库代码存在错误的话,Memcheck 检测到后,会自己隐藏起来。因为:库代码对用户来说是不可更改的,所以并不需要这类消息。
有些内存错误情况无法报错,更多是报错segmentation fault

valgrind 含有其他的工具

除了 Memcheck,Valgrind 还有其他几个工具:[8]

None,在虚拟机中运行代码而不执行任何分析,因此在所有工具中具有最小的 CPU 和内存开销。由于 valgrind 本身提供了从分段错误的回溯,因此none工具以最小的开销提供此回溯。

Addrcheck,类似于 Memcheck,但 CPU 和内存开销要小得多,因此捕获的错误类型更少。自 3.2.0 版起,Addrcheck 已被删除。[9]

Massif,一个堆 分析器。单独的GUI massif-visualizer 可视化 Massif 的输出。

Helgrind和DRD,检测多线程代码中的竞争条件

Cachegrind,一个缓存分析器。单独的 GUI KCacheGrind 可视化来自 Cachegrind 的输出。

Callgrind是由 Josef Weidendorfer创建的调用图分析器,从 3.2.0 版开始添加到 Valgrind。

KCacheGrind 可以可视化 Callgrind 的输出。

DHAT,动态堆分析工具,可分析分配了多少内存、分配了多长时间以及内存使用模式。

exp-sgcheck(在 3.7 版之前命名为exp-ptrcheck),一个实验性工具,用于查找 Memcheck 无法找到的堆栈和全局数组溢出错误。[10]此工具的某些代码会导致误报。[11]

exp-bbv,一个性能模拟器,可以从小样本集中推断性能。

还有一些外部开发的工具可用。一个这样的工具是 ThreadSanitizer,另一个竞争条件检测器。[12] [13]

版权声明:感谢您的阅读,资源整理自网络,如果您发现任何侵权行为,请联系 理科生网 管理人员,管理员将及时删除侵权内容。否则均为 理科生网 原创内容,转载时请务必以超链接(而非纯文本链接)标注来源于理科生网及本文完整链接,感谢!{alertInfo}

Ahmedabad
Kolkata
Hyderabad
后一页 Bangalore 前一页

Random Manga

Ads

نموذج الاتصال