linux c 内存泄漏调试工具 《valgrind用户手册》 2. 使用和理解Valgrind核心

valgrind 用户手册原版地址:https://valgrind.org/docs/manual/manual.html

此原文地址:https://valgrind.org/docs/manual/manual-core.html

以下是google翻译

本章介绍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启动时会读取。构建机制选择默认抑制,以使您的计算机上检测到的OS和库具有合理的行为。为了使写入抑制更加容易,可以使用该 --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格式。Valgrind 3.9.0已禁用用于“ stabs”调试格式的阅读器(由3.1之前的GCC版本使用)。

准备滚动时,请如上所述运行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-socket于Valgrind自身指定的端口号 。

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

这是关于注释和工具的概要分析输出之间的关系的重要说明。注释包含来自Valgrind核心和所选工具的消息混合。如果该工具报告错误,它将把它们报告给注释。但是,如果该工具进行性能分析,则配置文件数据将被写入某种文件,具体取决于工具,并且与有效的--log-*选项无关 。该评论旨在成为一种低带宽,人类可读的频道。另一方面,概要分析数据通常很庞大,没有进一步处理就没有意义,这就是我们选择这种安排的原因。

2.4。报告错误

当错误检查工具检测到程序中发生的不良情况时,错误消息将被写入注释。这是Memcheck的示例:

==25832== Invalid read of size 4
==25832==    at 0x8048724: BandMatrix::ReSize(int, int, int) (bogon.cpp:45)
==25832==    by 0x80487AF: main (bogon.cpp:66)
==25832==  Address 0xBFFFF74C is not stack'd, malloc'd or free'd

该消息表明该程序对地址0xBFFFF74C进行了非法的4字节读取,据Memcheck所知,该地址不是有效的堆栈地址,也不对应于任何当前堆块或最近释放的堆块。读取发生在的第45行 bogon.cpp,从同一文件的第66行调用,以此类推。对于与已标识(当前或已释放)堆块相关的错误,例如读取已释放的内存,Valgrind不仅报告错误发生的位置,以及分配/释放关联堆块的位置。

Valgrind会记住所有错误报告。检测到错误后,会将其与旧报告进行比较,以查看是否重复。如果是这样,则会指出该错误,但不会发出进一步的评论。这样可以避免您被成千上万的重复错误报告淹没。

如果您想知道每个错误发生了多少次,请运行该-v选项。执行完成后,所有报告将连同其出现次数一起打印输出。这样可以很容易地查看哪些错误最常发生。

在相关操作实际发生之前,将报告错误。例如,如果您使用的是Memcheck,并且您的程序尝试从地址0读取,则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构建系统时,脚本会创建一个默认的禁止文件 。

您可以随意修改并添加到抑制文件,或者更好地编写自己的文件。允许多个抑制文件。如果项目的一部分包含您无法或不想修复的错误,但又不想不断地被提醒,这将很有用。

注意:到目前为止,添加抑制的最简单方法是使用Core Command-Line Options中--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

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

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

每个抑制具有以下组成部分:

  • 第一行:其名称。这只是给抑制器起了方便的名字,在程序结束时打印出来的已使用抑制器摘要中用它来引用。名字叫什么并不重要。任何标识字符串都可以。

  • 第二行:禁止使用的工具名称(如果多个,用逗号分隔),以及禁止本身的名称,以冒号分隔(nb:不允许使用空格),例如:

    tool_name1,tool_name2:suppression_name

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

    如果工具不了解针对其的禁止,则工具会在启动时抱怨。工具会忽略不针对它们的抑制。结果,将所有工具的抑制结果放入同一个抑制文件中是非常实际的。

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

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

    位置可以是共享对象,函数或源代码行的名称。他们开始用 obj:, fun:或 src:分别。要匹配的函数,对象和文件名可以使用通配符 *和 ?。源代码行使用形式指定filename[:lineNumber]

    重要说明: C ++函数名称必须 乱码。如果您正在手动编写禁止操作,请使用该--demangle=no选项在错误消息中获取错误的名称。C ++名称错误的示例是 _ZN9QListView4showEv。这是GNU C ++编译器内部使用的格式,也是禁止文件中必须使用的格式。QListView::show()在C ++源代码级别上看到的是等效的已取消组合的名称。

    定位线也可以只是“ ...”(三个点)。这是帧级通配符,它​​匹配零个或多个帧。帧级通配符很有用,因为它们使忽略感兴趣帧之间不感兴趣帧的数量变化变得容易。这在编写抑制功能时非常重要,该功能旨在针对编译器完成的函数内联量的变化具有鲁棒性。

  • 最后,整个抑制必须在花括号之间。每个大括号必须是其各自行上的第一个字符。

仅当错误与抑制中的所有详细信息匹配时,抑制才会抑制错误。这是一个例子:

{
  __gconv_transform_ascii_internal/__mbrtowc/mbtowc
  Memcheck:Value4
  fun:__gconv_transform_ascii_internal
  fun:__mbr*toc
  fun: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:Value4
  obj:/usr/X11R6/lib/libX11.so.6.2
  obj:/usr/X11R6/lib/libX11.so.6.2
  obj:/usr/X11R6/lib/libXaw.so.7.0
}

这样可以抑制任何大小为4的未初始化值错误,该错误发生在libX11.so.6.2,从同一库中的任何地方调用,从相同库中的任何地方调用时 libXaw.so.7.0。位置的不精确说明令人遗憾,但是,考虑到在制作此示例的Linux发行版中附带的X11库已删除了它们的符号表,这几乎是您所希望的。

同样是Memcheck工具的src:规范示例:

{
  libX11.so.6.2/libX11.so.6.2/libXaw.so.7.0
  Memcheck:Value4
  src:valid.c:321
}

这样可以抑制in中的第321行出现的任何size-4未初始化值错误valid.c

尽管上述两个例子不明确这一点,您可以自由搭配obj:, fun:以及 src: 在抑制线。

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

{
   a-contrived-example
   Memcheck:Leak
   fun:malloc
   ...
   fun:ddd
   ...
   fun:ccc
   ...
   fun:main
}
在通过以下方式分配内存的情况下,这可以抑制Memcheck内存泄漏错误main :(尽管有任意数量的中介,包括零) ccc,依次通过ddd和最终调用 malloc.

2.6.``核心命令行选项

如上所述,Valgrind的核心接受一组通用选项。这些工具还接受特定于工具的选项,这些选项针对每个工具单独记录。

Valgrind的默认设置在大多数情况下都能成功地提供合理的行为。我们按粗略类别将可用选项分组。

2.6.1. 工具选择选项

最重要的选择。

--tool= [default: memcheck]

运行称为的Valgrind工具toolname,例如memcheck,cachegrind,callgrind,helgrind,drd,massif,dhat,bouchey,none,exp-sgcheck,exp-bbv等。

2.6.2 ..基本选项

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

-h --help

对核心和所选工具的所有选项显示帮助。如果重复该选项,则等同于给出 --help-debug

--help-debug

与相同--help,但还列出了调试选项,通常仅对Valgrind的开发人员有用。

--version

显示Valgrind内核的版本号。工具可以具有自己的版本号。有一个适当的方案可以确保仅在已知核心版本可以使用这些工具时才执行工具。这样做是为了最大程度地减少由于工具与核心版本不兼容而引起的奇怪问题的机会。

-q, --quiet

以静默方式运行,并且仅显示错误消息。如果您正在运行回归测试或具有其他一些自动化测试机制,则很有用。

-v, --verbose

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

--trace-children= [default: no]

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

请注意,Valgrind确实会跟踪到a的子项 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= [default: no]

启用后,Valgrind将不会显示由fork调用导致的子进程的任何调试或日志记录输出。在处理创建子进程时,这可以使输出的混乱程度降低(尽管会产生更多误导)。与结合使用时特别有用--trace-children=。如果您请求XML输出(--xml=yes),也强烈建议使用此选项,因为否则,子级和父级的XML可能会混合在一起,这通常使它无用。

--vgdb= [default: yes]

指定--vgdb=yes或时,Valgrind将提供“ gdbserver”功能 --vgdb=full。这使外部GNU GDB调试器在Valgrind上运行时可以控制和调试程序。 --vgdb=full会产生巨大的性能开销,但会提供更精确的断点和观察点。有关详细说明,请参见使用Valgrind的gdbserver和GDB调试程序。

如果启用了嵌入式gdbserver,但当前未使用任何gdb,则vgdb 命令行实用程序可以从外壳向Valgrind发送“监视命令”。Valgrind核心提供了一组 Valgrind监视命令。工具可以选择提供工具特定的监视命令,这些命令记录在工具特定的章节中。

--vgdb-error= [default: 999999999]

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

--vgdb-stop-at= [default: none]

当通过--vgdb=yes或启用Valgrind gdbserver时,请使用此选项 --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= [default: no]

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

--time-stamp= [default: no]

启用后,每条消息之前都会显示自启动以来经过的挂钟时间,以天,小时,分钟,秒和毫秒表示。

--log-fd= [default: 2, stderr]

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

--log-file=

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

%p被替换为当前的进程ID。这对于调用多个进程的程序非常有用。警告:如果您使用--trace-children=yes并且您的程序调用了多个进程,或者您的程序派生后没有调用exec,并且您不使用此说明符(或%q下面的说明符),则所有这些进程的Valgrind输出将进入一个文件,可能会混杂可能不完整。注意:如果程序在之后派生并调用exec,则从fork和exec之间的时间段的子代的Valgrind输出将丢失。幸运的是,对于大多数程序而言,这种差距确实很小。和现代程序posix_spawn 仍然使用。

%n替换为该过程唯一的文件序号。这对于从相同文件名模板生成多个文件的进程很有用。

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

%%被替换为%

如果an %后面跟随任何其他字符,则将导致中止。

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

--log-socket=

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

2.6.3。与错误相关的选项

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

--xml= [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或更高版本,在源树的文件中指定了输出格式 。

在请求XML输出时,建议GUI通过的选项是:--xml=yes启用XML输出, --xml-file将XML输出发送到(可能是GUI选择的)文件,--log-file将纯文本输出发送到第二个GUI选择的文件, --child-silent-after-fork=yes,并将 -q纯文本输出限制为Valgrind自身创建的严重错误消息。例如,无法读取指定的禁止文件将被视为严重错误消息。这样,为了成功运行,文本输出文件将为空。但是,如果不为空,则它将包含GUI用户应该意识到的重要信息。

--xml-fd= [default: -1, disabled]

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

--xml-file=

指定Valgrind应该将其XML输出发送到指定文件。它必须与结合使用 --xml=yes。文件名中出现的任何%p或 %q序列都以与完全相同的方式扩展--log-file。有关详细信息,请参见 --log-file的描述。

--xml-socket=

指定Valgrind应该在指定的IP地址将其XML输出发送到指定的端口。它必须与结合使用--xml=yes。参数的形式与所使用的形式相同--log-socket。有关--log-socket 更多详细信息,请参见的描述。

--xml-user-comment=

在XML输出的开头嵌入一个额外的用户注释字符串。仅在--xml=yes指定时有效;否则忽略。

--demangle= [default: yes]

启用/禁用C ++名称的自动拆线(解码)。默认启用。启用后,Valgrind将尝试将编码的C ++名称转换回接近原始名称的名称。分解器处理由g ++版本2.X,3.X和4.X破坏的符号。

关于分解的一个重要事实是,抑制文件中提到的函数名称应采用整齐的形式。在搜索适用的抑制时,Valgrind不会对函数名称进行拆散,因为否则将使抑制文件的内容取决于Valgrind的拆解机制的状态,并且还会减慢抑制匹配的速度。

--num-callers= [default: 12]

指定用于标识程序位置的堆栈跟踪中显示的最大条目数。请注意,错误仅通过使用函数的前四个位置(当前函数中的位置以及它的三个直接调用方的位置)来实现。因此,这不会影响报告的错误总数。

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

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

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

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

请注意,堆栈扫描是一种不精确的启发式机制,可能会产生非常误导的结果,或者根本不会给出任何结果。它仅应在正常展开失败时的紧急情况下使用,但是具有堆栈跟踪很重要。

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

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

此实现的第二个限制是它将仅扫描包含起始堆栈指针的页面(通常为4KB)。如果堆栈帧很大,则可能导致迹线中仅出现很少(甚至没有任何)。另外,如果您很不幸,并且在其包含页面的末尾附近有一个初始堆栈指针,则扫描可能会丢失所有有趣的帧。

默认情况下,堆栈扫描是禁用的。正常的用例是在堆栈跟踪非常短时要求它。因此,要启用它,请使用--unw-stack-scan-thresh=number。这要求Valgrind尝试使用堆栈扫描来“扩展”包含少于number帧的堆栈跟踪。

如果确实进行了堆栈扫描,则最多只会生成所指定的帧数--unw-stack-scan-frames。通常,堆栈扫描会生成大量垃圾条目,因此默认情况下将此值设置为较低的值(5)。在任何情况下,都--num-callers不会创建大于所指定值的堆栈跟踪。

--error-limit= [default: yes]

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

--error-exitcode= [default: 0]

指定一个替代退出代码,如果Valgrind报告运行中有任何错误,则返回该退出代码。当设置为默认值(零)时,Valgrind的返回值将始终是被模拟过程的返回值。当设置为非零值时,如果Valgrind检测到任何错误,则返回该值。这对于将Valgrind用作自动化测试套件的一部分很有用,因为通过检查返回码,可以很容易地检测Valgrind报告了错误的测试用例。

--exit-on-first-error= [default: no]

如果启用此选项,Valgrind将在第一个错误时退出。必须使用--error-exitcodeoption 定义非零退出值 。如果您正在运行回归测试或具有其他一些自动化测试机制,则很有用。

--error-markers=, [default: none]

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

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

请注意,可以使用空标记。因此,仅可以使用开始(或结束)标记。

--show-error-list=no|yes [default: no]

如果启用此选项,则对于报告错误的工具,valgrind将在出口处显示检测到的错误列表和已使用的抑制列表。

请注意,在详细程度2和更高的级别,除非--show-error-list=no选中,否则valgrind会自动显示检测到的错误列表和退出时使用的抑制列表 。

-s

指定-s等同于 --show-error-list=yes

--sigill-diagnostics= [default: yes]

启用/禁用非法指令诊断的打印。默认情况下启用,但--quiet给定时默认为禁用 。通过提供此选项,始终可以显式覆盖默认值。

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

--keep-debuginfo= [default: no]

启用后,保留(“归档”)符号和所有其他debuginfo来保存已卸载的代码。这允许保存的堆栈跟踪信息包括已被dlclose(或类似)的代码的文件/行信息。请注意这一点,因为它可能导致重复加载和卸载共享对象的程序无限制地使用内存。

某些工具和某些功能仅对存档的调试信息提供有限的支持。Memcheck完全支持它。通常,报告错误的工具可以使用存档的调试信息来显示错误堆栈跟踪。已知的限制是:Helgrind过去对竞争条件的访问堆栈跟踪是不使用存档的调试信息。Massif(更常见的是xtree Massif输出格式)不使用存档的调试信息。仅Memcheck已通过进行(某种程度上)测试--keep-debuginfo=yes,因此其他工具可能具有未知的限制。

--show-below-main= [default: no]

默认情况下,错误的堆栈跟踪不显示下面显示的任何函数,main因为在大多数情况下,它是不感兴趣的C库内容和/或gobbledygook。或者,如果main堆栈跟踪中不存在,则堆栈跟踪将不显示任何main类似于glibc之类的函数 __libc_start_main。此外,如果main在跟踪中存在类似函数,则将 它们标准化为(below main),以使输出更具确定性。

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

--fullpath-after= [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按照指定的顺序,将每个产生的路径与所有-specified字符串进行比较。如上所述,第一个匹配的字符串将导致路径被截断。如果没有匹配项,则显示完整路径。从多个不相关的目录中提取源代码时,这有助于切掉前缀。

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

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

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

在这些情况下,您可以通过指定来提供绝对路径,作为Valgrind搜索调试对象的最终位置 --extra-debuginfo-path=/path/to/debug/objects。给定的路径将放在搜索对象的绝对路径名之前。例如,如果Valgrind在为其寻找debuginfo /w/x/y/zz.so 并为其--extra-debuginfo-path=/a/b/c指定了调试信息,它将在处寻找一个调试对象 /a/b/c/w/x/y/zz.so

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

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

这是3.9.0版中引入的实验性新功能。

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

debuginfo服务器必须在port上接受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和main对象没有以正确的方式拆分时,这可能很有用。但是,使用此方法时要小心:它会禁用所有一致性检查,并且已经观察到当主对象和debuginfo对象不匹配时,Valgrind会崩溃。

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

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

--gen-suppressions= [default: no]

设置yes为时,Valgrind将在显示每个错误之后暂停并打印以下行:

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

RetN Ret或 n Ret导致Valgrind继续执行,而不会输出对该错误的抑制。

按下Y Ret或 y Ret使Valgrind为该错误写一个抑制符。如果您以后不想再听到该错误,则可以将其剪切并粘贴到抑制文件中。

设置all为时,Valgrind将为每个报告的错误打印抑制信息,而无需查询用户。

该选项在C ++程序中特别有用,因为它可以根据需要打印出带有乱码的名称。

请注意,打印的抑制内容尽可能具体。您可能希望通过在函数名称中添加通配符并使用帧级通配符来实现相似的功能。通配符功能强大而又灵活,并且通过一些仔细的编辑,您可能仅需少量抑制就可以抑制整个相关错误家族。

有时,相同的抑制可以抑制两个不同的错误,在这种情况下,Valgrind会多次输出抑制,但是您只需要在抑制文件中有一个副本(但是有多个副本不会造成问题)。另外,抑制名称为 ;名称实际上并不重要,它仅与-v打印所有已使用的抑制记录的选项一起使用。

--input-fd= [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= [default: 2000000]

堆叠框架的最大尺寸。如果堆栈指针的移动量超过此数量,则Valgrind将假定程序正在切换到其他堆栈。

如果您的程序具有大的堆栈分配数组,则可能需要使用此选项。Valgrind跟踪程序的堆栈指针。如果它的变化量大于阈值量,则Valgrind会假定您的程序正在切换到其他堆栈,并且Memcheck的行为与更改小于阈值的堆栈指针的行为不同。通常,这种启发式方法效果很好。但是,如果您的程序在堆栈上分配了大型结构,则该启发式将被愚弄,Memcheck随后将报告大量无效的堆栈访问。此选项使您可以将阈值更改为其他值。

仅当Valgrind的调试输出指导您这样做时,才应考虑使用此选项。在这种情况下,它将告诉您应该指定的新阈值。

通常,在堆栈上分配大型结构是一个坏主意,因为您很容易用完堆栈空间,尤其是在内存有限或希望支持大量线程且每个堆栈较小的系统上,并且因为错误由Memcheck执行的检查对堆分配的数据比对堆栈分配的数据更有效。如果必须使用此选项,则可能希望考虑重写代码以在堆而不是堆栈上进行分配。

--main-stacksize= [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= [default: 500]

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

2.6.4。与malloc相关的选项

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

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

默认情况下,Valgrind的malloc, realloc等返回一个起始地址为8字节对齐或16字节对齐的块(该值取决于平台并与平台默认值匹配)。此选项使您可以指定其他对齐方式。提供的值必须大于或等于默认值,小于或等于4096,并且必须是2的幂。

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

Valgrind malloc, realloc,等在运行程序分配的每个堆块之前和之后添加填充块。这种填充块称为Redzone。Redzone大小的默认值取决于工具。例如,Memcheck在客户端分配的每个块之前和之后添加并保护至少16个字节。这样,它就可以检测最多16个字节的块下溢或溢出。

增加红色区域的大小可以检测较大距离的超限,但可以增加Valgrind使用的内存量。减小Redzone大小将减少Valgrind所需的内存,但也会减少检测到超限/不足的机会,因此不建议这样做。

--xtree-memory=none|allocs|full [none]

替换Valgrind malloc, realloc,等的工具可以有选择地生成一个执行树,详细说明哪段代码负责堆内存的使用。有关执行树 的详细说明,请参见执行树。

设置为时none,不生成任何内存执行树。

设置为时allocs,内存执行树将给出当前分配的字节数和当前分配的块数。

设置为时full,内存执行树提供6种不同的度量:当前分配的字节和块数(与相同的值allocs),分配的字节和块总数,已释放的字节和块总数。

请注意,生成xtree的cpu和内存开销取决于工具。对于该值allocs,cpu的开销很小,因为生成此报告所需的信息在任何情况下都由该工具维护。对于massif和helgrind,指定full 意味着捕获每个自由操作的堆栈跟踪,而通常这些工具仅捕获分配堆栈跟踪。对于Memcheck,该值的cpu开销full很小,因为该值只能与--keep-stacktraces=alloc-and-free或 结合使用 --keep-stacktraces=alloc-then-free,它已经记录了每个空闲操作的堆栈跟踪。xtree中每个唯一堆栈跟踪的内存开销在5到10个字之间变化,如果专门针对xtree,则加上记录空闲操作的堆栈跟踪所需的内存。

--xtree-memory-file= [default: xtmemory.kcg.%p]

指定Valgrind应在指定文件中生成xtree内存报告。文件名中出现的任何%p或 %q序列都以与完全相同的方式扩展--log-file。有关 详细信息,请参见--log-file的描述。

如果文件名包含扩展名 .ms,那么生成的文件格式将是massif输出文件格式。如果文件名包含扩展名, .kcg 或者未提供或未识别任何扩展名,则生成的文件格式将为callgrind输出格式。

有关执行树格式的详细说明,请参见执行树。

2.6.5 ..罕见选项

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

--smc-check= [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会使Valgrind明显减速。none由于大多数程序中很少动态生成代码,因此运行很少会加快速度。VALGRIND_DISCARD_TRANSLATIONS客户请求是该 请求的替代方案--smc-check=all ,--smc-check=all-non-file 它需要程序员付出更多的努力,但通过准确告知何时需要重新生成翻译,Valgrind可以更快地运行您的程序。

--smc-check=all-non-file提供更便宜但更受限制的--smc-check=all。它将检查添加到不是源自文件支持的内存映射的所有转换中。生成代码的典型应用程序(例如,Web浏览器中的JIT)将代码生成到匿名的映射区域中,而浏览器的“固定”代码始终位于文件支持的映射中。 --smc-check=all-non-file 利用这种观察,将检查的开销限制在可能是JIT生成的代码上。

--read-inline-info= [default: see below]

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

==15380== Conditional jump or move depends on uninitialised value(s)
==15380==    at 0x80484EA: main (inlinfo.c:6)
==15380== 
==15380== Conditional jump or move depends on uninitialised value(s)
==15380==    at 0x8048550: fun_noninline (inlinfo.c:6)
==15380==    by 0x804850E: main (inlinfo.c:34)
==15380== 
==15380== Conditional jump or move depends on uninitialised value(s)
==15380==    at 0x8048520: main (inlinfo.c:6)

这是相同的错误 --read-inline-info=yes

==15377== Conditional jump or move depends on uninitialised value(s)
==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== Conditional jump or move depends on uninitialised value(s)
==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== Conditional jump or move depends on uninitialised value(s)
==15377==    at 0x8048520: fun_d (inlinfo.c:6)
==15377==    by 0x8048520: main (inlinfo.c:35)

--read-var-info= [default: no]

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

==15363== Uninitialised byte(s) found during client check request
==15363==    at 0x80484A9: croak (varinfo1.c:28)
==15363==    by 0x8048544: main (varinfo1.c:55)
==15363==  Address 0x80497f7 is 7 bytes inside data symbol "global_i2"
==15363== 
==15363== Uninitialised byte(s) found during client check request
==15363==    at 0x80484A9: croak (varinfo1.c:28)
==15363==    by 0x8048550: main (varinfo1.c:56)
==15363==  Address 0xbea0d0cc is on thread 1's stack
==15363==  in frame #1, created by main (varinfo1.c:45)

这是相同的错误 --read-var-info=yes

==15370== Uninitialised byte(s) found during client check request
==15370==    at 0x80484A9: croak (varinfo1.c:28)
==15370==    by 0x8048544: main (varinfo1.c:55)
==15370==  Location 0x80497f7 is 0 bytes inside global_i2[7],
==15370==  a global variable declared at varinfo1.c:41
==15370== 
==15370== Uninitialised byte(s) found during client check request
==15370==    at 0x80484A9: croak (varinfo1.c:28)
==15370==    by 0x8048550: main (varinfo1.c:56)
==15370==  Location 0xbeb4a0cc is 0 bytes inside local var "local"
==15370==  declared at varinfo1.c:46, in frame #1 of thread 1

--vgdb-poll= [default: 5000]

作为其主循环的一部分,Valgrind调度程序将轮询以检查gdbserver是否必须处理某些活动(例如外部命令或来自gdb的某些输入)。该活动轮询将在运行给定数量的基本块(或略大于给定数量的基本块)之后进行。该轮询非常便宜,因此默认值设置得较低。如果在大多数情况下(大部分时间)所有线程都被阻塞,则vgdb无法使用ptrace系统调用来中断Valgrind,则可能会进一步减小该值。

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

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

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

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

--run-libc-freeres= [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上正常运行,但是在退出时出现段错误,则您可能会发现此问题已 --run-libc-freeres=no解决,尽管这样做的代价是可能会错误地报告中的空间泄漏 libc.so

--run-cxx-freeres= [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时才有用。在Solaris和Mac OS X上它将被忽略。

    libpthread.sopthread程序使用的GNU glibc pthread库()维护pthread堆栈的缓存。当pthread终止时,用于pthread堆栈的内存和某些与线程本地存储相关的数据结构并不总是直接释放。此内存保留在高速缓存中(最大大小),如果启动新线程,则将重新使用该内存。

    由于helgrind不了解内部glibc缓存同步原语,因此此缓存使helgrind工具在此缓存的内存上报告一些误报竞争条件错误。因此,在使用helgrind时,禁用缓存有助于避免错误的正竞争条件,尤其是在使用线程本地存储变量(例如,使用__thread限定符的变量 )时。

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

    注意:Valgrind使用glibc堆栈高速缓存实现的一些内部知识并通过检查pthread库的调试信息来禁用高速缓存。因此,该技术有些脆弱,可能不适用于所有glibc版本。已在各种平台上使用各种glibc版本(例如2.11、2.16、2.18)成功进行了测试。

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

  • fallback-llsc: (仅MIPS和ARM64):启用加载链接(LL)和存储条件(SC)指令的替代实现。标准实现提供了更多正确的行为,但是会导致某些处理器实现上出现不确定的循环,这些循环无法容忍LL和SC之间的额外内存引用。到目前为止,仅在Cavium 3内核上才发生这种情况。您不需要使用此标志,因为在启动时会检测到相关的内核,并且在必要时会自动启用替代实现。没有等效的反标记:如果自动启用了替代实现,则不能强制禁用它。存在根本问题是因为“

--fair-sched= [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上运行,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设备上处理Qualcomm Adreno 3XX系列GPU的专有ioctl。

--merge-recursive-frames= [default: 0]

一些递归算法,例如平衡二叉树实现,会创建许多不同的堆栈跟踪,每个跟踪都包含调用周期。循环定义为两个相同的程序计数器值,它们之间被零个或多个其他程序计数器值分隔。然后,Valgrind可能会使用大量内存来存储所有这些堆栈跟踪。考虑到这样的堆栈跟踪包含重复的无趣的递归调用,而不是诸如启动递归调用的函数之类的更有趣的信息,这是对内存的不良使用。

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

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

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

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

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

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

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

--aspace-minaddr=

[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= [default: 1MB]

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

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

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

--show-emwarns= [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扫描其符号表,如果该符号不存在,则运行中止,而是而不是继续使用未标记的库。请注意,您应将整个标志放在引号中,以防止shell扩展* 和?通配符。

--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,helgrind,drd,massif,dhat,exp-sgcheck)。

  • 备用malloc库:使用soname mymalloclib.so(而不是其他任何)替换特定备用库中与malloc相关的功能,请提供option --soname-synonyms=somalloc=mymalloclib.so。模式可用于匹配多个库soname。例如,--soname-synonyms=somalloc=*tcmalloc* 将匹配tcmalloc库的所有变体的名称(本机,调试,分析,... tcmalloc变体)。

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

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

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

  • 要仅截取默认系统库中的分配符号,而不截取任何其他共享库或定义公共malloc或Operator的可执行文件中的分配符号,则新的相关函数使用不存在的库名,例如--soname-synonyms=somalloc=nouserintercepts (其中nouserintercepts可以是任何不存在的库名)。

  • 动态(运行时)链接程序的共享库不搜索全局公用符号,例如与malloc相关的功能(由somalloc同义词标识)的公用符号。

--progress-interval= [default: 0, meaning 'disabled']

这是对Valgrind调试输出的增强。最终用户不太可能感兴趣。

number设置为非零值时,Valgrind将每秒钟打印一行一行的进度摘要number。有效的设置number介于0和3600之间(含0和3600)。这是一些number 设置为10的示例输出:

PROGRESS: U 110s, W 113s, 97.3% CPU, EvC 414.79M, TIn 616.7k, TOut 0.5k, #thr 67
PROGRESS: U 120s, W 124s, 96.8% CPU, EvC 505.27M, TIn 636.6k, TOut 3.0k, #thr 64
PROGRESS: U 130s, W 134s, 97.0% CPU, EvC 574.90M, TIn 657.5k, TOut 3.0k, #thr 63

每行显示:

  • U:总用户时间
  • W:总挂钟时间
  • CPU:总体平均CPU使用率
  • EvC:事件检查次数。事件检查是模拟程序中的向后分支,因此这是程序向前进度的一种量度
  • TIn:JIT检测到的代码块数
  • TOut:已丢弃的已检测代码块的数量
  • #thr:程序中的线程数

从这些进展中,可以观察到:

  • 当程序受计算限制时(TIn 缓慢EvC上升,迅速上升)
  • 当程序处于自旋循环中时(TIn/已TOut 修复,EvC迅速上升)
  • 当程序受JIT约束时(TIn 快速上升)
  • 当程序迅速丢弃代码(TOut迅速上升)时
  • 当程序即将达到某个预期状态时(达到EvC您期望的某个值)
  • 程序空闲时(U上升速度比慢W

 

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:leak-check=yes

如果运行除Memcheck以外的任何其他工具,则将忽略此操作。如果没有该memcheck: 零件,那么如果您选择其他不了解的工具,则会导致问题 --leak-check=yes

2.7。对线程的支持

完全支持线程程序。

关于线程程序的主要要指出的是,您的程序将使用本机线程库,但是Valgrind会序列化执行,以便一次仅运行一个(内核)线程。这种方法避免了实现真正的Valgrind多线程版本的可怕实现问题,但这确实意味着线程应用程序永远不会同时使用一个以上的CPU,即使您拥有多处理器或多核计算机也是如此。

Valgrind不会调度线程本身。它使用简单的锁定方案仅确保一次仅运行一个线程。实际的线程调度仍在OS内核的控制之下。不过,这的意思是,在Valgrind上运行的程序与正常运行时的程序将有很大不同。这既是因为Valgrind正在序列化线程,又因为代码运行速度比正常情况慢得多。

如果您具有某种并发性,严重竞争,锁定或类似的错误,则日程安排上的这种差异可能导致程序的行为有所不同。在这种情况下,您可以考虑使用Helgrind和/或DRD工具进行跟踪。

在Linux上,Valgrind还支持直接使用 clone系统调用, futex等等。 clone在共享所有内容(线程)或不共享任何内容(类似fork)的情况下受支持;部分共享将失败。

2.7.1.``计划和多线程性能

线程仅在持有上述锁时才执行代码。在执行了一些指令之后,正在运行的线程将释放锁。准备好运行的所有线程将争夺该锁。

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

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

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

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

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

Valgrind的线程序列化和OS内核调度程序对CPU的分配可能与许多现代CPU上可用的CPU频率缩放严重地相互作用。为了降低功耗,如果最近未使用过CPU /内核,则会自动降低CPU或内核的频率。如果OS内核经常将刚刚获得锁的线程分配给另一个CPU /内核,则该CPU /内核当前很可能处于较低频率。一段时间后,该CPU的频率将增加。但是,在此期间,(仅)正在运行的线程将以较低的频率运行。一旦该线程运行了一段时间,它将释放锁定。另一个线程将获得此锁定,并且可能会在与此同时时钟频率降低的另一个CPU上再次进行调度。

基于futex的锁定导致线程更频繁地更改CPU /内核。因此,如果激活了CPU频率缩放,则基于futex的锁定可能会大大降低在Valgrind下运行的多线程应用程序的性能。与在禁用了CPU频率缩放的计算机上运行相比,已观察到性能下降高达50%。基于管道的锁定锁定方案还严重影响了CPU频率缩放,已观察到10..20%的性能损失。

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

避免这些问题的另一种方法是告诉OS调度程序使用该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。执行树

执行树(xtree)由一组堆栈跟踪组成,每个堆栈跟踪都与一些资源消耗或事件计数相关联。取决于xtree,可以在xtree中记录不同的事件计数/资源消耗。多个工具可以使用xtree产生内存。Memcheck可以在xtree中输出泄漏搜索结果。

xtree的典型用法是显示程序堆用法的图形或文本表示。下图是kcachegrind生成的堆使用情况xtree图形表示。在kcachegrind输出中,您可以看到主要的当前堆使用情况(间接分配)为528个字节:通过调用函数f1间接分配的388个字节和通过函数f2调用的间接分配的140个字节。f2通过调用g2分配了内存,而f1通过调用g11和g12分配了内存。g11,g12和g1直接调用了内存分配函数(malloc),因此具有非零的'Self'值。请注意,当kcachegrind显示xtree时,“调用图”中的“ Called”列和call nr指示并不重要(始终设置为0或1,与实际的调用nr无关)。

 

需要时使用option在执行结束时生成xtree堆内存报告--xtree-memory。也可以使用xtmemorymonitor命令按需生产(请参阅 Valgrind monitor命令)。当前,的XTree堆内存报告可以通过生产memcheckhelgrind 和massif工具。

由选项--xtree-memory或xtmemory monitor命令产生的 xtree显示了以下事件/资源消耗,这些事件/资源消耗描述了堆的使用情况:

  • curB当前分配的字节数。分配的字节数被添加到curB 每个分配的堆栈跟踪的值中。当释放由该堆栈跟踪分配的块时(通过另一个“释放”堆栈跟踪)释放它会减少

  • curBk 当前分配的块数,与curB相似:每次分配为+1,释放块时为-1。

  • totB已分配的总字节数。对于每个分配,分配的字节数都会增加。

  • totBk 已分配的总块数,与每次分配的totB相似:+1。

  • totFdB 总释放字节数,每次通过此堆栈跟踪(“释放”)释放一个块时,释放的字节数增加:+每个释放操作的nr个释放字节。

  • totFdBk 总的释放块数,与每个自由操作的totFdB相似:+1。

请注意,仅--xtree-memory=full在启动时给出了后4个计数 。

Xtree可以保存为2种文件格式,即“ Callgrind格式”和“ Massif格式”。

  • Callgrind格式

    Callgrind格式的xtree文件包含一个调用图,将每个堆栈跟踪与xtree中记录的值相关联。

    提供了不同的Callgrind格式文件可视化工具:

    Valgrind发行版包括callgrind_annotate 命令行实用程序,该实用程序读取xtree数据,并打印功能排序的列表,并可选地带有源注释。请注意,由于xtree的特殊性,您必须给--inclusive=yescallgrind_annotate 选项 。

    对于数据的图形可视化,可以使用 KCachegrind,这是一个基于KDE / Qt的GUI,可以轻松导航xtree可以包含的大量数据。

    请注意,即使指定,xtree Callgrind Format也不会使用内联信息--read-inline-info=yes

  • 断层块格式

    Massif格式的xtree文件包含一个详细的树调用图数据,用于记录在xtree中的每种事件。因此,对于--xtree-memory=alloc,输出文件将包含2个详细的树(用于count curB 和curBk),而--xtree-memory=full将给出包含6个详细的树的文件。

    提供了不同的Massif格式文件可视化工具。Valgrind发行版包括ms_print 命令行实用程序,该实用程序可生成易于读取的Massif输出文件。有关可视化Massif Format输出文件的更多详细信息,请参见使用Massif和ms_print和 using massif -visualizer。

    请注意,xtree Massif格式在指定时会使用内联信息--read-inline-info=yes

请注意,对于等效信息,Callgrind格式比Massif格式更紧凑。但是,Callgrind格式始终包含完整的数据:在文件生成过程中不会进行任何过滤,而过滤是由可视化工具(例如kcachegrind)完成的。kcachegrind特别易于使用,它可以分析包含多个事件计数或资源消耗的大型xtree数据。Massif格式(可选)仅包含部分数据。例如,根据--threshold选项,Massif工具可能会过滤一些数据 。

为了阐明xtree的概念,下面给出了以下命令产生的输出的一些摘录:

valgrind --xtree-memory=full --xtree-memory-file=xtmemory.kcg mfg
callgrind_annotate --auto=yes --inclusive=yes --sort=curB:100,curBk:100,totB:100,totBk:100,totFdB:100,totFdBk:100  xtmemory.kcg

下面的摘录显示程序mfg在60个不同的块中总共分配了770个字节。在这60个块中,有19个已释放,释放了总共242个字节。堆当前包含41个块中的528个字节。

--------------------------------------------------------------------------------
curB curBk totB totBk totFdB totFdBk 
--------------------------------------------------------------------------------
 528    41  770    60    242      19  PROGRAM TOTALS

下面提供了有关哪些功能已分配或释放内存的更多详细信息。例如,我们看到main已(直接或间接)分配了770个字节的内存,并释放了(直接或间接)了242个字节的内存。函数f1已(直接或间接)分配了570个字节的内存,并且没有(直接或间接)释放了内存。由函数f1分配的570个字节中,尚未释放388个字节(34个块)。

--------------------------------------------------------------------------------
curB curBk totB totBk totFdB totFdBk  file:function
--------------------------------------------------------------------------------
 528    41  770    60    242      19  mfg.c:main
 388    34  570    50      0       0  mfg.c:f1
 220    20  330    30      0       0  mfg.c:g11
 168    14  240    20      0       0  mfg.c:g12
 140     7  200    10      0       0  mfg.c:g2
 140     7  200    10      0       0  mfg.c:f2
   0     0    0     0    131      10  mfg.c:freeY
   0     0    0     0    111       9  mfg.c:freeX

下面给出了有关调用图以及哪些源行/调用(直接或间接)分配或释放了内存的更详细的信息。下面显示了main分配的770个字节已通过对f1和f2的调用间接分配。类似地,我们看到f1分配的570个字节是通过对g11和g12的调用间接分配的。由30个对g11的调用分配的330个字节中,有168个字节尚未释放。freeY函数(由main调用一次)总共释放了10个块和131个字节。

--------------------------------------------------------------------------------
-- Auto-annotated source: /home/philippe/valgrind/littleprogs/ + mfg.c
--------------------------------------------------------------------------------
curB curBk totB totBk totFdB totFdBk 
....
   .     .    .     .      .       .  static void freeY(void)
   .     .    .     .      .       .  {
   .     .    .     .      .       .     int i;
   .     .    .     .      .       .     for (i = 0; i < next_ptr; i++)
   .     .    .     .      .       .        if(i % 5 == 0 && ptrs[i] != NULL)
   0     0    0     0    131      10           free(ptrs[i]);
   .     .    .     .      .       .  }
   .     .    .     .      .       .  static void f1(void)
   .     .    .     .      .       .  {
   .     .    .     .      .       .     int i;
   .     .    .     .      .       .     for (i = 0; i < 30; i++)
 220    20  330    30      0       0        g11();
   .     .    .     .      .       .     for (i = 0; i < 20; i++)
 168    14  240    20      0       0        g12();
   .     .    .     .      .       .  }
   .     .    .     .      .       .  int main()
   .     .    .     .      .       .  {
 388    34  570    50      0       0     f1();
 140     7  200    10      0       0     f2();
   0     0    0     0    111       9     freeX();
   0     0    0     0    131      10     freeY();
   .     .    .     .      .       .     return 0;
   .     .    .     .      .       .  }

堆内存xtree帮助您了解(大型)程序如何使用堆。完整的堆内存xtree有助于查明一些分配了许多小对象的代码:分配这些小对象可能会被更有效的技术所代替,例如使用malloc分配大块,然后将该块按顺序分解为较小的块以减少分配大量小块的cpu和/或内存开销。这样的完整xtree信息补充了callgrind可以显示的内容:callgrind可以显示对函数(例如malloc)的调用次数,但不指示已分配(或释放)的内存量。

完整的堆内存xtree也可以识别分配并释放许多块的代码:程序的总占用量可能无法反映出同一内存被反复分配然后释放的事实。

最后,诸如kcachegrind之类的Xtree可视化工具正在帮助识别大内存使用者,以便可能优化程序所需的内存量。

2.10 ..建立和安装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脚本测试current指示的当前X服务器的版本 $DISPLAY。这是一个已知的错误。目的是检测当前X客户端库的版本,以便可以为它们选择正确的抑制,但是测试将检查服务器版本。这是完全错误的。

如果您要构建Valgrind的二进制软件包以进行分发,请阅读README_PACKAGERS Readme Packagers。它包含一些重要信息。

除此之外,这里没有什么激动人心的地方。如果您有构建问题,请告诉我们。

2.11。如果有问题

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

有关Valgrind的已知限制以及已知无法使用的程序列表,请参见限制。

系统的所有部分都大量使用断言和内部自检。它们已永久启用,我们没有计划禁用它们。如果其中之一发生故障,请给我们发送邮件!

如果您在中遇到断言失败m_mallocfree.c,则可能是因为程序注销了堆块的末尾或开始之前,从而破坏了堆元数据。Valgrind希望在以这种方式死亡之前已经发出了这样的信息。

阅读Valgrind FAQ,以获取有关常见问题,崩溃等的更多建议。

2.12。限制

以下限制似乎很长。但是,大多数程序实际上都可以正常运行。

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指令集。此外,还支持POWER6 CPU中的Power ISA 2.05规范中的指令。

  • 在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上,FPD算术运算默认是在SSE2上完成的,因此从FP角度来看,amd64看起来更像PowerPC,而不是x86,并且与x86相比,明显的精度差异要少得多。

    舍入:对于以下转换,Valgrind确实会遵循4种IEEE规定的舍入模式(至最接近,至+无穷大,至-无穷大,至零):浮于整数,整数浮于有可能会损失精度的位置,以及浮动到浮动的舍入。对于所有其他FP操作,仅支持IEEE默认模式(四舍五入到最接近)。

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

    对于每个异常,IEEE754定义了两个动作过程:(1)可以调用用户定义的异常处理程序,或者(2)定义一个默认动作,该动作可以“修正”并允许计算继续进行而无需引发异常。

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

    当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功能仿真 。如果您将Emacs构建为使用标准的malloc / free例程,则可以正常工作。

2.13。一个示例运行

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

sewardj@phoenix:~/newmat10$ ~/Valgrind-6/valgrind -v ./bogon 
==25832== Valgrind 0.10, a memory error detector for x86 RedHat 7.1.
==25832== Copyright (C) 2000-2001, and GNU GPL'd, by Julian Seward.
==25832== Startup, with flags:
==25832== --suppressions=/home/sewardj/Valgrind/redhat71.supp
==25832== reading syms from /lib/ld-linux.so.2
==25832== reading syms from /lib/libc.so.6
==25832== reading syms from /mnt/pima/jrs/Inst/lib/libgcc_s.so.0
==25832== reading syms from /lib/libm.so.6
==25832== reading syms from /mnt/pima/jrs/Inst/lib/libstdc++.so.3
==25832== reading syms from /home/sewardj/Valgrind/valgrind.so
==25832== reading syms from /proc/self/exe
==25832== 
==25832== Invalid read of size 4
==25832==    at 0x8048724: BandMatrix::ReSize(int,int,int) (bogon.cpp:45)
==25832==    by 0x80487AF: main (bogon.cpp:66)
==25832==  Address 0xBFFFF74C is not stack'd, malloc'd or free'd
==25832==
==25832== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==25832== malloc/free: in use at exit: 0 bytes in 0 blocks.
==25832== malloc/free: 0 allocs, 0 frees, 0 bytes allocated.
==25832== For a detailed leak analysis, rerun with: --leak-check=yes

GCC的人员在GCC 3.0出厂前大约一周修复了此问题。

2.14. 您可能会看到的警告消息

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

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

    在显示了100个不同的错误之后,Valgrind对收集它们变得更加保守。然后,在确定两个错误是否确实相同时,只需要顶部两个堆栈帧中的程序计数器相匹配即可。在此之前,需要匹配前四个框架中的PC。此hack的作用是减慢前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发现堆栈指针有如此大的变化,以为客户端正在切换到其他堆栈。此时,它会大胆地猜测新堆栈的基础在哪里,并相应地设置内存权限。目前,“大变化”定义为堆栈指针寄存器的值变化超过2000000。如果Valgrind猜错了,您可能会在此之后收到很多假错误消息,并且/或者在堆栈跟踪记录代码中崩溃。您可以通过使用VALGRIND_STACK_REGISTER客户端请求通知Valgrind有关堆栈边界的方法来避免这些问题。

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

    Valgrind不允许客户端关闭日志文件,因为在那之后您将再也看不到任何诊断信息。如果看到此消息,则可能要使用该 --log-fd=选项来指定其他日志文件的文件描述符编号。

  • Warning: noted but unhandled ioctl

    Valgrind观察到对大量ioctl系统调用中的一个的 调用,但未修改其内存状态信息(因为尚未有人编写合适的包装器)。调用仍然会进行,但是由于未更新内存信息,您可能会收到虚假错误。

  • Warning: set address range perms: large range

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

 

 

你可能感兴趣的:(linux c 内存泄漏调试工具 《valgrind用户手册》 2. 使用和理解Valgrind核心)