gpt4 book ai didi

c - 为什么 valgrind 报告 glibc tsearch() 随机泄漏内存?

转载 作者:行者123 更新时间:2023-12-01 12:09:52 26 4
gpt4 key购买 nike

我正在使用 glibc tsearch()用于在示例程序中存储动态分配的数据 block 的 API 系列。

我发现当我使用 tsearch() 将几个 malloc()ed block 添加到树中时,valgrind 报告其中一些 block “可能已丢失” ”。虽然“可能丢失”与“肯定丢失”并不完全相同,previous SO advice一般是调查是什么导致了这些。

我的示例程序如下:

#include <stdio.h>
#include <search.h>
#include <stdlib.h>
#include <signal.h>

struct data {
int id;
char *str;
};

static int
compare (const void *a, const void *b) {
const struct data *data_a = a, *data_b = b;

if (data_a->id < data_b->id) {
return -1;
} else if (data_a->id > data_b->id) {
return 1;
} else {
return 0;
}
}

int main (int argc, char **argv) {
void *tree = NULL;
struct data *d1, *d2, *d3, *d4;

d1 = malloc(sizeof(struct data));
d1->id = 10;
d1->str = "Hello";
tsearch(d1, &tree, compare);

d2 = malloc(sizeof(struct data));
d2->id = 30;
d2->str = "Goodbye";
tsearch(d2, &tree, compare);

d3 = malloc(sizeof(struct data));
d3->id = 20;
d3->str = "Thanks";
tsearch(d3, &tree, compare);

d4 = malloc(sizeof(struct data));
d4->id = 40;
d4->str = "OK";
tsearch(d4, &tree, compare);

raise(SIGINT);

return 0;
}

请注意,我在 main() 的末尾调用了 raise(SIGINT),这样 valgrind 仍然能够在隐式分配 block 之前分析它们 free()d.

我在valgrind下编译运行如下:

$ gcc ts.c -o ts
$ valgrind --leak-check=full ./ts
==2091== Memcheck, a memory error detector
==2091== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2091== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==2091== Command: ./ts
==2091==
==2091==
==2091== Process terminating with default action of signal 2 (SIGINT)
==2091== at 0x4E7AE97: raise (raise.c:51)
==2091== by 0x1088CE: main (in /home/ubuntu/ts)
==2091==
==2091== HEAP SUMMARY:
==2091== in use at exit: 160 bytes in 8 blocks
==2091== total heap usage: 8 allocs, 0 frees, 160 bytes allocated
==2091==
==2091== 24 bytes in 1 blocks are possibly lost in loss record 8 of 8
==2091== at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2091== by 0x4F59483: tsearch (tsearch.c:338)
==2091== by 0x108801: main (in /home/ubuntu/ts)
==2091==
==2091== LEAK SUMMARY:
==2091== definitely lost: 0 bytes in 0 blocks
==2091== indirectly lost: 0 bytes in 0 blocks
==2091== possibly lost: 24 bytes in 1 blocks
==2091== still reachable: 136 bytes in 7 blocks
==2091== suppressed: 0 bytes in 0 blocks
==2091== Reachable blocks (those to which a pointer was found) are not shown.
==2091== To see them, rerun with: --leak-check=full --show-leak-kinds=all
==2091==
==2091== For counts of detected and suppressed errors, rerun with: -v
==2091== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

$

Valgrind 报告丢失了一个 24 字节的 block 。如果我将 raise(SIGINT) 移动到 d4 分配和树添加之前,则不会报告任何 block 丢失。

为什么添加 4 个 block 时丢失一个 block ,即使添加 3 个 block 时没有丢失?

最佳答案

事实证明,glibc tsearch() 实现有点顽皮,可以在指向它存储在二叉搜索树中的 block 的指针中旋转低位。它使用指针中的低位来存储标志: https://code.woboq.org/userspace/glibc/misc/tsearch.c.html#341

具体来说,实现使用这些宏来设置或清除低位指针位以分别将 block 标记为红色或黑色:

#define SETRED(N) (N)->left_node |= ((uintptr_t) 0x1)
#define SETBLACK(N) (N)->left_node &= ~((uintptr_t) 0x1)

指针有效递增,这使得 valgrind 认为这些有效指针(存储在 tsearch() 树中)已经是 overwritten and therefore possibly lost .但这些不是丢失的 block ——它们成功地存储在二叉搜索树中,取低位为模。 tsearch() 将在访问树时对这些位进行必要的屏蔽。 tsearch() 可以做到这一点,因为 malloc()ed block 通常至少对齐到偶数地址。

只有二叉树中标记为“红色”节点的 block 才设置此位,因此其模式是否“可能丢失”完全取决于实现如何将 block 分类为“红色”或“black"在添加、删除和重新平衡操作期间。

所以 tsearch() 的位操作使得 valgrind 错误地认为这些 block 丢失了。在这种情况下,valgrind 报告误报。

关于c - 为什么 valgrind 报告 glibc tsearch() 随机泄漏内存?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52731193/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com