2009-11-10 187 views
8

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

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

,但在某些情况下,你必须比死其他的选择,因为你已经分配给高速缓存或任何一些额外的东西,你可以回收该内存。

但是,在那些您可以尝试从失败的malloc()中恢复的情况下,您在代码路径中做了一些棘手且容易出错的错误,这使得测试变得尤为重要。你如何真的去做这件事?

+2

您可以劫持'malloc()'并使其有时返回0。 – 2009-11-10 21:08:57

+2

当进程内存不足时,许多库函数(如'printf')可能会失败。 – ephemient 2009-11-10 21:19:00

+0

@ephemient没关系,如果'fprintf()'正确处理。 ;-) – 2018-01-08 15:14:32

回答

15

我看到了一个很酷的解决了这个问题这是由S. Paavolainen呈现在我面前。我们的想法是要覆盖标准malloc(),你可以做的只是在链接,通过自定义分配器,其

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

然后你只运行单元测试很多次:该系统通过不同的控制路径可自动列出来malloc()故障,比如更高效和可靠随机测试。

+0

_Nice_答案。不依赖于机会来发现问题,并让您系统地测试分配失败的后果。 – quark 2009-11-12 21:00:38

+1

+1,用于完全覆盖随机测试。 SQLite和这个http://www.sqlite.org/malloc.html#testing类似。使用计数器触发malloc()失败,而不是检查堆栈的唯一性。 – 2011-08-08 19:08:45

+0

关于此的理论是坚实的,我想实现这一点。你的答案并没有真正的实现细节,并且对S. Paavolainen malloc的搜索也没有提供任何东西。是否有可能提供一些实施细节? – 2014-05-22 02:34:30

1

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

也期待here一组很好的工具,你好像我的意见需要。至少他们重载malloc()/ free()来跟踪泄漏,所以它看起来可用点来添加任何你想要的。

1

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

2

我建议创建你期望可能会失败你的特殊的malloc代码中的特定功能,你可以从容地处理。例如:

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

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

+2

/*做些狡猾的事*/ /* ???? */ /*盈利! */ – Derek 2009-11-10 21:31:42

2

写自己的库,通过随机失败或调用真正的malloc(无论是staticly链接或明确dlopened)实现的malloc

然后LD_PRELOAD它

+0

我结束了这个方法。它需要一些额外的编译标志用于我的测试框架,但它非常灵活。另外,在我的共享对象库中,而不是让malloc随机失败,我声明了一个全局值,当我指定时会使malloc失败。我的测试代码中必须将该变量声明为'extern'。 – 2014-05-23 05:46:44

1

你可以通过使用一些定义和全局参数劫持的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; 
}