9

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

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

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

但是,在那些您可以尝试从失败malloc()中恢复的情况下,您在非常不寻常的代码路径中做一些棘手且容易出错的事情,这使得测试变得尤为重要。你实际上是怎么做的?

4

6 回答 6

16

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

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

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

于 2009-11-10T21:17:45.860 回答
2

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

然后 LD_PRELOAD 它

于 2009-11-10T21:59:47.600 回答
2

我建议为您的特殊 malloc 代码创建一个特定的函数,您期望它可能会失败并且您可以优雅地处理。例如:

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

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

于 2009-11-10T21:21:53.253 回答
2

这有点恶心,但如果你真的想要单元测试,你可以用#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在内存压力下运行代码,但要小心。

于 2009-11-10T21:17:18.460 回答
1

你可以通过使用一些定义和全局参数来劫持 malloc 来控制它......这有点骇人听闻但似乎有效。

#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;
}
于 2009-11-10T22:18:50.063 回答
1

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

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

于 2009-11-10T21:16:42.580 回答