2009-11-10 240 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; 
}