使用和了解Valgrind核心

来源:互联网 发布:阿富汗猎犬知乎 编辑:程序博客网 时间:2024/06/06 20:21

使用和了解Valgrind核心

目录

2.1。Valgrind与您的程序一致
2.2。入门
2.3。评论
2.4。报告错误
2.5。抑制错误
2.6。核心命令行选项
2.6.1。工具选择选项
2.6.2。基本选项
2.6.3。错误相关选项
2.6.4。malloc相关选项
2.6.5。不常见的选项
2.6.6。调试选项
2.6.7。设置默认选项
2.7。支持线程
2.7.1。调度和多线程性能
2.8。处理信号
2.9。建筑和安装Valgrind
2.10。如果你有问题
2.11。限制
2.12。示例运行
2.13。你可能看到的警告信息

本章介绍了Valgrind核心服务,命令行选项和行为。这意味着它是相关的,无论你使用什么特定的工具。这些信息应该足以使您有效地日常使用Valgrind。与Valgrind核心相关的高级主题在Valgrind的核心内容中介绍:高级主题。

术语的一点:本章中大多数提及“Valgrind”是指Valgrind的核心服务。

2.1。Valgrind与您的程序一致

Valgrind旨在尽可能不侵入。它直接与现有的可执行文件一起工作。您不需要重新编译,重新链接或以其他方式修改要检查的程序。

你这样调用Valgrind:

valgrind [valgrind-options] your-prog [your-prog-options]

最重要的选项是--tool哪个Valgrind工具运行。例如,如果要ls -l使用内存检查工具Memcheck 运行命令 ,请发出以下命令:

valgrind --tool = memcheck ls -l

但是,Memcheck是默认值,所以如果要使用它,您可以省略该--tool选项。

无论使用哪种工具,Valgrind在启动之前都会控制您的程序。调试信息是从可执行文件和关联的库中读取的,以便在适当的时候可以根据源代码位置来编写错误消息和其他输出。

然后您的程序在由Valgrind核心提供的合成CPU上运行。当第一次执行新代码时,核心将代码移动到所选择的工具。该工具为此添加了自己的测试代码,并将结果转回核心,该核心协调该仪器化代码的持续执行。

各种工具之间添加的仪器代码量有很大差异。在比例的一端,Memcheck添加代码来检查每个内存访问和每个计算的值,使其运行速度比本机慢10-50倍。另一方面,Nulgrind最小的工具根本不添加任何工具,只能造成4倍的减速。

Valgrind模拟程序执行的每个指令。因此,活动工具不仅会检查应用程序中的代码,还可以检查所有支持动态链接库(包括C库,图形库等)的配置文件。

如果您使用错误检测工具,Valgrind可能会检测系统库中的错误,例如您必须使用的GNU C或X11库。您可能不会对这些错误感兴趣,因为您可能无法控制该代码。因此,Valgrind允许您选择性地抑制错误,通过将它们记录在Valgrind启动时读取的抑制文件中。构建机制选择默认抑制,为您的机器上检测到的操作系统和库提供合理的行为。为了使写入更容易,您可以使用该 --gen-suppressions=yes选项。这告诉Valgrind为每个报告的错误打印一个压缩,然后您可以将其复制到抑制文件中。

不同的错误检查工具报告不同种类的错误。因此,抑制机制允许您说明每种抑制适用的工具或工具。

2.2。入门

首先,请考虑重新编译应用程序和支持启用调试信息的库(-g选项)可能是有益的。没有调试信息,最好的Valgrind工具将能够做的是猜测一个特定的代码段所属的功能,这使得错误消息和分析输出几乎没用。随着-g,您将收到指向相关源代码行的消息。

另外您可能想要考虑的一个选项是,如果您正在使用C ++,那么-fno-inline。这使得更容易看到函数调用链,这可以帮助减少在大型C ++应用程序上导航时的混乱。例如,使用Memcheck调试OpenOffice.org在使用此选项时会更容易一些。您不需要这样做,但是这样做有助于Valgrind产生更准确,更少的混淆错误报告。如果您打算使用GNU GDB或其他调试器调试程序,那么您可能已经设置好了。或者,Valgrind选项 --read-inline-info=yes指示Valgrind读取描述内联信息的调试信息。这样,即使应用程序使用内联编译,函数调用链也将被正确显示。

如果您打算使用Memcheck:在极少数情况下,已经观察到编译器优化(等于-O2 或者等等,有时候-O1)生成的代码会使Memcheck错误地报告未初始化的值错误或缺少未初始化的值错误。我们已经详细研究了这一点,不幸的是结果是这样做会给已经是一个缓慢的工具带来进一步的显着的放缓。所以最好的解决方案是关闭优化。因为这经常使事情变得难以控制,所以使用合理的妥协 -O这使您获得更高优化水平的大部分优势,同时保持Memcheck的假阳性或假阴性的机会相对较小。也,-Wall您应该编译代码, 因为它可以识别Valgrind在较高优化级别可能会错过的一些或所有问题。(-Wall 一般来说,使用也是一个好主意)所有其他工具(据我们所知)不受优化级别的影响,对于像Cachegrind这样的分析工具,最好在其正常优化级别编译程序。

Valgrind了解GCC 3.1及更高版本使用的DWARF2 / 3/4格式。用于“stabs”调试格式的读者(在3.1之前由GCC版本使用)在Valgrind 3.9.0中被禁用。

准备好滚动时,按上述方法运行Valgrind。请注意,您应该在此运行真实的(机器代码)可执行文件。如果您的应用程序是由例如shell或Perl脚本启动的,那么您需要修改它才能在真正的可执行文件上调用Valgrind。直属Valgrind的运行这些脚本会导致你得到有关错误报告 /bin/sh, /usr/bin/perl或者你使用任何解释。这可能不是你想要的,可能会令人困惑。你可以通过提供这个选项来强制这个问题 --trace-children=yes,但仍然可能会有混乱。

2.3。评论

Valgrind工具写一个评论,一串文本,详细的错误报告和其他重大事件。评论中的所有行都具有以下形式:

== 12345 == some-message-from-Valgrind

12345是进程ID。该方案可以方便地将程序输出与Valgrind注释区分开来,也可以容易地区分不同流程的注释,这些流程由于任何原因而被合并在一起。

默认情况下,Valgrind工具只会将重要信息写入评论,以免发生带有次要重要信息的信息。如果您想了解有关正在发生的更多信息,请重新运行,将-v选项传递给Valgrind。第二个 -v给出更多的细节。

您可以将注释引导到三个不同的地方:

  1. 默认值:将其发送到文件描述符,默认情况下为2(stderr)。所以,如果你给核心没有选项,它将写入标准错误流的注释。如果要将其发送到其他文件描述符,例如9号,您可以指定 --log-fd=9

    这是最简单和最常见的安排,但是当Valgrinding整个进程的树,特别是stdin / stdout / stderr特定的文件描述符可用于自己使用时,可能会导致问题。

  2. 一个较少侵入性的选项是将注释写入您指定的文件 --log-file=filename。有特殊的格式说明符可用于在日志文件名中使用进程ID或环境变量名。如果您的程序调用多个进程(特别是MPI程序),这些是有用的/必要的。有关 详细信息,请参阅基本选项部分。

  3. 最简单的选择是将评论发送到网络套接字。套接字被指定为IP地址和端口号对,如下所示:--log-socket=192.168.0.1:12345如果要将输出发送到主机IP 192.168.0.1端口12345(注意:我们不知道12345是否是预先存在的重要性的端口)。您也可以省略端口号: --log-socket=192.168.0.1在这种情况下,使用默认端口1500。该默认值由VG_CLO_DEFAULT_LOGPORT源中的常量定义 。

    请注意,不幸的是,您必须在此使用IP地址,而不是主机名。

    如果您没有在另一端收听东西,写入网络套接字是毫无意义的。我们提供一个简单的监听程序, valgrind-listener它接受指定端口上的连接,并将发送到stdout的任何东西复制。大概有人会告诉我们这是一个可怕的安全隐患。人们似乎有可能在充分的时间内写出更复杂的听众。

    valgrind-listener可以接受多达50个Valgrinded进程的同时连接。在每行输出的前面,打印当前活动连接数在圆括号中。

    valgrind-listener 接受三个命令行选项:

    -e --exit-at-zero

    当连接的进程的数量恢复为零时,退出。没有这个,它将永远运行,就是直到你发送Control-C。

    --max-connect=INTEGER

    默认情况下,监听器最多可以连接50个进程。偶尔,这个数字太小了。使用此选项提供不同的限制。例如 --max-connect=100

    portnumber

    将其侦听的端口从默认端口(1500)更改。指定的端口必须在1024到65535之间。同样的限制适用于由--log-socketValgrind本身指定的端口号 。

    如果Valgrinded进程无法连接到侦听器,无论什么原因(侦听器未运行,无效或无法访问的主机或端口等),Valgrind将切换回写入stderr的注释。任何失去与收听者建立连接的进程也是如此。换句话说,杀死侦听器不会杀死向其发送数据的进程。

关于评论和工具分析输出之间的关系,这里是一个重要的一点。评论包含Valgrind核心和所选工具的混合信息。如果工具报告错误,它会将其报告给评论。但是,如果该工具进行概要分析,则根据工具,配置文件数据将被写入某种文件,并且与执行的--log-*选项无关 。评论意图是一个低带宽,人类可读的频道。另一方面,分析数据通常是大量的,没有进一步的处理是没有意义的,这就是为什么我们选择这种安排。

2.4。报告错误

当错误检查工具检测到程序发生不良情况时,会将错误信息写入评论。这是Memcheck的一个例子:

== 25832 ==大小4读取无效== 25832 == at 0x8048724:BandMatrix :: ReSize(int,int,int)(bogon.cpp:45)== 25832 == by 0x80487AF:main(bogon.cpp:66)== 25832 ==地址0xBFFFF74C不是stack'd,malloc'd或free'd

该消息表示该程序对地址0xBFFFF74C执行了一个非法的4字节读取操作,只要Memcheck可以告诉,它不是有效的堆栈地址,也不对应于任何当前的堆块或最近释放的堆块。读取是在bogon.cpp同一文件的第66行从第45 行发出的。对于与识别的(当前的或释放的)堆块相关联的错误,例如读取释放的内存,Valgrind不仅报告发生错误的位置,而且在相关联的堆块被分配/释放的地方。

Valgrind记得所有错误报告。当检测到错误时,将与旧报告进行比较,以查看是否重复。如果是这样,则会注意到错误,但不会发出进一步的评论。这样就避免了一大堆重复的错误报告。

如果您想知道每个错误发生多少次,请运行该-v选项。执行完成后,所有的报告将随着它们的发生次数一并打印出来并排序。这使得很容易看出哪些错误最常发生。

在相关操作实际发生之前报告错误。例如,如果您正在使用Memcheck并且您的程序尝试从地址零读取,Memcheck会发出一个消息,这样会产生一个消息,您的程序可能会因分段错误而死亡。

一般来说,您应该按照报告的顺序尝试修复错误。不这样做可能会令人困惑。例如,当在Memcheck上运行时,将未初始化的值复制到多个存储器位置并稍后使用它们的程序将生成多个错误消息。第一个这样的错误消息可能给出最直接的线索,导致问题的根本原因。

检测重复错误的过程是相当昂贵的,如果您的程序产生大量错误,可能会成为显着的性能开销。为了避免严重的问题,Valgrind将会在收到1000个不同的错误后立即停止收集错误,总共有10,000,000个错误。在这种情况下,您可以停止您的程序并修复它,因为Valgrind在此之后不会告诉您任何其他有用的东西。请注意,在删除了抑制误差之后,1,000 / 10,000,000的限制适用。这些限制在m_errormgr.c必要时定义并可以增加。

为了避免这种限制,您可以使用该 --error-limit=no选项。那么Valgrind总是会显示错误,不管有多少。仔细使用此选项,因为它可能对性能有不良影响。

2.5。抑制错误

错误检查工具可以检测系统库中的许多问题,例如预装了操作系统的C库。你不能轻易地修复这些错误,但是您不希望看到这些错误(是的,有很多!)所以Valgrind在启动时读取一个错误列表来抑制。./configure系统构建时脚本创建默认压缩文件 。

您可以随时修改并添加到抑制文件,或者更好地编写自己的文件。允许多个压缩文件。如果您的项目的一部分包含您不能或不想修复的错误,但您不想不断提醒他们,这是有用的。

注: 到目前为止添加镇压最简单的方法是使用--gen-suppressions=yes在描述选项核心命令行选项。这会自动产生抑制。为获得最佳效果,您可能需要--gen-suppressions=yes手动编辑输出 ,在这种情况下,请阅读本节。

描述要抑制的每个错误都是非常具体地描述的,以最大限度地减少抑制指令无意中抑制一些你想要看到的类似错误的可能性。抑制机制旨在允许精确而灵活地规定误差来抑制。

如果使用该-v选项,则在执行结束时,Valgrind将为每个使用的抑制打印一行,给出其使用次数,其名称以及定义了抑制的文件名和行号。根据抑制类型,文件名和行号可选地附加附加信息(例如通过memcheck泄漏抑制抑制的块数和字节数)。以下是运行中使用的禁用valgrind -v --tool=memcheck ls -l

--1610-- used_suppression:2 dl-hack3-cond-1 /usr/lib/valgrind/default.supp:1234--1610-- used_suppression:2 glibc-2.5.x-on-SUSE-10.2-(PPC)-2a /usr/lib/valgrind/default.supp:1234

允许多个禁止文件。Valgrind负载抑制模式,$PREFIX/lib/valgrind/default.supp除非 --default-suppressions=no已指定。您可以通过指定--suppressions=/path/to/file.supp一次或多次来要求从其他文件添加抑制 。

如果您想了解有关抑制的更多信息,请查阅现有的压缩文件,同时阅读以下文档。glibc-2.3.supp源文件中的文件提供了一些很好的例子。

每个抑制都有以下组件:

  • 第一行:它的名字。这仅仅提供了一个方便的名称到抑制,由此它在使用镇压的打印输出时,程序完成内容中所指。名字不重要,任何识别字符串都会做。

  • 第二行:抑制的工具的名称(如果多于一个,以逗号分隔),以及由冒号分隔的抑制本身的名称(nb:不允许有空格),例如:

    tool_name1,tool_name2:suppression_name

    回想一下,Valgrind是一个模块化系统,其中不同的仪器工具可以在运行时观察您的程序。由于不同的工具检测到不同种类的错误,因此有必要说明抑制对哪些工具有意义。

    工具在启动时会抱怨工具不明白对此的任何抑制。工具忽略不针对他们的抑制。因此,将所有工具的禁止放在同一个压缩文件中是相当实用的。

  • 下一行:少数抑制类型在第二行之后有额外的信息(例如,Param 对Memcheck 的抑制)

  • 剩余行:这是错误的调用上下文 - 导致它的函数调用链。最多可以有24行。

    位置可以是共享对象或函数的名称。他们开始 obj:和 fun:分别。要匹配的功能和对象名称可能会使用通配符 *和 ?

    重要说明: C ++函数名称必须被 破坏。如果您手动写入抑制,请使用该--demangle=no选项在错误消息中获取被破坏的名称。一个被破坏的C ++名称的例子是 _ZN9QListView4showEv。这是GNU C ++编译器在内部使用的形式,以及在压缩文件中必须使用的形式。等效的被清空的名字,QListView::show()就是你在C ++源代码级别看到的。

    位置线也可以简单地“ ...”(三个点)。这是一个帧级通配符,匹配零个或多个帧。帧级通配符是有用的,因为它们可以容易地忽略感兴趣的帧之间不同数量的不感兴趣的帧。当编写压缩时,这通常是重要的,这些抑制是针对编译器完成的功能内联量的变化而进行的。

  • 最后,整个镇压必须在花括号之间。每个大括号必须是自己的第一个字符。

当抑制错误与抑制中的所有细节匹配时,抑制只会抑制错误。以下是一个例子:

{  __gconv_transform_ascii_internal / __ mbrtowc / mbtowc  MEMCHECK:值4  有趣的:__ gconv_transform_ascii_internal  有趣的:__ MBR * TOC  好玩的:mbtowc}

这意味着:仅对于Memcheck,当数据大小为4时,当该函数发生在函数__gconv_transform_ascii_internal中时,当从名称匹配的任何函数调用该函数时 ,禁止使用未初始化的值错误,当 __mbr*toc从 mbtowc。在任何其他情况下都不适用。为用户识别此抑制的字符串是__gconv_transform_ascii_internal/__mbrtowc/mbtowc

(有关Memcheck抑制类型的详细信息,请参阅编写抑制文件。)

另一个例子,再次为Memcheck工具:

{  libX11.so.6.2 / libX11.so.6.2 / libXaw.so.7.0  MEMCHECK:值4  OBJ:/usr/X11R6/lib/libX11.so.6.2  OBJ:/usr/X11R6/lib/libX11.so.6.2  OBJ:/usr/X11R6/lib/libXaw.so.7.0}

这样可以抑制libX11.so.6.2从任何位置调用时在同一个库中的任何地方调用 时的任何大小4未初始化值错误libXaw.so.7.0。不符合要求的位置是令人遗憾的,但是关于所有你可以期望的,因为在这个例子的Linux发行版上发布的X11库已经删除了它们的符号表。

虽然上述两个例子并不清楚,但您可以自由地混合obj:和 fun:排除一些压制。

最后,这里是一个使用三个框架通配符的例子:

{   一个做作-示例   MEMCHECK:泄漏   有趣:的malloc   ...   好玩的:DDD   ...   好玩的:CCC   ...   好玩的:主}
这样做会抑制Memcheck内存泄漏错误,在分配是通过main 调用(尽管任意数量的中介,包括零) ccc,通过调用ddd和最终调用 来完成的malloc.

2.6。核心命令行选项

如上所述,Valgrind的核心接受一组共同的选择。这些工具还可以接受特定于工具的选项,这些选项是为每个工具单独记录的。

在大多数情况下,Valgrind的默认设置成功地给出了合理的行为。我们按粗略类别分组可用的选项。

2.6.1。工具选择选项

单一最重要的选项。

--tool=<toolname> [default: memcheck]

运行Valgrind工具toolname,例如memcheck,cachegrind,callgrind,helgrind,drd,massif,lackey,none,exp-sgcheck,exp-bbv,exp-dhat等

2.6.2。基本选项

这些选项适用于所有工具。

-h --help

显示所有选项的帮助,包括核心和所选工具。如果该选项重复,则相当于给予 --help-debug

--help-debug

--help与之一样,但也列出了通常仅用于Valgrind开发人员的调试选项。

--version

显示Valgrind内核的版本号。工具可以有自己的版本号。有一个方案来确保只有核心版本是已知的工具才能执行的工具。这样做是为了尽量减少由于工具与核心版本不兼容而产生的奇怪问题的机会。

-q, --quiet

静默运行,只打印错误消息。如果您正在进行回归测试或有其他自动化测试机器,那么有用。

-v, --verbose

更冗长 提供有关程序各个方面的更多信息,例如:加载的共享对象,使用的禁用,检测和执行引擎的进度以及有关异常行为的警告。重复该选项会增加详细程度。

--trace-children=<yes|no> [default: no]

启用后,Valgrind将跟踪通过exec系统调用发起的子进程。这对于多进程程序是必需的。

请注意,Valgrind确实会追踪到一个fork(的孩子) ,因为 fork这样一个进程的相同拷贝就很难,所以这个选项可能被命名得很糟。然而,大多数fork电话的孩子 立即打电话exec 。

--trace-children-skip=patt1,patt2,...

此选项仅在指定时--trace-children=yes才有效果 。它允许一些孩子被跳过。该选项为Valgrind不应该跟踪的可执行文件的名称使用逗号分隔的模式列表。这些模式可以包括元字符? 和*,它们具有通常的意义。

这可以用于从Valgrind上运行的进程树中修剪不感兴趣的分支。但是,使用它时应该小心。当Valgrind跳过跟踪到可执行文件时,它不会跳过跟踪该可执行文件,它也跳过跟踪任何可执行文件的子进程。换句话说,该标志不仅仅是引起跟踪停止在指定的可执行文件 - 它跳过跟踪任何一个指定的可执行文件的整个进程子树。

--trace-children-skip-by-arg=patt1,patt2,...

这是一样的 --trace-children-skip,有一个区别:关于是否追踪到子进程的决定是通过检查子进程的参数而不是其可执行文件的名称来进行的。

--child-silent-after-fork=<yes|no> [default: no]

启用后,Valgrind不会显示任何来自fork调用的子进程的调试或记录输出。这可以使输出在处理创建子进程时更不容易混淆(尽管更具误导性)。它特别有用--trace-children=。如果要求XML输出(--xml=yes),则强烈建议使用此选项,否则来自子级和父级的XML可能会混淆,这通常会使其无用。

--vgdb=<no|yes|full> [default: yes]

当Valgrind的将提供“gdbserver的”功能 --vgdb=yes--vgdb=full指定的。这允许外部GNU GDB调试器在Valgrind上运行时控制和调试程序。 --vgdb=full引起显着的性能开销,但提供更精确的断点和观察点。有关详细说明,请参阅使用Valgrind的gdbserver和GDB调试程序。

如果嵌入式gdbserver已启用但目前没有使用gdb,则vgdb 命令行实用程序可以从shell向Valgrind发送“监视器命令”。Valgrind内核提供一组 Valgrind监视器命令。工具可以选择提供特定于工具的监视器命令,这些命令在工具特定的章节中有所记录。

--vgdb-error=<number> [default: 999999999]

当Valgrind gdbserver启用--vgdb=yes或时,使用此选项 --vgdb=full。报告错误的工具将number在冻结程序并等待您与GDB连接之前等待“ ”错误报告。因此,值为零将导致在执行程序之前启动gdbserver。这通常用于在执行前插入GDB断点,并且还可以与不报告错误的工具(如Massif)一起使用。

--vgdb-stop-at=<set> [default: none]

当Valgrind gdbserver启用--vgdb=yes或时,使用此选项 --vgdb=full--vgdb-error报告之后,将为每个错误调用Valgrind gdbserver 。您还可以询问为其他事件调用Valgrind gdbserver,以下列方式之一指定:

  • 一个或多个逗号分隔的列表 startup exit valgrindabexit

    startup exit valgrindabexit分别表示在程序执行之前,在程序的最后一个指令之后,在Valgrind异常退出(例如内部错误,内存不足,...)中调用gdbserver。

    注意:startup并且 --vgdb-error=0将会在程序执行之前调用Valgrind gdbserver。此外 --vgdb-error=0,还将导致您的程序停止所有后续错误。

  • all指定完整集。相当于 --vgdb-stop-at=startup,exit,valgrindabexit

  • none 为空集。

--track-fds=<yes|no> [default: no]

启用后,Valgrind将通过gdbserver monitor命令在出口或请求时打印出打开的文件描述符列表v.info open_fds。与每个文件描述符一起打印文件打开位置的堆栈回溯以及与文件描述符相关的任何细节,如文件名或套接字详细信息。

--time-stamp=<yes|no> [default: no]

启用后,每个消息之前都会显示自启动以来的已经逝去的时钟,表示为天,小时,分钟,秒和毫秒。

--log-fd=<number> [default: 2, stderr]

指定Valgrind应将其所有消息发送到指定的文件描述符。默认值2是标准错误通道(stderr)。请注意,这可能会干扰客户端自己使用stderr,因为Valgrind的输出将与客户端发送给stderr的任何输出交错。

--log-file=<filename>

指定Valgrind应将其所有邮件发送到指定的文件。如果文件名为空,则会导致中止。有三种特殊的格式说明符可以在文件名中使用。

%p被当前进程ID替换。这对于调用多个进程的程序非常有用。警告:如果您使用--trace-children=yes并且您的程序调用多个进程或您的程序fork而不调用exec,并且您不使用此说明符(或%q下面的说明符),则所有这些进程的Valgrind输出将进入一个文件,可能会混乱并且可能不完整。

%q{FOO}被替换为环境变量的内容FOO。如果 {FOO}部件畸形,会导致中止。此说明符很少需要,但在某些情况下(例如运行MPI程序时)非常有用。这个想法是指定一个变量,该变量将为作业中的每个进程设置不同,例如BPROC_RANK或MPI设置中适用的任何内容。如果未设置命名环境变量,则会导致中止。请注意,在某些shell中, 角色{}角色可能需要使用反斜杠进行转义。

%%被替换%

如果%其后的任何其他字符,则导致中止。

如果文件名指定相对文件名,则将其放入程序的初始工作目录中:当程序在fork之后或exec之后执行时,这是当前目录。如果它指定绝对文件名(即以'/'开始),那么它将放在那里。

--log-socket=<ip-address:port-number>

指定Valgrind应将其所有消息发送到指定IP地址的指定端口。可以省略端口,在这种情况下使用端口1500。如果无法连接到指定的套接字,则Valgrind将返回写入标准错误(stderr)的输出。此选项旨在与valgrind-listener程序一起使用 。有关详细信息,请参阅 手册中的注释。

2.6.3。错误相关选项

所有可以报告错误的工具都使用这些选项,例如Memcheck,而不是Cachegrind。

--xml=<yes|no> [default: no]

启用时,输出的重要部分(例如工具错误消息)将采用XML格式而不是纯文本格式。此外,XML输出将被发送到与纯文本输出不同的输出通道。因此,还必须使用的一个 --xml-fd--xml-file或 --xml-socket到指定的XML将被发送。

不太重要的消息将仍然以纯文本被打印,但是,因为XML输出和明文输出被发送到不同的输出信道(纯文本输出的目的地仍然由控制--log-fd--log-file 和--log-socket)这不应引起问题。

此选项旨在使消费Valgrind输出的工具(如GUI前端)变得更简单。目前这个选项适用于Memcheck,Helgrind,DRD和SGcheck。输出格式在docs/internals/xml-output-protocol4.txt Valgrind 3.5.0或更高版本的源代码树中的文件 中指定。

GUI请求XML输出时,推荐的选项是:--xml=yes启用XML输出, --xml-file将XML输出发送到(大概是GUI选择的)文件,--log-file将纯文本输出发送到第二个GUI选择的文件, --child-silent-after-fork=yes,并将 -q纯文本输出限制为由Valgrind本身创建的关键错误消息。例如,无法读取指定的抑制文件将被视为关键错误消息。这样,为了成功运行文本输出文件将为空。但是如果它不是空的,那么它将包含GUI用户应该注意的重要信息。

--xml-fd=<number> [default: -1, disabled]

指定Valgrind应将其XML输出发送到指定的文件描述符。它必须与之配合使用 --xml=yes

--xml-file=<filename>

指定Valgrind应将其XML输出发送到指定的文件。它必须与之配合使用 --xml=yes。出现在文件名中的任何%p或者 %q序列以与它们完全相同的方式进行扩展--log-file。有关--log-file详细信息,请参阅说明。

--xml-socket=<ip-address:port-number>

指定Valgrind应将指定端口的XML输出发送到指定的IP地址。它必须与之配合使用--xml=yes。参数的形式与使用的形式相同--log-socket。有关--log-socket 详细信息,请参阅说明。

--xml-user-comment=<string>

在XML输出的开始处嵌入一个额外的用户注释字符串。仅在--xml=yes指定时才能工作; 否则忽略。

--demangle=<yes|no> [default: yes]

启用/禁用C ++名称的自动解码(解码)。默认启用。启用后,Valgrind将尝试将编码的C ++名称转换为接近原始文件的东西。demangler处理由g ++版本2.X,3.X和4.X驱动的符号。

关于解密的一个重要事实是在压制文件中提到的函数名称应该处于它们的格式。在搜索适用的抑制方面,Valgrind不会使功能名称发生拆分,因为否则会使压缩文件内容取决于Valgrind的分离机器的状态,并且还会减缓抑制匹配。

--num-callers=<number> [default: 12]

指定标识程序位置的堆栈跟踪中显示的最大条目数。请注意,仅使用前四个功能位置(当前功能中的位置,以及其三个直接调用者的位置)常见错误。所以这并不影响所报错误的总数。

最大值为500.请注意,较高的设置将使Valgrind运行速度更慢,并占用更多内存,但在使用深嵌套的调用链的程序时可能会很有用。

--unw-stack-scan-thresh=<number> [default: 0] , --unw-stack-scan-frames=<number> [default: 5]

堆栈扫描支持仅在ARM目标上可用。

这些标志通过堆栈扫描启用和控制堆栈展开。当正常堆栈展开机制 - 使用Dwarf CFI记录和帧指针跟随失败时,堆栈扫描可能能够恢复堆栈跟踪。

请注意,堆栈扫描是一种不精确的启发式机制,可能会产生非常误导的结果,或者根本没有。它只能在紧急情况下使用,正常展开失败,重要的是仍然有堆栈跟踪。

堆栈扫描是一种简单的技术:展开器从堆栈中读取单词,并尝试猜测哪些可能是返回地址,通过检查它们是否指向ARM或Thumb调用指令。如果是这样,该词被添加到回溯。

当函数调用返回时,会发生主要的危险,其返回地址被暴露,并且调用一个新的函数,但新函数不会覆盖旧的地址。这样做的结果是回溯可能包含已经返回的函数的条目,因此非常混乱。

此实现的第二个限制是它将仅扫描包含起始堆栈指针的页面(通常为4KB)。如果堆栈帧较大,则这可能导致轨迹中仅存在少量(甚至不甚甚)。此外,如果您不幸运,并且在其包含页面的末尾附近有一个初始堆栈指针,扫描可能会错过所有有趣的帧。

默认情况下,堆栈扫描被禁用。正常的使用情况是要求堆栈跟踪否则会很短。所以,启用它,使用--unw-stack-scan-thresh=number。这要求Valgrind尝试使用堆栈扫描来“扩展”包含少于number帧的堆栈跟踪。

如果堆栈扫描发生,它将只生成最多指定的帧数--unw-stack-scan-frames。通常,堆栈扫描会生成这么多垃圾条目,默认情况下该值设置为低值(5)。在任何情况下,堆栈跟踪大于--num-callers创建指定的值。

--error-limit=<yes|no> [default: yes]

启用后,Valgrind停止报告错误,总共有10,000,000个,或者1,000个不同的。这是为了阻止错误跟踪机制在具有许多错误的程序中成为巨大的性能开销。

--error-exitcode=<number> [default: 0]

如果Valgrind在运行中报告任何错误,则指定返回的替代退出代码。当设置为默认值(零)时,Valgrind的返回值将始终是正在仿真的进程的返回值。当设置为非零值时,将返回该值,如果Valgrind检测到任何错误。这对于使用Valgrind作为自动化测试套件的一部分非常有用,因为通过检查返回码,可以轻松检测Valgrind报告错误的测试用例。

--error-markers=<begin>,<end> [default: none]

当错误输出为纯文本(即XML未使用)时, --error-markers指示在每个错误之前(之后)输出包含beginend)字符串的行。

这样的标记线便于搜索包含与程序输出混合的valgrind错误的输出文件中的错误和/或提取错误。

请注意,空标记被接受。因此,只能使用开始(或结束)标记。

--sigill-diagnostics=<yes|no> [default: yes]

启用/禁用打印非法指令诊断。默认情况下启用,但默认为禁用 --quiet。通过给出此选项,总是可以明确地覆盖默认值。

启用该功能后,在程序发出SIGILL信号之前,每当遇到Valgrind无法解码或转换的指令时,都会打印一条警告消息以及一些诊断信息。通常,非法指令表示程序中的错误或缺少对Valgrind特定指令的支持。但有些程序会故意尝试执行可能丢失的指令,并捕获SIGILL信号以检测处理器功能。使用此标志可以避免在这种情况下您会得到的诊断输出。

--show-below-main=<yes|no> [default: no]

默认情况下,错误的堆栈跟踪不会显示下面的任何函数,main因为大多数时候它不感兴趣C库东西和/或gobbledygook。或者,如果main堆栈跟踪中不存在堆栈跟踪,堆栈跟踪将不会显示main类似函数(如glibc)的 任何函数__libc_start_main。此外,如果 main跟踪中存在类似函数,则它们被归一化为(below main)使得输出更确定。

如果启用此选项,将显示所有堆栈跟踪条目,并且main类似的函数将不会被归一化。

--fullpath-after=<string> [default: don't show source paths]

默认情况下,Valgrind仅显示堆栈跟踪中的文件名,但不显示源文件的完整路径。当在大型项目中使用Valgrind,其中源位于多个不同的目录中时,这可能是不方便的。 --fullpath-after为这个问题提供了灵活的解决方案。当此选项存在时,将显示每个源文件的路径,其中包含以下所有重要的注意事项:如果string在路径中找到,则将string省略直到和包含的路径,否则路径未修改。注意,string不需要作为路径的前缀。

例如,考虑一个名为的文件 /home/janedoe/blah/src/foo/bar/xyzzy.c。指定--fullpath-after=/home/janedoe/blah/src/ 将导致Valgrind显示名称foo/bar/xyzzy.c

因为字符串不需要作为前缀,所以 --fullpath-after=src/会产生相同的输出。当路径包含任意机器生成的字符时,这很有用。例如,/my/build/dir/C32A1B47/blah/src/foo/xyzzy 可以修剪路径 以foo/xyzzy 使用 --fullpath-after=/blah/src/

如果您只想看到完整的路径,只需指定一个空字符串:--fullpath-after=。这不是一个特殊情况,只是上述规则的逻辑后果。

最后,你可以--fullpath-after 多次使用。任何外观都会导致Valgrind切换到生成完整路径并应用上述过滤规则。每个生成的路径按照--fullpath-after指定的顺序与所有指定的字符串进行比较。要匹配的第一个字符串将导致路径被截断,如上所述。如果不匹配,则显示完整路径。当源从多个不相关的目录中抽取时,这有助于切除前缀。

--extra-debuginfo-path=<path> [default: undefined and unused]

默认情况下,Valgrind在几个众所周知的路径中搜索调试对象,例如/usr/lib/debug/

但是,您可能希望将调试对象放置在任意位置,例如在具有有限本地存储空间的移动设备上运行Valgrind时的外部存储。另一个例子可能是您没有在运行Valgrind的系统上安装调试对象包的权限的情况。

在这些情况下,您可以提供绝对路径作为Valgrind通过指定搜索调试对象的额外最终位置 --extra-debuginfo-path=/path/to/debug/objects。给定的路径将被添加到搜索对象的绝对路径名称。例如,如果Valgrind正在寻找/w/x/y/zz.so 和--extra-debuginfo-path=/a/b/c指定的debuginfo,那么它将在此处查找一个调试对象 /a/b/c/w/x/y/zz.so

该标志只能指定一次。如果指定多次,则只会保留最后一个实例。

--debuginfo-server=ipaddr:port [default: undefined and unused]

这是在3.9.0版本中引入的一个新的,实验性的功能。

在某些情况下,从存储在不同机器上的对象读取debuginfo可能很方便。使用此标志,Valgrind将在本地文件系统中找不到debuginfo对象时查询运行在ipaddr侦听端口上的debuginfo服务器port

debuginfo服务器必须接受端口上的TCP连接port。debuginfo服务器包含在源文件中auxprogs/valgrind-di-server.c。它将仅从它启动的目录中提供。 port如果未指定,默认为客户端和服务器的1500。

如果Valgrind /w/x/y/zz.so通过使用debuginfo服务器查找debuginfo,它将剥离路径名组件,并仅zz.so在服务器上请求。反过来,它将仅在其当前工作目录中查找匹配的debuginfo对象。

根据Valgrind的要求,debuginfo数据以小片段(8 KB)传输。使用LZO压缩每个块以减少传输时间。通过单阶段802.11g(WiFi)网络链路实现了最佳性能。

请注意,即使使用debuginfo服务器,也会执行使用GNU debuglink CRC方案来匹配主要和调试对象的检查。要禁用此类检查,您还需要指定 --allow-mismatched-debuginfo=yes

默认情况下,Valgrind构建系统将为valgrind-di-server目标平台构建,这几乎肯定不是您想要的。到目前为止,我们一直无法找到如何获取automake / autoconf来构建它的构建平台。如果要使用它,则必须使用顶部显示的命令手动重新编译它auxprogs/valgrind-di-server.c

--allow-mismatched-debuginfo=no|yes [no]

当从单独的debuginfo对象读取debuginfo时,Valgrind将默认使用GNU debuglink机制检查main和debuginfo对象是否匹配。这样保证它不会从过期的debuginfo对象中读取debuginfo,并且还可以确保Valgrind不会因为不匹配而崩溃。

此检查可以使用 --allow-mismatched-debuginfo=yes。当debuginfo和主对象没有以正确的方式拆分时,这可能很有用。尽管如此,使用它时要小心:它禁用所有一致性检查,并且当主要和debuginfo对象不匹配时,Valgrind被观察到崩溃。

--suppressions=<filename> [default: $PREFIX/lib/valgrind/default.supp]

指定一个额外的文件,从中读取要抑制的错误的描述。您可以使用多达100个额外的压缩文件。

--gen-suppressions=<yes|no|all> [default: no]

设置后yes,Valgrind将在每次显示错误后暂停,并打印行:

    ---- Print suppression ? --- [Return/N/n/Y/y/C/c] ----

Ret或,N Ret或 n Ret导致Valgrind继续执行而不打印此错误的抑制。

按下Y Ret或 y Ret导致Valgrind写入此错误的抑制。如果您不想再听到有关该错误的信息,可以将其剪切并粘贴到抑制文件中。

设置时all,Valgrind将会针对每个报告的错误打印一个抑制,而不会查询用户。

此选项对于C ++程序特别有用,因为它根据需要打印出具有破坏名称的抑制。

请注意,打印的压印尽可能具体。您可能希望通过添加通配符到函数名称以及使用帧级别的通配符来寻找类似的通配符。通配设备功能强大而灵活,并且经过仔细的编辑,您可能只需少量的压制即可抑制整个系列的相关错误。

有时两个不同的错误被同样的抑制所压制,在这种情况下,Valgrind将会多次输出抑制,但您只需要在压缩文件中有一个副本(但是不止一个则不会导致问题)。此外,抑制名称为 <insert a suppression name here>:该名称并不重要,它只用于-v打印所有使用的抑制记录的选项。

--input-fd=<number> [default: 0, stdin]

当使用 --gen-suppressions=yes时,Valgrind将停止,以便在发生每个错误时读取您的键盘输入。默认情况下,它从标准输入(stdin)读取,这对于关闭stdin的程序是有问题的。此选项允许您指定从中读取输入的备用文件描述符。

--dsymutil=no|yes [yes]

此选项仅在Mac OS X上运行Valgrind时才有用。

Mac OS X使用延迟调试信息(debuginfo)链接方案。当包含debuginfo的对象文件链接到一个.dylib或可执行文件中时,debuginfo不会被复制到最终文件中。相反,必须通过在可执行文件上运行dsymutil系统提供的实用程序来手动链接debuginfo .dylib。生成的组合debuginfo放在可执行文件旁边的目录中,或者.dylib扩展名.dSYM

使用--dsymutil=no,Valgrind将检测 .dSYM目录丢失或存在的情况,但不符合关联的可执行文件,或者.dylib很可能是因为它已过期。在这种情况下,Valgrind会打印一个警告消息,但不采取进一步行动。

随着--dsymutil=yes,Valgrind的意志,在这样的情况下,自动运行dsymutil必要使debuginfo软最新的。对于所有实际的目的,如果您始终使用--dsymutil=yes,则绝对不需要dsymutil手动运行或作为应用程序构建系统的一部分运行,因为Valgrind将根据需要运行它。

Valgrind的不会尝试运行dsymutil在任何可执行文件或库 /usr/, /bin/, /sbin/, /opt/, /sw/, /System/, /Library/或 /Applications/ 因为dsymutil总是会在这种情况下失败。它失败了,因为这种预安装的系统组件的debuginfo在任何地方都不可用,也因为它需要这些目录中的写入权限。

使用--dsymutil=yes时要小心,因为它会导致预先存在的.dSYM 目录被静默删除并重新创建。还要注意的 dsymutil是相当慢,有的时候太过分了。

--max-stackframe=<number> [default: 2000000]

堆栈帧的最大大小。如果堆栈指针移动超过此值,则Valgrind将假定程序正在切换到另一个堆栈。

如果您的程序具有大量堆栈分配的数组,则可能需要使用此选项。Valgrind跟踪程序的堆栈指针。如果它改变超过阈值,Valgrind假设您的程序正在切换到不同的堆栈,Memcheck的行为与堆栈指针变化小于阈值的行为不同。通常这个启发式工作原理很好。但是,如果您的程序在堆栈中分配大型结构,则该启发式将被愚弄,并且Memcheck将会报告大量的无效堆栈访问。此选项允许您将阈值更改为不同的值。

如果Valgrind的调试输出指示您这样做,您应该仅考虑使用此选项。在这种情况下,它会告诉您应该指定的新阈值。

通常,在堆栈上分配大型结构是一个坏主意,因为您可以轻松地耗尽堆栈空间,特别是在内存有限的系统上,或者希望支持大量线程,每个都有一个小堆栈,并且还因为错误由Memcheck执行的检查对堆分配数据比对堆栈分配的数据更有效。如果您必须使用此选项,您可能希望考虑将代码重写为在堆上分配而不是堆栈。

--main-stacksize=<number> [default: use current 'ulimit' value]

指定主线程堆栈的大小。

为了简化内存管理,Valgrind在启动时保留主线程堆栈的所有空间。这意味着它需要在启动时知道所需的堆栈大小。

默认情况下,Valgrind使用当前的“ulimit”值作为堆栈大小,或16 MB,以较低者为准。在许多情况下,这将提供8到16 MB的堆栈大小,对于大多数应用程序几乎不会溢出。

如果您需要更大的总堆栈大小,请使用--main-stacksize它来指定它。只需将其设置为您所需的高度,因为保留比您需要的更多空间(即,比您需要的数百兆字节)约束了Valgrind的内存分配器,并可能减少Valgrind可以使用的内存总量。这对32位机器来说真的很重要。

在Linux上,您可以要求一个大小为2GB的堆栈。如果无法分配堆栈,Valgrind将会停止诊断消息。

--main-stacksize只影响程序初始线程的堆栈大小。它对线程栈的大小没有任何影响,因为Valgrind不会分配它们。

您可能需要同时使用--main-stacksize 和--max-stackframe在一起。重要的是要理解,--main-stacksize设置最大堆栈总数,同时--max-stackframe指定任何一个堆栈帧的最大大小。您将必须--main-stacksize为自己找出价值(通常,如果您的应用程序发生故障)。但是--max-stackframe,如果需要,Valgrind会告诉你需要的尺寸。

如在描述中进一步讨论的--max-stackframe,对于大堆栈的要求是潜在可移植性问题的迹象。最好建议将所有大数据放在堆分配的内存中。

--max-threads=<number> [default: 500]

默认情况下,Valgrind最多可处理500个线程。偶尔,这个数字太小了。使用此选项提供不同的限制。例如 --max-threads=3000

2.6.4。malloc相关选项

对于使用自己的版本 malloc(例如Memcheck,Massif,Helgrind,DRD)的工具,适用以下选项。

--alignment=<number> [default: 8 or 16, depending on the platform]

默认情况下Valgrind的的malloc, realloc等等,返回块,其起始地址为8字节对齐或16字节对齐(数值取决于平台和相匹配的平台默认)。此选项允许您指定不同的对齐方式。提供的值必须大于或等于默认值小于或等于4096,并且必须是2的幂。

--redzone-size=<number> [default: depends on the tool]

Valgrind的malloc, realloc,等等,在运行程序分配的每个堆块之前和之后添加填充块。这种填充块称为redzones。redzone大小的默认值取决于工具。例如,Memcheck在客户端分配的每个块之前和之后增加并保护至少16个字节。这允许它检测最多16个字节的块欠载或超限。

增加红色区域大小可以检测更大距离的超限,但会增加Valgrind使用的内存量。减少红区大小将减少Valgrind所需的内存,同时也降低了检测超出/欠载的机会,因此不推荐使用。

2.6.5。不常见的选项

这些选项适用于所有工具,因为它们会影响Valgrind核心的某些模糊工作。大多数人不需要使用它们。

--smc-check=<none|stack|all|all-non-file> [default: all-non-file for x86/amd64/s390x, stack for other archs]

此选项控制Valgrind对自修改代码的检测。如果没有进行任何检查,当程序执行一些代码时,用新的代码覆盖它,并执行新的代码,Valgrind将继续执行旧代码的翻译。这可能会导致错误的行为和/或崩溃。

对于“现代”架构 - 任何不是x86,amd64或s390x - 的默认设置stack。这是因为正确的程序必须采取明确的行动来重新建立代码修改后的DI缓存一致性。Valgrind遵守并尊重此类行为,结果是自我修改代码以零额外成本进行透明处理。

对于x86,amd64和s390x,程序不需要通知硬件所需的DI一致性同步。因此,默认是all-non-file覆盖将代码生成到匿名(非文件备份)mmap'd区域的正常情况。

四种可用设置的含义如下。无检测(none)检测堆栈上的自修改代码(由GCC用于实现嵌套函数)(stack),检测每个地方的自修改代码all,并检测除了文件支持的映射之外的其他修改代码(all-non-file)。

跑步all会缓慢的下降。运行时none很少加快速度,因为很少的代码在大多数程序中动态生成。该 VALGRIND_DISCARD_TRANSLATIONS客户端请求是一种替代--smc-check=all 和--smc-check=all-non-file 需要更多的程序员的努力,但允许Valgrind的更快地运行您的程序,告诉它准确翻译时需要重新制作。

--smc-check=all-non-file提供了更便宜但更有限的版本--smc-check=all。它会将检查添加到不是源于文件支持的内存映射的任何翻译。生成代码的典型应用程序(例如Web浏览器中的JIT)将生成代码到匿名的mmaped区域,而浏览器的“固定”代码总是存在于文件支持的映射中。 --smc-check=all-non-file 利用这一观察,限制检查可能是JIT生成的代码的开销。

--read-inline-info=<yes|no> [default: see below]

启用后,Valgrind将从DWARF3调试信息中读取有关内联函数调用的信息。这减慢了Valgrind启动并使其使用更多的内存(通常为每个内联代码段,6个字和空格用于函数名称),但它会导致更多描述性的堆栈跟踪。对于3.10.0版本,默认情况下仅对Linux,Android和Solaris目标以及仅用于Memcheck,Helgrind和DRD的工具启用此功能。以下是一些堆栈跟踪的示例 --read-inline-info=no

== 15380 ==有条件的跳转或移动取决于未初始化的值== 15380 == at 0x80484EA:main(inlinfo.c:6)== == 15380 == 15380 ==有条件跳转或移动取决于未初始化的值== 15380 == at 0x8048550:fun_noninline(inlinfo.c:6)== 15380 == by 0x804850E:main(inlinfo.c:34)== == 15380 == 15380 ==有条件跳转或移动取决于未初始化的值== 15380 == at 0x8048520:main(inlinfo.c:6)

以下是与以下相同的错误 --read-inline-info=yes

== 15377 ==有条件跳转或移动取决于未初始化的值== 15377 == at 0x80484EA:fun_d(inlinfo.c:6)== 15377 == by 0x80484EA:fun_c(inlinfo.c:14)== 15377 == by 0x80484EA:fun_b(inlinfo.c:20)== 15377 == by 0x80484EA:fun_a(inlinfo.c:26)== 15377 == by 0x80484EA:main(inlinfo.c:33)== == 15377 == 15377 ==有条件跳转或移动取决于未初始化的值== 15377 == at 0x8048550:fun_d(inlinfo.c:6)== 15377 == by 0x8048550:fun_noninline(inlinfo.c:41)== 15377 == by 0x804850E:main(inlinfo.c:34)== == 15377 == 15377 ==有条件跳转或移动取决于未初始化的值== 15377 == at 0x8048520:fun_d(inlinfo.c:6)== 15377 == by 0x8048520:main(inlinfo.c:35)
--read-var-info=<yes|no> [default: no]

启用后,Valgrind将从DWARF3调试信息中读取有关变量类型和位置的信息。这显着减缓了Valgrind的启动,并使其占用了更多的内存,但是对于可以利用它的工具(Memcheck,Helgrind,DRD),它可能会导致更精确的错误消息。例如,以下是Memcheck发出的一些标准错误:

== 15363 ==客户端检查请求期间发现的未初始化字节== 15363 == at 0x80484A9:croak(varinfo1.c:28)== 15363 == 0x8048544:main(varinfo1.c:55)== 15363 ==地址0x80497f7是数据符号“global_i2”内的7个字节== == 15363 == 15363 ==客户端检查请求期间发现的未初始化字节== 15363 == at 0x80484A9:croak(varinfo1.c:28)== 15363 == by 0x8048550:main(varinfo1.c:56)== 15363 ==地址0xbea0d0cc在线程1的堆栈上== 15363 ==在框架#1中,由主(varinfo1.c:45)创建

以下是与以下相同的错误 --read-var-info=yes

== 15370 ==客户端检查请求期间发现的未初始化字节== 15370 == at 0x80484A9:croak(varinfo1.c:28)== 15370 == by 0x8048544:main(varinfo1.c:55)== 15370 ==位置0x80497f7是global_i2 [7]中的0个字节,== 15370 ==在varinfo1.c中声明的全局变量:41== == 15370 == 15370 ==客户端检查请求期间发现的未初始化字节== 15370 == at 0x80484A9:croak(varinfo1.c:28)== 15370 == 0x8048550:main(varinfo1.c:56)== 15370 ==位置0xbeb4a0cc是本地var“local”内的0个字节== 15370 ==在线程1的第1帧中的varinfo1.c:46声明
--vgdb-poll=<number> [default: 5000]

作为其主循环的一部分,Valgrind调度程序将轮询以检查某些活动(例如外部命令或gdb的某些输入)是否必须由gdbserver处理。在运行给定数量的基本块(或略大于给定数量的基本块)之后,将进行此活动轮询。这个投票相当便宜,所以默认值设置得比较低。如果vgdb无法使用ptrace系统调用中断Valgrind,如果系统调用中的所有线程(大部分时间)被阻止,您可能会进一步减少此值。

--vgdb-shadow-registers=no|yes [default: no]

激活时,gdbserver会将Valgrind影子寄存器公开到GDB。这样,可以使用GDB检查或更改Valgrind影子寄存器的值。隐藏影子寄存器仅适用于GDB 7.1或更高版本。

--vgdb-prefix=<prefix> [default: /tmp/vgdb-pipe]

要与gdb / vgdb通信,Valgrind gdbserver会创建3个文件(2个命名的FIFO和一个mmap共享内存文件)。prefix选项控制创建这些文件的目录和前缀。

--run-libc-freeres=<yes|no> [default: yes]

此选项仅在Linux上运行Valgrind时才有用。

libc.so所有程序使用的GNU C库()可能会为自己的用途分配内存。通常,当程序结束时,它不会释放内存,因为Linux内核在进程退出时会回收所有进程资源,所以它只会减慢速度。

glibc作者意识到,当出口检查完成时,这种行为会导致漏洞检查器(如Valgrind)错误地报告glibc中的泄漏。为了避免这种情况,他们提供了一个__libc_freeres 专门用于使glibc释放其分配的所有内存的例程。因此,Memcheck尝试__libc_freeres在退出时运行 。

不幸的是,在一些很旧的glibc版本中, __libc_freeres足够错误的引起分段错误。这在Red Hat 7.1中尤其明显。所以这个选项是为了禁止运行而提供的 __libc_freeres。如果您的程序似乎在Valgrind上运行良好,但是在出口处出现segfaults,您可能会发现 --run-libc-freeres=no修复尽管可能会错误地报告空间漏洞 libc.so

--run-cxx-freeres=<yes|no> [default: yes]

此选项仅在运行Linux或Solaris C ++程序上的Valgrind时才有用。

libstdc++.so由g ++编译的所有C ++程序使用的GNU标准C ++库()可能会为自己的用途分配内存。通常,当程序结束时,它不会释放内存,因为内核在进程退出时会回收所有进程资源,所以它只会减慢速度。

gcc作者意识到,当在出口处进行泄漏检查时,此行为会导致漏洞检查器(如Valgrind)在libstdc ++中错误地报告泄漏。为了避免这种情况,他们提供了一个__gnu_cxx::__freeres 专门用于使libstdc ++释放其已分配的所有内存的例程。因此,Memcheck尝试__gnu_cxx::__freeres在退出时运行 。

为了灵活性和不可预见的问题 __gnu_cxx::__freeres,选择 --run-cxx-freeres=no存在,尽管可能错误地报告空间泄漏 libstdc++.so

--sim-hints=hint1,hint2,...

通过Valgrind的其他提示,以非标准或危险的方式稍微修改模拟行为,可能有助于模拟奇怪的功能。默认情况下不启用任何提示。谨慎使用!目前已知的提示有:

  • lax-ioctls: 对于ioctl处理非常松懈; 唯一的假设是大小是正确的。写入时不需要初始化完整的缓冲区。没有这个,使用一些设备驱动程序与大量的奇怪的ioctl命令变得非常麻烦。

  • fuse-compatible: 对可能在FUSE文件系统中阻塞的某些系统调用启用特殊处理。在使用一个线程来管理FUSE文件系统的多线程程序上运行Valgrind并且另一个线程访问该文件系统时,这可能是必需的。

  • enable-outer: 当运行程序本身是Valgrind时,启用了一些特殊的魔法。

  • no-inner-prefix: 禁止>在外部Valgrind运行的内部Valgrind中,在每个stdout或stderr输出行前面打印前缀。当在外部/内部设置中运行Valgrind回归测试时,这是非常有用的。请注意,前缀>将始终打印在内部调试日志记录行的前面。

  • no-nptl-pthread-stackcache: 这个提示仅在Linux上运行Valgrind时才有用。

    libpthread.sopthread程序使用的GNU glibc pthread库()维护一个pthread栈缓存。当pthread终止时,用于pthread栈的内存和一些线程本地存储相关数据结构并不总是直接释放。这个内存被保存在高速缓存中(达到一定的大小),如果启动了一个新的线程,那么这个内存被重新使用。

    这个缓存导致helgrind工具在这个缓存的内存上报告一些错误的正面竞争条件错误,因为helgrind不了解内部glibc缓存同步原语。所以,当使用helgrind时,禁用缓存有助于避免错误的正面竞争条件,特别是当使用线程本地存储变量(例如使用 __thread限定符的变量)时。

    当使用memcheck工具时,禁用高速缓存可确保glibc处理__thread变量所使用的内存在线程终止时直接释放。

    注意:Valgrind使用glibc堆栈缓存实现的一些内部知识以及检查pthread库的调试信息来禁用缓存。因此,这种技术有些脆弱,并且可能对所有glibc版本都无效。这已经在各种平台上通过各种glibc版本(例如2.11,2.16,2.18)成功测试。

  • lax-doors: (仅限Solaris)对于无法识别的门文件描述符,关于门系统调用处理非常松懈。不需要在写入时初始化完整的缓冲区。没有这一点,使用具有完全专有语义的libdoor(3LIB)功能的程序可能会报告大量的误报。

--fair-sched=<no|yes|try> [default: no]

--fair-sched选项控制Valgrind使用的锁定机制来串行线程执行。锁定机制控制线程的安排方式,不同的设置在公平性和性能之间进行不同的权衡。有关Valgrind线程序列化方案及其对性能和线程调度的影响的更多详细信息,请参阅 计划和多线程性能。

  • 该值--fair-sched=yes 激活公平的调度程序。简而言之,如果多个线程准备运行,线程将以轮循方式进行安排。该机制在所有平台或Linux版本上都不可用。如果不可用,则使用--fair-sched=yes会导致Valgrind终止并出现错误。

    如果您在Valgrind上运行交互式多线程程序(例如Web浏览器),则可能会发现此设置会提高整体响应能力。

  • 该值--fair-sched=try 激活公平调度(如果在平台上可用)。否则会自动回退--fair-sched=no

  • 该值--fair-sched=no激活一个不能保证线程准备运行的线程之间的公平性,但通常给出最高性能的调度器。

--kernel-variant=variant1,variant2,...

处理来自该平台的默认内核的较小变体的系统调用和ioctl。这对于在黑客内核上运行或者支持非标准ioctl的内核模块非常有用。谨慎使用。如果你不明白这个选项是什么,那么你几乎肯定不需要它。目前已知的变体是:

  • bproc:支持sys_brocx86上的 系统调用。这是用于在BProc上运行的,它是标准Linux的一小部分变体,有时用于构建集群。

  • android-no-hw-tls:ARM版Android模拟器的一些版本不提供硬件TLS(线程本地状态)寄存器,Valgrind在启动时崩溃。使用此变体选择TLS的软件支持。

  • android-gpu-sgx5xx:使用它来支持在Android设备上处理PowerVR SGX 5XX系列GPU的专有ioctl。未选择此项不会导致稳定性问题,但可能会导致Memcheck在程序执行GPU特定的ioctl之后报告错误的错误。

  • android-gpu-adreno3xx:同样地,使用它来支持在Android设备上处理高通Adreno 3XX系列GPU的专有ioctl。

--merge-recursive-frames=<number> [default: 0]

一些递归算法,例如平衡二叉树实现,创建许多不同的堆栈跟踪,每个包含循环的调用。一个周期被定义为由零个或多个其他程序计数器值分隔的两个相同的程序计数器值。然后,Valgrind可能会使用大量内存来存储所有这些堆栈跟踪。考虑到这种堆栈跟踪包含重复的不感兴趣的递归调用而不是更有趣的信息,例如已经发起递归调用的函数,这是一个很差的内存使用。

该选项--merge-recursive-frames=<number> 指示Valgrind检测并合并大小最多为<number> 帧的递归调用循环。当检测到这样的循环时,Valgrind将堆栈跟踪中的循环记录为唯一的程序计数器。

值0(默认值)不会导致递归调用合并。值为1将导致简单递归算法(例如,阶乘实现)的堆栈跟踪被折叠。通常需要2的值来折叠由递归算法(如二叉树,快速排序等)等产生的堆栈跟踪。更复杂的递归算法可能需要更高的值。

注意:通过分析程序计数器值来检测递归调用。通过查看函数名称来检测它们。

--num-transtab-sectors=<number> [default: 6 for Android platforms, 16 for all others]

Valgrind以小碎片(基本块)翻译和调整程序的机器代码。翻译存储在翻译高速缓存中,该高速缓存分为多个部分(扇区)。如果缓存已满,则包含最旧的翻译的扇区将被清空并重新使用。如果需要这些旧的翻译,Valgrind必须重新翻译和重新设备相应的机器代码,这是昂贵的。如果程序的“执行指令”工作集很大,则通过减少所需的翻译次数,增加扇区数量可以提高性能。部门按需分配。一旦分配,一个部门永远不会被释放,并占据相当大的空间,--avg-transtab-entry-size 取决于工具和值(Memcheck每个扇区大约40 MB)。使用选项--stats=yes来获取有关部门使用的内存以及部门分配和回收的准确信息。

--avg-transtab-entry-size=<number> [default: 0, meaning use tool provided default]

翻译基本块的平均大小。该平均尺寸用于确定扇区的大小。每个工具提供要使用的默认值。如果此默认值太小,则翻译扇区将变得过快。如果此默认值太大,则翻译部分内存的很大一部分将不被使用。请注意,基本块平移的平均大小取决于工具,并且可能取决于工具选项。例如,memcheck选项--track-origins=yes 增加了基本块转换的大小。使用--avg-transtab-entry-size调整扇区的大小,既可以获取内存或避免过多的重译。

--aspace-minaddr=<address> [default: depends on the platform]

为了避免与某些系统库的潜在冲突,Valgrind不会使用下面的地址空间--aspace-minaddr值,保留它,以防库中特别要求该区域内存。所以根据平台,Valgrind猜测到一些“悲观”的价值。在Linux上,默认情况下,Valgrind避免使用第一个64MB,即使这个完整区域通常没有冲突。您可以使用该选项--aspace-minaddr使您的内存饥饿应用程序从更多的这种较低的内存中受益。另一方面,如果遇到冲突,增加aspace-minaddr值可能会解决它。冲突通常会在地址空间较低的范围内出现mmap故障。address提供的页面必须对齐,并且必须等于或大于0x1000(4KB)。要在平台上找到默认值,请执行以下操作 valgrind -d -d date 2>&1 | grep -i minaddr。已知低于0x10000(64KB)的值会在某些分布上产生问题。

--valgrind-stacksize=<number> [default: 1MB]

对于每个线程,Valgrind需要自己的“私有”堆栈。这些堆栈的默认大小在很大程度上是大小尺寸的,因此在大多数情况下应该是足够的。如果尺寸太小,Valgrind将会发生故障。在分裂之前,Valgrind可能在接近极限时发出警告。

--valgrind-stacksize如果产生这样的(不太可能)警告,或者由于分段违规导致Valgrind死亡, 请使用该选项。在分离大量C ++符号时已经看到这种分段违例。

如果您的应用程序使用许多线程并需要大量内存,您可以通过使用该选项减小这些Valgrind堆栈的大小来获得一些内存--valgrind-stacksize

--show-emwarns=<yes|no> [default: no]

启用后,Valgrind在某些​​情况下会发出关于CPU仿真的警告。这些通常不是有趣的。

--require-text-symbol=:sonamepatt:fnnamepatt

当soname匹配的共享对象sonamepatt加载到进程中时,检查其导出的所有文本符号。如果没有匹配fnnamepatt,打印错误信息并放弃运行。这使得有可能确保运行不会继续,除非给定的共享对象包含特定的函数名称。

双方sonamepatt并 fnnamepatt可以用通常的写入 ?*通配符。例如:":*libc.so*:foo?bar"。您可以使用冒号以外的字符分隔两个模式。第一个字符和分隔符相同只是重要的。例如,上面的例子也可以写成"Q*libc.so*Qfoo?bar" --require-text-symbol允许多个标志,在这种情况下,将检查加载到进程中的共享对象。

其目的是支持可靠使用已标记的库。例如,假设我们有一个GCC的版本, libgomp.so其中已经标注了注释以支持Helgrind。将错误的,未注释的加载到应用程序中是太容易和混乱了 libgomp.so。所以这个想法是:例如在标记的库中添加一个文本符号annotated_for_helgrind_3_6,然后给出标志 --require-text-symbol=:*libgomp*so*:annotated_for_helgrind_3_6 ,以便libgomp.so加载时,Valgrind扫描其符号表,如果符号不存在,则运行中止,而不是与未标记的图书馆保持沉默。请注意,您应该将整个标记放在引号中,以阻止扩展* 和?通配符的扩展。

--soname-synonyms=syn1=pattern1,syn2=pattern2,...

加载共享库时,Valgrind会检查库中必须更换或包装的函数。例如,Memcheck用自己的版本替换一些字符串和内存函数(strchr,strlen,strcpy,memchr,memcpy,memmove等)。这样的替换通常仅在共享库中进行,其共享库的soname匹配预定义的soname模式(例如 libc.so*在linux上)。默认情况下,除了分配函数(malloc,free,calloc,memalign,realloc,operator new,operator delete等)之外,静态链接二进制或替代库不进行替换。默认情况下,这种分配函数被截取任何共享库或可执行文件,如果它们被导出为全局符号。 这意味着如果找到替换分配库(如tcmalloc),则默认情况下也会拦截其函数。--soname-synonyms在某些情况下,替换可以指定一个额外的同义词模式,从而给予更换的灵活性。或防止截取所有公共分配符号。

目前,这种灵活性只允许使用同义词的malloc相关功能somalloc。这个同义词可用于执行malloc相关功能的标准替换的所有工具(例如memcheck,massif,drd,helgrind,exp-dhat,exp-sgcheck)。

  • 替代的malloc库:使用soname mymalloclib.so(而不是任何其他)替换特定备用库中的malloc相关函数,给出选项--soname-synonyms=somalloc=mymalloclib.so。可以使用一个模式来匹配多个库的声名。例如,--soname-synonyms=somalloc=*tcmalloc* 将匹配tcmalloc库(native,debug,profiled,... tcmalloc变体)的所有变体的soname。

    注意:可以使用readelf实用程序检索elf共享库的soname。

  • 静态链接库中的替换是通过使用NONE模式来完成的。例如,如果您链​​接libtcmalloc.a,并且只想拦截可执行文件(和标准库)本身的malloc相关函数,而不是任何其他共享库,则可以给出该选项--soname-synonyms=somalloc=NONE。请注意,NONE模式将匹配主可执行文件和任何没有soname的共享库。

  • 要运行用于Linux的“默认”Firefox版本,其中JEMalloc链接到主可执行文件,请使用--soname-synonyms=somalloc=NONE

  • 为了仅拦截默认系统库中的分配符号,但不能在任何其他共享库或定义public malloc或operator new相关函数的可执行文件中使用不存在的库名称--soname-synonyms=somalloc=nouserintercepts (如果nouserintercepts可以是任何不存在的库名称)。

  • 搜索动态(运行时)链接器的共享库被排除在搜索全局公共符号之外,如malloc相关函数(由somalloc同义词标识)。

2.6.6。调试选项

还有一些用于调试Valgrind本身的选项。你不应该在正常的事情中使用它们。如果您希望看到列表,请使用该 --help-debug选项。

如果你想调试你的程序,而不是调试Valgrind本身,那么你应该使用选项 --vgdb=yes--vgdb=full

2.6.7。设置默认选项

请注意,Valgrind还从三个地方读取选项:

  1. 文件 ~/.valgrindrc

  2. 环境变量 $VALGRIND_OPTS

  3. 文件 ./.valgrindrc

这些在命令行选项之前以给定的顺序进行处理。稍后处理的选项将覆盖之前处理的选项; 例如,选项 ./.valgrindrc将优先于那些 ~/.valgrindrc

请注意,如果./.valgrindrc 文件被标记为当前用户可以写入或不拥有,则该文件将被忽略。这是因为 ./.valgrindrc可能包含潜在有害的选项,或者本地攻击者可以使用该选项来执行用户帐户下的代码。

任何放入工具的选项 $VALGRIND_OPTS或 .valgrindrc文件都应该加上工具名称和冒号。例如,如果要让Memcheck始终进行泄漏检查,可以将以下条目放入~/.valgrindrc

--memcheck:泄漏检查= YES

如果运行Memcheck以外的任何工具,这将被忽略。没有这个memcheck: 部分,如果选择其他不了解的工具,这将导致问题 --leak-check=yes

2.7。支持线程

线程程序得到完全支持。

关于线程程序的主要内容是,您的程序将使用本机线程库,但是Valgrind可以串行化执行,以便一次只运行一个(内核)线程。这种方法避免了实施Valgrind的真正多线程版本的可怕实现问题,但这意味着即使有多处理器或多核机器,线程应用程序也不会同时使用多个CPU。

Valgrind没有安排线程本身。它只是确保只有一个线程一次运行,使用简单的锁定方案。实际的线程调度仍然受OS内核的控制。这是什么意思,但是,您的程序将看到非常不同的调度,当运行在Valgrind比正常运行时。这是因为Valgrind串行化线程,并且因为代码运行速度比正常慢。

如果您有某种并发,严重的竞争,锁定或类似的错误,那么调度的差异可能会导致程序的行为不同。在这种情况下,您可以考虑使用Helgrind和/或DRD工具来跟踪它们。

在Linux上,Valgrind还支持直接使用 clone系统调用, futex等等。 clone支持任何一个共享(一个线程)或者没有共享(fork-like); 部分分享将失败。

2.7.1。调度和多线程性能

只有当线程保持上述锁时,线程才执行代码。执行一些指令后,正在运行的线程将释放锁。准备运行的所有线程将会竞争获取锁。

--fair-sched选项控制用于序列化线程执行的锁定机制。

默认的基于管道的锁定机制(--fair-sched=no)可在所有平台上使用。基于管道的锁定不能保证线程之间的公平性:刚刚释放锁的线程很可能立即重新获取,即使其他线程已准备好运行。当使用基于管道的锁定时,相同多线程应用程序的不同运行可能给出非常不同的线程调度。

在一些平台上可以使用基于futexes的替代锁定机制。如果有的话,它被激活--fair-sched=yes或 --fair-sched=try。基于Futex的锁定确保线程之间的公平性(循环调度):如果多个线程准备运行,则锁将被提供给首先请求锁定的线程。请注意,系统调用中阻塞的线程(例如,阻塞读取系统调用)尚未请求锁定:只有系统调用完成后,该线程才会请求该锁。

基于futex的锁定的公平性为多线程应用程序的不同执行提供了线程调度的更好的重现性。使用Helgrind或DRD时,这种更好的再现性特别有用。

Valgrind使用线程序列化意味着一次只能运行一个线程。在多处理器/多核系统上,运行的线程由OS内核调度程序分配给其中一个CPU。当一个线程获取锁定时,有时线程将被分配给刚刚释放锁的线程的同一个CPU。有时,线程将被分配给另一个CPU。当使用基于管道的锁定时,刚刚获取锁的线程通常将被调度在与刚释放锁的线程相同的CPU上。使用基于futex的机制,刚获取锁的线程将更频繁地在另一个CPU上调度。

由OS内核调度程序进行的Valgrind的线程序列化和CPU分配可以与许多现代CPU上可用的CPU频率扩展进行严重的交互。为了降低功耗,如果最近没有使用CPU /内核,则CPU或内核的频率会自动降低。如果操作系统内核经常将刚刚获得锁定的线程分配给另一个CPU /内核,那么这个CPU /核心很可能当前处于低频率。这个CPU的频率会在一段时间后增加。但是,在这段时间内,(只)运行的线程将以低频运行。一旦这个线程运行了一段时间,它将释放锁。另一个线程将获取此锁,并可能会在另一个CPU的时钟频率降低的同时进行调度。

基于futex的锁定会导致线程更频繁地更改CPU /内核。因此,如果CPU频率缩放被激活,则基于futex的锁定可能会显着降低在Valgrind下运行的多线程应用程序的性能。与在CPU频率缩放已停用的机器上运行相比,已经观察到高达50%的性能损失。基于管道的锁定锁定方案也与CPU频率缩放相关,性能损失在10..20%的范围内。

为了避免这种性能下降,您应该向内核指出所有CPU /内核应始终以最大时钟速度运行。根据您的Linux发行版,可以使用图形界面或使用诸如cpufreq-selector或 之类的命令行来控制CPU频率缩放 cpufreq-set

避免这些问题的另一种方法是告诉操作系统调度程序使用taskset命令将Valgrind进程与特定(固定)CPU绑定 。只要程序的任何线程都有工作,这样就可以确保所选择的CPU不会低于其最大频率设置。

2.8。处理信号

Valgrind有一个相当完整的信号实现。它应该能够应付符合POSIX标准的信号使用。

如果您以聪明的方式使用信号(例如,捕获SIGSEGV,修改页面状态并重新启动指令),则可能依赖于精确的异常。在这种情况下,您将需要使用--vex-iropt-register-updates=allregs-at-mem-access 或--vex-iropt-register-updates=allregs-at-each-insn

如果您的程序由于致命的核心转储信号而死亡,Valgrind将生成自己的vgcore.NNNNN包含程序状态的核心文件()。您可以将此核心文件用于GDB或类似的验证后调试。(注意:如果您的核心转储大小限制为0,则不会生成内核)。在编写核心转储时,不包括所有浮点寄存器信息。

在不太可能的情况下,Valgrind本身崩溃,操作系统将以通常的方式创建一个核心转储。

2.9。建筑和安装Valgrind

我们使用标准的Unix ./configure, makemake install机制。一旦完成, make install您可能需要运行回归测试make regtest

除了通常情况 --prefix=/path/to/install/tree,还有三个选项影响了Valgrind的构建方式:

  • --enable-inner

    这使得Valgrind成为一些特殊的魔法黑客,可以在标准版本的Valgrind(开发者称之为“自主托管”)上运行它。通常您不应使用此选项,因为各种安全检查被禁用。

  • --enable-only64bit

    --enable-only32bit

    在64位平台(amd64-linux,ppc64-linux,amd64-darwin)上,默认情况下,Valgrind内置了32位和64位可执行文件的运行方式。有时这个聪明才是一个问题,原因有很多。在这种情况下,这两个选项允许单目标构建。如果同时发出这两个,配置脚本将会抱怨。请注意,它们仅在32位平台(x86-linux,ppc32-linux,arm-linux,x86-darwin)上被忽略。

configure脚本测试目前由当前指示的X服务器的版本 $DISPLAY。这是一个已知的错误。目的是检测当前X客户端库的版本,以便可以为它们选择正确的抑制,而是测试检查服务器版本。这只是错误的。

如果您正在构建一个二进制包Valgrind进行发行,请阅读README_PACKAGERS Readme Packagers。它包含一些重要信息。

除此之外,这里没有什么兴奋。如果您有建立问题,请告诉我们。

2.10。如果你有问题

请联系我们http://www.valgrind.org/。

请参阅限制为Valgrind的的已知的限制,而对于已知不进行这项工作的程序列表。

系统的所有部分大量使用断言和内部自检。它们被永久启用,我们没有计划禁用它们。如果其中一个休息,请邮寄给我们!

如果您发现断言失败m_mallocfree.c,这可能是因为您的程序注销堆块的末尾,或在其开始之前,从而破坏了堆元数据。Valgrind希望在这样做之前就会发出这样的消息。

阅读Valgrind常见问题解答,了解有关常见问题,崩溃等的更多建议。

2.11。限制

以下列出的限制似乎很长。然而,大多数程序实际上正常工作。

Valgrind将在受支持的平台上运行程序,但受到以下限制:

  • 在Linux上,Valgrind在启动时使用RLIMIT_DATA rlim_cur确定“brk段”的大小,最小为1 MB,最大为8 MB。每次程序尝试将brk段扩展到启动时确定的大小时,Valgrind将输出一条消息。大多数程序可以正常运行这个限制,通常是通过切换到使用mmap来获得更多的内存。如果您的程序真的需要一个大的brk段,则必须更改8 MB的硬编码限制并重新编译Valgrind。

  • 在x86和amd64上,不支持3DNow!说明。如果翻译者遇到这种情况,Valgrind将在执行指令时产生一个SIGILL。除此之外,在x86和amd64上,基本上所有的指令都支持,包括64位模式下的AVX和AES,以及32位模式下的SSSE3。实际上,32位模式支持在32位目标上在MacOSX 10.6上运行程序所需的最小SSE4指令。

  • 在ppc32和ppc64上,几乎支持所有的整数,浮点和Altivec指令。具体来说:PowerPC强制的整数和FP insns,“通用可选”组(fsqrt,fsqrts,stfiwx),“图形可选”组(fre,fres,frsqrte,frsqrtes)和Altivec(也称为作为VMX)SIMD指令集,支持。此外,支持Power ISA 2.05规范中的说明,如POWER6 CPU中所示。

  • 在ARM上,ARM和Thumb模式基本上支持整个ARMv7-A指令集。ThumbEE和Jazelle不受支持。NEON,VFPv3和ARMv6媒体支持相当完整。

  • 如果您的程序执行自己的内存管理,而不是使用malloc / new / free / delete,它仍然可以工作,但是Memcheck的错误检查将不会那么有效。如果您使用“客户端请求”描述程序的内存管理方案(请参阅 客户端请求机制),Memcheck可以做得更好。然而,使用malloc / new和free / delete仍然是最好的方法。

  • Valgrind的信号模拟并不像现在那样强大。提供基本的POSIX兼容的sigaction和sigprocmask功能,但可以想象,如果您使用信号做奇怪的事情,事情可能会非常糟糕。解决方法:不要。在任何情况下,执行非POSIX信号技巧的程序在本质上是不可移植的,所以如果可能的话应该避免。

  • 机器指令和系统调用已经按需实现。所以尽管不太可能,但有可能这样一个消息可能会导致程序崩溃。如果发生这种情况,请报告打印出的所有详细信息,以便我们尝试并实施缺少的功能。

  • 在Valgrind的Memcheck工具下运行时,程序的内存消耗量会大大增加。这是由于幕后维护的管理信息大量。另一个原因是Valgrind动态翻译原始的可执行文件。翻译的测试代码是原始代码的12-18倍,因此您可以在运行(例如)网络浏览器时轻松获得150+ MB的翻译。

  • Valgrind可以处理动态生成的代码。如果您在旧代码(即相同的内存地址)上重新生成代码,则如果代码位于堆栈上,Valgrind将会实现代码已更改,并正常工作。这是必要的,以处理GCC用于实现嵌套函数的蹦床。如果您重新生成堆栈以外的其他地方的代码,并且运行在32位或64位x86 CPU上,则需要使用该--smc-check=all选项,而Valgrind的运行速度要比正常运行的要慢。或者您可以添加客户端请求,告诉Valgrind,当您的程序覆盖代码。

    在其他平台(ARM,PowerPC)上,Valgrind观察并尊重程序不得不发出的缓存无效提示,以通知新代码,因此自修改代码支持应该可以自动工作,而不需要--smc-check=all

  • Valgrind在执行相对于IEEE754的x86 / AMD64浮点数方面有以下限制。

    精度:不支持80位算术。在内部,Valgrind代表64位中的所有这样的“长双数”数字,因此结果可能会有一些差异。这是否至关重要还有待观察。请注意,x86 / amd64 fldt / fstpt指令(读/写80位数字)被正确地模拟,使用/从64位转换,所以如果有人想看到,80位数字的内存映像看起来是正确的。

    许多FP回归测试观察到的印象是精度差异不显着。一般来说,如果一个程序依赖于80位精度,那么移植到只能支持64位FP精度的非x86 / amd64平台上可能会有困难。即使在x86 / amd64上,程序可能得到不同的结果,这取决于是否编译为使用SSE2指令(仅64位)或x87指令(80位)。最终的效果是使FP程序的行为就像在具有64位IEEE浮点数的机器上运行一样,例如PowerPC。在amd64上,FP算法默认在SSE2上完成,所以从FP的角度看,amd64看起来更像是PowerPC而不是x86,与x86相比,显着的精度差异要小得多。

    四舍五入:Valgrind确实遵循以下转换的4个IEEE强制舍入模式(最接近+无穷大至-infinity,为零):float到整数,整数浮动,有可能丢失精度;浮点到浮动四舍五入。对于所有其他FP操作,仅支持IEEE默认模式(round to nearest)。

    FP代码中的数字异常:IEEE754定义了可能发生的五种类型的数字异常:无效操作(负数的sqrt等),除以零,溢出,下溢,不精确(精度损失)。

    对于每个例外,IEEE754定义了两个行动方案:(1)可以调用用户定义的异常处理程序,或者(2)定义了一个默认动作,“修复”并允许计算在没有抛出异常

    目前,Valgrind仅支持默认的fixup操作。同样,对异常支持的重要性的反馈将不胜感激。

    当Valgrind检测到程序试图超出任何这些限制(设置异常处理程序,舍入模式或精度控制)时,它可以打印一个消息,提供发生这种情况的回溯,并继续执行。此行为曾经是默认的,但是消息很烦人,因此默认情况下显示它们已被禁用。用--show-emwarns=yes来看他们。

    上述限制精确地定义了IEEE754“默认”行为:对所有异常进行默认修复,从最近到最近的操作和64位精度。

  • 相对于IEEE754,Valgrind在执行x86 / AMD64 SSE2 FP运算时有以下限制。

    本质上是一样的:没有例外,有限的遵守舍入模式。此外,SSE2具有控制位,使得它将非正规化数字视为零(DAZ)和相关动作,将数值清零(FTZ)。这两个都导致SSE2算法的精度要低于IEEE要求。Valgrind检测,忽略并可以警告,尝试启用任一模式。

  • 相对于IEEE754,Valgrind在执行ARM VFPv3运算时有以下限制。

    本质上是一样的:没有例外,有限的遵守舍入模式。此外,将VFP单元切换到矢量模式将导致Valgrind中止程序 - 它无法在合理的性能级别模拟VFP的矢量使用。由于VFP指令的非标量使用在任何情况下都不推荐使用,这并不重要。

  • 相对于IEEE754,Valgrind在执行PPC32和PPC64浮点运算时有以下限制。

    标量(非Altivec):Valgrind提供了除了“fre”和“fres”之外的所有浮点指令的比特精确仿真,它们比PowerPC架构规范要求更精确。所有浮点运算都观察当前的舍入方式。

    但是,每次操作后都不会设置fpscr [FPRF]。这可以做到,但会产生可衡量的性能开销,到目前为止还没有找到它。

    在x86 / AMD64上,不支持IEEE754异常:使用默认的IEEE定义操作来处理所有浮点异常。Valgrind可以通过写入浮点状态和控制寄存器(fpscr)来检测,忽略并且可以警告,尝试取消屏蔽5种IEEE FP异常种类。

    矢量(Altivec,VMX):基本上与x86 / AMD64 SSE / SSE2一样:没有例外,并且有限地遵循舍入模式。对于Altivec,FP算法在IEEE / Java模式下完成,这比Linux默认设置更准确。“更准确”是指正确处理代数,而不是简单地将其清零。

已知无法使用的程序有:

  • emacs启动,但马上得出结论,它是不记忆和中止。这可能是Memcheck不能提供足够好的mallinfo功能仿真 。如果您使用标准的malloc / free例程来构建Emacs,它们会很正常。

2.12。示例运行

这是使用Memcheck运行一个小程序的日志。该程序实际上是正确的,报告的错误是GNU g ++(快照20010527)中潜在严重的代码生成错误的结果。

sewardj @ phoenix:〜/ newmat10 $〜/ Valgrind-6 / valgrind -v ./bogon == 25832 == Valgrind 0.10,x86 RedHat 7.1的内存错误检测器。== 25832 ==版权所有(C)2000-2001和GNU GPL'd,由朱利安·西瓦德。== 25832 ==启动,带标志:== 25832 == --suppressions = / home / sewardj / Valgrind / redhat71.supp== 25832 ==从/lib/ld-linux.so.2读取syms== 25832 ==从/lib/libc.so.6读取符号== 25832 ==从/mnt/pima/jrs/Inst/lib/libgcc_s.so.0读取符号== 25832 ==从/lib/libm.so.6读取syms== 25832 ==从/mnt/pima/jrs/Inst/lib/libstdc++.so.3读取符号== 25832 ==从/home/sewardj/Valgrind/valgrind.so读取符号== 25832 ==从/ proc / self / exe读取syms== == 25832 == 25832 ==大小4读取无效== 25832 == at 0x8048724:BandMatrix :: ReSize(int,int,int)(bogon.cpp:45)== 25832 == by 0x80487AF:main(bogon.cpp:66)== 25832 ==地址0xBFFFF74C不是stack'd,malloc'd或free'd== == 25832== 25832 ==错误摘要:1个上下文中的1个错误(被禁止:0从0)== 25832 == malloc / free:在退出时使用:0个字节,0个块。== 25832 == malloc / free:0 allocs,0 frees,0 bytes assigned。== 25832 ==对于详细的泄漏分析,重新运行:--leak-check = yes

海湾合作委员会在GCC 3.0发货前大约一周内修复了这个问题。

2.13。你可能看到的警告信息

其中一些仅在您运行在详细模式(启用-v))时出现:

  • More than 100 errors detected. Subsequent errors will still be recorded, but in less detail than before.

    经过100次不同的错误显示,Valgrind对于收集这些错误变得更保守。然后,当决​​定两个错误是否相同时,它只需要前两个堆栈帧中的程序计数器进行匹配。在此之前,前四个框架中的PC需要匹配。这个黑客具有在第一个100之后减慢新错误的出现的效果。可以通过重新编译Valgrind来更改100个常量。

  • More than 1000 errors detected. I'm not reporting any more. Final error counts may be inaccurate. Go fix your program!

    检测到1000个不同的错误后,Valgrind再次忽略。收集更多不同的信息似乎不太可能对任何人都有实际的帮助,并且避免了Valgrind花费越来越多的时间将新的错误与日益增长的收藏进行比较的危险。如上所述,1000个数字是编译时常数。

  • Warning: client switching stacks?

    Valgrind在堆栈指针中发现了这么大的变化,它猜测客户端正在切换到不同的堆栈。在这一点上,它使得kludgey猜测新堆栈的基础在哪里,并相应地设置内存权限。目前,“大变化”被定义为堆栈指针寄存器值的变化大于2000000。如果Valgrind猜测错误,您可能会在此之后收到许多虚假的错误消息和/或在堆栈跟踪记录代码中发生崩溃。通过向Valgrind通知使用VALGRIND_STACK_REGISTER客户机请求的堆栈边界可以避免这些问题。

  • Warning: client attempted to close Valgrind's logfile fd <number>

    Valgrind不允许客户端关闭日志文件,因为在此之后您永远不会看到任何诊断信息。如果看到此消息,可能需要使用该 --log-fd=<number>选项来指定不同的日志文件文件描述符号。

  • Warning: noted but unhandled ioctl <number>

    Valgrind观察到对一系列 ioctl系统调用的调用,但没有修改其内存状态信息(因为没有人写过合适的包装器)。该通话仍然会通过,但是由于内存信息的不更新,您可能会在此之后收到虚假错误。

  • Warning: set address range perms: large range <number>

    诊断消息,主要是为了受益于Valgrind开发人员,与内存权限有关。