MIPS上的Valgrind报告没有堆使用情况

时间:2014-09-30 16:17:45

标签: c memory-management memory-leaks mips valgrind

我正在使用valgrind(v3.10.0)来寻找复杂应用程序(一个经过大量修改的net-snmp版本)的内存泄漏,这是一个更大的软件套件的一部分。我确信存在泄漏(应用程序的内存占用量无限制地线性增长),但valgrind在终止时总会报告以下内容。

==1139== HEAP SUMMARY:
==1139==     in use at exit: 0 bytes in 0 blocks
==1139==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==1139== 
==1139== All heap blocks were freed -- no leaks are possible

总堆使用量不能为零 - 整个应用程序中有许多次调用mallocfree。 Valgrind仍然能够找到“无效写入”错误。

正在编译该应用程序以及其他软件包,其中MIPS处理器的uclibc-gcc工具链(uclibc v0.9.29)将闪存到运行busybox(v1.17.2)linux shell的嵌入式设备上。我正在设备上直接运行valgrind。我在启动Valgrind时使用以下选项:

--tool=memcheck --leak-check=full --undef-value-errors=no --trace-children=yes

基本上,即使我使用了堆,Valgrind也没有检测到任何堆使用情况。为什么会这样?我的任何假设(下面)都是错误的吗?


我尝试过什么

简单测试计划

我从Valgrind quick-start tutorial编译了简单的测试程序(使用与上面的应用程序相同的目标和工具链),以查看Valgrind是否会检测到泄漏。最终输出与上面相同:没有堆使用。

链接问题?

Valgrind文档在their FAQ上有以下说法:

  

如果您的程序是静态链接的,那么大多数Valgrind工具只有在能够用自己的版本替换某些功能(例如malloc)时才能正常工作。默认情况下,不替换静态链接的malloc函数。一个关键的指标是,如果Memcheck说“所有堆块都被释放 - 没有泄漏是可能的”。

以上听起来与我的问题完全一样,所以我查看它是否动态链接到包含mallocfree的C库。我使用了uclibc工具链的自定义ldd可执行文件(I can't use the native linux ldd),输出包括以下几行:

libc.so.0 => not found (0x00000000)
/lib/ld-uClibc.so.0 => /lib/ld-uClibc.so.0 (0x00000000)

(找不到它们的原因是因为我在x86主机设备上运行它; mips目标设备没有ldd可执行文件。)根据我的理解,malloc和{{ 1}}将位于其中一个库中,它们似乎是动态链接的。我还对可执行文件执行了freereadelf,以确认对nmmalloc的引用是未定义的(这是动态链接的可执行文件的特征)。

此外,我尝试使用常见问题解答建议的free选项启动Valgrind。

LD_PRELOAD支持?

正如评论者和回答者所指出的,Valgrind依赖于LD_PRELOAD的使用。有人建议我的工具链不支持此功能。为了确认它,我跟着this example创建了一个简单的测试库并加载它(我用一个只返回42的函数替换--soname-synonyms=somalloc=NONE)。测试工作正常,所以看起来我的目标支持LD_PRELOAD就好了。

精灵数据

我还会在rand()命令中包含一些可能有用的信息。而不是一个巨大的转储,我已经削减了一些东西,只包括可能相关的东西。

readelf

3 个答案:

答案 0 :(得分:10)

首先,让我们做一个真正的测试,看看是否有静态链接。

$ ldd -v /bin/true
    linux-vdso.so.1 =>  (0x00007fffdc502000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0731e11000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f07321ec000)

    Version information:
    /bin/true:
        libc.so.6 (GLIBC_2.3) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.3.4) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.14) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.4) => /lib/x86_64-linux-gnu/libc.so.6
        libc.so.6 (GLIBC_2.2.5) => /lib/x86_64-linux-gnu/libc.so.6
    /lib/x86_64-linux-gnu/libc.so.6:
        ld-linux-x86-64.so.2 (GLIBC_2.3) => /lib64/ld-linux-x86-64.so.2
        ld-linux-x86-64.so.2 (GLIBC_PRIVATE) => /lib64/ld-linux-x86-64.so.2

输出中的第二行显示它动态链接到libc,其中包含malloc

至于可能出现的问题,我可以提出四点建议:

  1. 也许它与正常libc没有关联,但与其他一些C库(例如uclibc)或其他valgrind没有预料到的关联。上述测试将准确显示它与之相关的内容。为了使valgrind起作用,它使用LD_PRELOAD来包装malloc()free()函数(一般函数包装here的描述)。如果您的libc替代品不支持LD_PRELOAD或(某种程度上)C库的malloc()free()根本不被使用(使用这些名称),然后valgrind将不起作用。也许您可以包含构建应用程序时使用的链接行。

  2. 它正在泄漏,但它没有使用malloc()分配内存。例如,它可能(不太可能)对brk()进行自己的调用,或者(更有可能)使用mmap分配内存。您可以使用它来查找(这是cat本身的转储)。

  3. $ cat /proc/PIDNUMBERHERE/maps
    00400000-0040b000 r-xp 00000000 08:01 805303                             /bin/cat
    0060a000-0060b000 r--p 0000a000 08:01 805303                             /bin/cat
    0060b000-0060c000 rw-p 0000b000 08:01 805303                             /bin/cat
    02039000-0205a000 rw-p 00000000 00:00 0                                  [heap]
    7fbc8f418000-7fbc8f6e4000 r--p 00000000 08:01 1179774                    /usr/lib/locale/locale-archive
    7fbc8f6e4000-7fbc8f899000 r-xp 00000000 08:01 1573024                    /lib/x86_64-linux-gnu/libc-2.15.so
    7fbc8f899000-7fbc8fa98000 ---p 001b5000 08:01 1573024                    /lib/x86_64-linux-gnu/libc-2.15.so
    7fbc8fa98000-7fbc8fa9c000 r--p 001b4000 08:01 1573024                    /lib/x86_64-linux-gnu/libc-2.15.so
    7fbc8fa9c000-7fbc8fa9e000 rw-p 001b8000 08:01 1573024                    /lib/x86_64-linux-gnu/libc-2.15.so
    7fbc8fa9e000-7fbc8faa3000 rw-p 00000000 00:00 0
    7fbc8faa3000-7fbc8fac5000 r-xp 00000000 08:01 1594541                    /lib/x86_64-linux-gnu/ld-2.15.so
    7fbc8fca6000-7fbc8fca9000 rw-p 00000000 00:00 0
    7fbc8fcc3000-7fbc8fcc5000 rw-p 00000000 00:00 0
    7fbc8fcc5000-7fbc8fcc6000 r--p 00022000 08:01 1594541                    /lib/x86_64-linux-gnu/ld-2.15.so
    7fbc8fcc6000-7fbc8fcc8000 rw-p 00023000 08:01 1594541                    /lib/x86_64-linux-gnu/ld-2.15.so
    7fffe1674000-7fffe1695000 rw-p 00000000 00:00 0                          [stack]
    7fffe178d000-7fffe178f000 r-xp 00000000 00:00 0                          [vdso]
    ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
    

    请注意[heap]的结束地址是否实际增长,或者您是否看到其他mmap条目。关于valgrind是否正常工作的另一个好的指标是发送SIGSEGV或类似的进程,看看你是否在退出时看到堆正在使用。

    1. 严格来说,它不会泄漏,但它会泄漏到所有意图和目的。例如,它可能具有数据结构(如缓存),随着时间的推移而增长。退出时,程序(正确)释放缓存中的所有条目。因此,在退出时,堆上没有任何东西在使用。在这种情况下,您将想知道正在发展的是什么。这是一个更难的主张。我使用该技术杀死程序(上图),捕获输出并对其进行后处理。如果您在24小时后看到500件事,48小时后看到1000件,在72小时后看到1,500件,这应该可以告诉您什么是“漏水”。但是,正如 haris 在评论中指出的那样,虽然这会导致内存没有显示为泄漏,但它并没有解释总堆使用情况'为零,因为它描述了所做的总分配和释放。

    2. 或许valgrind无法在您的平台上运行。如果您构建一个非常简单的程序,如下所示,并在您的平台上运行valgrind,会发生什么?如果这不起作用,您需要找出 valgrind无法正常运行的原因。请注意,MIPS上的valgrind非常新。 Here是一个电子邮件主题,MIPS和uclibc发现valgrind的开发人员未报告任何分配。他的解决方案是将ntpl替换为linuxthreads

    3. #include <stdio.h>
      #include <stdlib.h>
      int
      main (int argc, char **argv)
      {
        void *p = malloc (100);       /* does not leak */
        void *q = malloc (100);       /* leaks */
        free (p);
        exit (0);
      }
      

答案 1 :(得分:5)

(在OP获得第一笔奖金后,问题本身已经发生了重大变化,添加了另一个答案)

根据我对您编辑的理解,您现在拥有:

  1. 使用valgrind自己的测试程序
  2. 复制问题
  3. 确认测试程序二进制文件动态链接到uclibc
  4. 已确认LD_PRELOAD正在使用您的系统
  5. 确认(如果仅使用测试程序)这不是来自其他图书馆的符号干扰
  6. 对我而言,这表示valgrind有错误或与您的工具链不兼容。我发现引用说它应该适用于你的工具链,所以这意味着我有一个错误。

    因此,我建议您使用here描述的机制报告错误。也许忽略一下你复杂的应用程序,并指出简单的测试程序不起作用。如果您还没有,可以按照here所述尝试用户邮件列表。

答案 2 :(得分:1)

  

为了确认可执行文件没有静态链接,我运行了文件snmpd

你的问题很可能不是二进制是静态链接的(你现在知道它不是),但是mallocfree静态链接到它(也许你正在使用替代的malloc实现,例如tcmalloc?)。

当您构建简单的测试用例(Valgrind正常工作)时,您可能没有使用与实际应用程序相同的链接命令行(和相同的库)。

无论如何,检查是非常简单的:

readelf -Ws snmpd | grep ' malloc'

如果显示UND(即未定义),Valgrind可以毫无困难地拦截它。但有可能会显示FUNC GLOBAL DEFAULT ... malloc,这意味着就valgrind而言,snmpd 与静态链接一样好。

假设我的猜测是正确的,请使用snmpd标记重新链接-Wl,-y,malloc。这将告诉您哪个库定义了您的malloc。将其从链接中删除,找到并修复泄漏,然后确定是否值得为它带来麻烦。