c - MIPS 上的 Valgrind 报告没有堆使用

标签 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 v0.9.29)的 uclibc-gcc 工具链,以闪存到运行 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 上有以下内容:

If your program is statically linked, most Valgrind tools will only work well if they are able to replace certain functions, such as malloc, with their own versions. By default, statically linked malloc functions are not replaced. A key indicator of this is if Memcheck says "All heap blocks were freed -- no leaks are possible".

以上听起来和我的问题完全一样,所以我检查它是否动态链接到包含 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 可执行文件。)根据我的理解,mallocfree 将在这些库之一中,它们似乎是动态链接的。我还在可执行文件上执行了 readelfnm 以确认对 mallocfree 的引用未定义(这是动态链接可执行文件的特征)。

此外,我尝试按照常见问题解答的建议使用 --soname-synonyms=somalloc=NONE 选项启动 Valgrind。

LD_PRELOAD 支持吗?

正如评论者和回答者所指出的,Valgrind 取决于 LD_PRELOAD 的使用。有人建议我的工具链不支持此功能。为了确认确实如此,我关注了 this example创建一个简单的测试库并加载它(我将 rand() 替换为仅返回 42 的函数)。测试成功了,所以看起来我的目标支持 LD_PRELOAD 就好了。

Sprite 数据

我还将包含一些来自 readelf 命令的信息,这些信息可能会有用。我没有进行大量转储,而是将内容精简为仅包含可能相关的内容。

Dynamic section
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libnetsnmpagent.so.30]
 0x00000001 (NEEDED)                     Shared library: [libnetsnmpmibs.so.30]
 0x00000001 (NEEDED)                     Shared library: [libnetsnmp.so.30]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libc.so.0]
 0x0000000f (RPATH)                      Library rpath: [//lib]

Symbol table '.dynsym'
   Num:    Value  Size Type    Bind   Vis      Ndx Name
    27: 00404a40     0 FUNC    GLOBAL DEFAULT  UND free
    97: 00404690     0 FUNC    GLOBAL DEFAULT  UND malloc

最佳答案

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

$ 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 本身的转储)。

.

$ 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 小时后看到 1,000 个东西,在 72 小时后看到 1,500 个东西,这应该表明什么是“泄漏”。然而,正如 haris 在评论中指出的那样,虽然这会导致内存未显示为泄漏,但它并不能解释“总堆使用量”为零,因为这描述了总分配制造和释放。

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

.

#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);
}

关于c - MIPS 上的 Valgrind 报告没有堆使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26125494/

相关文章:

c++ - 计算矩阵的行列式时内存泄漏

c - 是否可以将字符 ( * ) 放入声明为 int a[10][10] 的二维数组中?

java - 确定 Android 上的可用内存

C 基于定界符拆分 char 数组,但它因连续定界符而失败

c++ - 转换已定义(已知)内存的邻居内存

C++动态加载类: Why is a "destroy" function needed?

ios - React Native ios 应用程序中的 "libBacktraceRecording.dylib"内存泄漏

c++ - 在类的构造函数中初始化映射时如何避免内存泄漏?

c - 循环显示菜单永无止境

c - 查找与正则表达式匹配的子字符串