失败的 malloc() 的单元测试

发布于 2024-08-10 22:54:52 字数 417 浏览 5 评论 0原文

对涉及失败的 malloc() 的单元测试代码路径的最佳方法是什么?在大多数情况下,这可能并不重要,因为你正在做类似的事情

thingy *my_thingy = malloc(sizeof(thingy));
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
} 

,但在某些情况下,你除了死亡之外还有选择,因为你已经分配了一些额外的东西用于缓存或其他什么,并且你可以回收该内存。

然而,在您可以尝试从失败的 malloc() 中恢复的情况下,您正在代码路径中执行一些非常棘手且容易出错的事情,这非常不寻常,因此测试尤其重要。你实际上是如何去做这件事的?

What is the best way for unit testing code paths involving a failed malloc()? In most instances, it probably doesn't matter because you're doing something like

thingy *my_thingy = malloc(sizeof(thingy));
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
} 

but in some instances you have choices other than dying, because you've allocated some extra stuff for caching or whatever, and you can reclaim that memory.

However, in those instances where you can try to recover from a failed malloc() that you're doing something tricky and error prone in a code path that's pretty unusual, making testing especially important. How do you actually go about doing this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

苦笑流年记忆 2024-08-17 22:54:52

我看到了 S. Paavolainen 向我提出的这个问题的一个很酷的解决方案。这个想法是通过自定义分配器来覆盖标准的 malloc() ,您可以在链接器中执行此操作,该分配器

  1. 读取调用 malloc() 的线程的当前执行堆栈>
  2. 检查堆栈是否存在于存储在硬盘上的数据库中
    1. 如果堆栈不存在,则将堆栈添加到数据库并返回NULL
    2. 如果堆栈已经存在,则正常分配内存并返回

然后您只需多次运行单元测试:系统自动枚举 malloc() 失败的不同控制路径,并且比随机测试等更加高效和可靠。

I saw a cool solution to this problem which was presented to me by S. Paavolainen. The idea is to override the standard malloc(), which you can do just in the linker, by a custom allocator which

  1. reads the current execution stack of the thread calling malloc()
  2. checks if the stack exists in a database that is stored on hard disk
    1. if the stack does not exist, adds the stack to the database and returns NULL
    2. if the stack did exist already, allocates memory normally and returns

Then you just run your unit test many times: this system automatically enumerates through different control paths to malloc() failure and is much more efficient and reliable than e.g. random testing.

飞烟轻若梦 2024-08-17 22:54:52

我建议为您的特殊 malloc 代码创建一个特定的函数,您预计该函数可能会失败,但您可以优雅地处理。例如:

void* special_malloc(size_t bytes) {
  void* ptr = malloc(bytes);
  if(ptr == NULL) {
    /* Do something crafty */
  } else {
    return ptr;
  }
}

然后您可以通过传递一些错误的字节值来对这个狡猾的业务进行单元测试。您可以将其放入一个单独的库中,并创建一个模拟库,该模拟库对于测试调用该库的函数具有特殊的行为。

I suggest creating a specific function for your special malloc code that you expect could fail and you could handle gracefully. For example:

void* special_malloc(size_t bytes) {
  void* ptr = malloc(bytes);
  if(ptr == NULL) {
    /* Do something crafty */
  } else {
    return ptr;
  }
}

Then you could unit-test this crafty business in here by passing in some bad values for bytes. You could put this in a separate library and make a mock-library that does behaves special for your testing of the functions which call this one.

剧终人散尽 2024-08-17 22:54:52

这有点恶心,但如果你真的想要单元测试,你可以使用 #ifdefs 来完成:

thingy *my_thingy = malloc(sizeof(thingy));
#ifdef MALLOC_UNIT_TEST_1
my_thingy = NULL;
#endif
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
}

不幸的是,你必须使用这个解决方案重新编译很多东西。

如果您使用的是 Linux,您还可以考虑使用 ulimit,但要小心。

This is a kinda gross, but if you really want unit testing, you could do it with #ifdefs:

thingy *my_thingy = malloc(sizeof(thingy));
#ifdef MALLOC_UNIT_TEST_1
my_thingy = NULL;
#endif
if (my_thingy == NULL) {
  fprintf(stderr, "We're so screwed!\n");
  exit(EXIT_FAILURE);
}

Unfortunately, you'd have to recompile a lot with this solution.

If you're using linux, you could also consider running your code under memory pressure by using ulimit, but be careful.

感受沵的脚步 2024-08-17 22:54:52

编写自己的库,通过随机失败或调用真正的 malloc(静态链接或显式 dlopened)来实现 malloc,

然后 LD_PRELOAD 它

write your own library that implements malloc by randomly failing or calling the real malloc (either staticly linked or explicitly dlopened)

then LD_PRELOAD it

漆黑的白昼 2024-08-17 22:54:52

在 FreeBSD 中,我曾经简单地重载了 C 库 malloc.o 模块(那里的符号很弱),并将 malloc() 实现替换为可控制失败概率的实现。
所以我静态链接并开始进行测试。 srandom() 以受控的伪随机序列完成图片。

另外,请在此处查找我认为您似乎需要的一组好工具。至少它们重载了 malloc() / free() 来跟踪泄漏,因此它似乎是添加您想要的任何内容的可用点。

In FreeBSD I once simply overloaded C library malloc.o module (symbols there were weak) and replaced malloc() implementation with one which had controlled probability to fail.
So I linked statically and started to perform testing. srandom() finished the picture with controlled pseudo-random sequence.

Also look here for a set of good tools that you seems to need by my opinion. At least they overload malloc() / free() to track leaks so it seems as usable point to add anything you want.

空名 2024-08-17 22:54:52

您可以通过使用一些定义和全局参数来控制它来劫持 malloc...这有点 hackish,但似乎可行。

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

#define malloc(x) fake_malloc(x)

struct {
  size_t last_request;
  int should_fail;
  void *(*real_malloc)(size_t);
} fake_malloc_params;

void *fake_malloc(size_t size) {
  fake_malloc_params.last_request = size;
  if (fake_malloc_params.should_fail) {
    return NULL;
  }
  return (fake_malloc_params.real_malloc)(size);;
}

int main(void) {
  fake_malloc_params.real_malloc = malloc;
  void *ptr = NULL;
  ptr = malloc(1);
  printf("last: %d\n", (int) fake_malloc_params.last_request);
  printf("ptr: 0x%p\n", ptr);
  return 0;
}

You could hijack malloc by using some defines and global parameter to control it... It's a bit hackish but seems to work.

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

#define malloc(x) fake_malloc(x)

struct {
  size_t last_request;
  int should_fail;
  void *(*real_malloc)(size_t);
} fake_malloc_params;

void *fake_malloc(size_t size) {
  fake_malloc_params.last_request = size;
  if (fake_malloc_params.should_fail) {
    return NULL;
  }
  return (fake_malloc_params.real_malloc)(size);;
}

int main(void) {
  fake_malloc_params.real_malloc = malloc;
  void *ptr = NULL;
  ptr = malloc(1);
  printf("last: %d\n", (int) fake_malloc_params.last_request);
  printf("ptr: 0x%p\n", ptr);
  return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文