失败的malloc()的unit testing

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

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

但在某些情况下,除了死亡之外你还有其他选择,因为你已经为缓存或其他任何东西分配了一些额外的东西,你可以收回那些记忆。

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

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

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

然后,您只需多次运行unit testing:此系统自动枚举通过malloc()故障的不同控制路径,并且比例如随机测试更有效和可靠。

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

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

然后你可以通过传入一些错误的字节值来对这个狡猾的业务进行unit testing。 你可以把它放在一个单独的库中,然后创建一个模拟库,它对你调用这个函数的函数的测试有特殊的作用。

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

然后LD_PRELOAD吧

这有点粗,但是如果你真的想进行unit testing,可以用#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在内存压力下运行你的代码,但要小心。

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

还可以在这里查看一些您认为需要的好工具。 至少他们重载malloc()/ free()来跟踪泄漏,所以它似乎可以添加你想要的任何东西。

你可以通过使用一些定义和全局参数来劫持malloc来控制它…它有点hackish但似乎工作。

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