2009-11-10 31 views
8

ユニットパスのテストでエラーが発生した場合の最良の方法は、malloc()ですか?あなたは失敗した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を返すことができます。 –

+2

'printf'のような多くのライブラリ関数は、プロセスのメモリが不足していると失敗する可能性があります。 – ephemient

+0

@ephemient 'fprintf()'が正しく処理しても大丈夫です。 ;-) –

答えて

15

私はS. Paavolainenによって私に提示されたこの問題のクールな解決策を見ました。アイデアは

  1. は、スタックがデータベースに存在する場合malloc()
  2. チェックを呼び出したスレッドの現在の実行スタックを読み込み、カスタムアロケータで、あなただけのリンカで行うことができます標準malloc()をオーバーライドすることですそれは、ハードディスク
    1. スタックが存在しない場合に保存されている、データベースにスタックを追加し、スタックがすでに存在しなかった場合NULL
    2. を返し、正常にメモリを割り当て、
    3. を返します。

その後、あなたは自分のユニットテストを何度も実行します。このシステムは自動的malloc()失敗に異なる制御経路を通って列挙し、はるかに効率的で信頼性の高い、例えば以下でありますランダムテスト。

+0

_Nice_答え。問題を見つけるチャンスに頼るのではなく、割り当て失敗の結果を体系的にテストすることができます。 – quark

+1

+1ランダムテストよりも完全なカバレッジ。 SQLiteは、このhttp://www.sqlite.org/malloc.html#testingに似た何かを行います。 malloc()の失敗は、スタックの一意性をチェックするのではなく、カウンタを使用してトリガされます。 –

+0

これに関する理論は堅実であり、私はこれを実装したいと思います。あなたの答えは実際に実装の詳細を持っていなかったし、S. Paavolainen mallocの検索でも何も出てこなかった。実装の詳細をいくつか提供することは可能でしょうか? –

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

/*何か工夫してください*/ /* */ /* PROFIT! */ – Derek

2

がランダムに(どちらか静的にリンクまたは明示的にdlopenさ)失敗したり、実際のmalloc関数を呼び出すことによって、malloc関数を実装して独自のライブラリを作成し

は、それが

+0

私はこの方法に行きました。私のテストフレームワークで使用するには、コンパイルフラグをいくつか追加する必要がありますが、非常に柔軟です。また、mallocをランダムに失敗させるのではなく、共有オブジェクトライブラリで、指定したときにmallocが失敗するグローバル値を宣言しました。その変数はテストコード内で「extern」として宣言されなければなりませんでした。 –

1

あなたは、いくつかの定義とのグローバルパラメータを使用してmalloc関数を乗っ取ることができLD_PRELOADそれを制御する...それは少しハッキリですが、動作するようです。

#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; 
} 
関連する問題