失败malloc()的单元测试
对涉及失败的失败malloc()的单元测试,c,unit-testing,memory-management,libc,C,Unit Testing,Memory Management,Libc,对涉及失败的malloc()的代码路径进行单元测试的最佳方法是什么?在大多数情况下,这可能并不重要,因为你正在做类似的事情 thingy *my_thingy = malloc(sizeof(thingy)); if (my_thingy == NULL) { fprintf(stderr, "We're so screwed!\n"); exit(EXIT_FAILURE); } 但在某些情况下,除了死亡,你还有其他选择,因为你已经分配了一些额外的东西用于缓存或其他什么,你可以回收
malloc()
的代码路径进行单元测试的最佳方法是什么?在大多数情况下,这可能并不重要,因为你正在做类似的事情
thingy *my_thingy = malloc(sizeof(thingy));
if (my_thingy == NULL) {
fprintf(stderr, "We're so screwed!\n");
exit(EXIT_FAILURE);
}
但在某些情况下,除了死亡,你还有其他选择,因为你已经分配了一些额外的东西用于缓存或其他什么,你可以回收这些内存
然而,在那些您可以尝试从失败的
malloc()
中恢复的实例中,您在一个非常不寻常的代码路径中执行一些棘手且容易出错的操作,这使得测试特别重要。你到底是怎么做的 在FreeBSD中,我曾经简单地重载了C库malloc.o模块(符号很弱),并用一个具有控制失败概率的实现替换了malloc()实现。
所以我静态链接并开始执行测试。srandom()使用受控伪随机序列完成图片
另外,根据我的意见,还需要一套好的工具。至少它们会重载malloc()/free()来跟踪泄漏,因此它似乎是添加任何您想要的内容的可用点。这有点恶心,但如果您确实想要单元测试,您可以使用#ifdef: 不幸的是,您必须使用此解决方案重新编译很多内容
如果你使用Linux,你也可以考虑通过使用内存压力来运行代码,但是要小心。
< P>我看到了S. Paavolainen提出的这个问题的一个很酷的解决方案。其思想是通过一个定制的分配器覆盖标准的malloc()
,您只需在链接器中就可以做到这一点
NULL
然后您只需多次运行单元测试:此系统通过不同的控制路径自动枚举到
malloc()
失败,并且比随机测试更有效、更可靠。我建议为您的特殊malloc代码创建一个特定的函数,您希望它可能会失败,并且您可以优雅地处理它。例如:
void* special_malloc(size_t bytes) {
void* ptr = malloc(bytes);
if(ptr == NULL) {
/* Do something crafty */
} else {
return ptr;
}
}
然后,您可以在这里通过为字节传递一些坏值来单元测试这个狡猾的业务。您可以将其放在一个单独的库中,并创建一个模拟库,该模拟库的行为对于您测试调用此函数的函数来说非常特殊。编写您自己的库,通过随机失败或调用真正的malloc(静态链接或显式dlopened)来实现malloc
然后LD_预加载它您可以通过使用一些定义和全局参数来控制它来劫持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;
}
#包括
#包括
#定义malloc(x)fake_malloc(x)
结构{
上次请求的大小;
int应该失败;
空*(*真实的马洛克)(尺寸);
}假参数;
void*fake\u malloc(尺寸){
假_malloc_params.last_request=大小;
如果(如果失败,则为假参数){
返回NULL;
}
返回值(假参数、真参数、大小);;
}
内部主(空){
伪_malloc_params.real_malloc=malloc;
void*ptr=NULL;
ptr=malloc(1);
printf(“最后一个:%d\n”,(int)假参数(最后一个请求);
printf(“ptr:0x%p\n”,ptr);
返回0;
}
您可以劫持malloc()
并使其有时返回0。许多库函数,如printf
,在进程内存不足时可能会失败。@如果fprintf()
处理正确,请说明一切正常。;-)回答得好。不依赖偶然发现问题,让您系统地测试分配失败的后果。+1表示完全覆盖随机测试。SQLite做了类似的事情。malloc()失败是使用计数器触发的,而不是检查堆栈的唯一性。这方面的理论是可靠的,我想实现这一点。您的回答没有具体的实施细节,搜索S.Paavolainen malloc也没有发现任何问题。可以提供一些实现细节吗?我制作了一个共享库来实现这种行为。只要调试符号可用,就可以使用它,而无需通过LD_预加载修改可执行文件。我最终选择了这种方法。我的测试框架需要使用一些额外的编译标志,但它非常灵活。另外,在我的共享对象库中,我没有让malloc随机失败,而是声明了一个全局值,当我指定时,它将使malloc失败。在我的测试代码中,该变量必须声明为extern
。
#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;
}