Hatena::ブログ(Diary)

shouhの日記

2015-01-18

Fake Function Framework

Fake Function Framework(FFF)
https://github.com/meekrosoft/fff

C言語用のモックツール
特定の関数をフェイク関数に置き換えることができる。

とはいっても、ヘッダ等に宣言されてる関数の実体を、FFFの機構で作ってくれるというだけで、既に存在する関数を置き換えられるわけじゃない。

  • 内部的には所詮、指定された関数名から、マクロを上手く使って普通に関数を定義してるだけ
    • mallocなど組み込み関数に対して使えば識別子エラー
    • 既存関数に対して使えば多重定義エラー

要するに「ヘッダはあるけど中身がない」場合のモックとして使える。

特徴

  • 準備するものは fff.h のヘッダ一つのみ
    • 導入が超ラクチン

その他

2014-04-26

Linuxでメモリ枯渇をテストするには

メモリが足りない場合に malloc を実行すると NULL が返ってくるとか、new を実行すると std::bad_alloc 例外が投げられるとか、そういったメモリ枯渇時のテストを行いたい時がある。いくつか方法があるようなのでメモ。

デバッガで無理矢理通す

デバッガにステップ実行と変数の値を変更する機能があれば、無理矢理通すことができる。

ptr = malloc(…); 
if(ptr == NULL){ // ここに入る前に, デバッガで ptr の値を NULL にする
	…
}

メモリ枯渇時のテストはレアケースだし、自動化するのも難しいので、無理に自動化にこだわらなくてもいいなら、このように手動でサクっとやってしまうのが楽。

(失敗)仮想メモリの上限サイズを落とし, ループ+malloc(or new)でメモリを枯渇させる

メモリ枯渇を起こしたいなら、実際に枯渇させてしまえばいい。

malloc(or new)を、ループの中から、失敗するまで呼び出せばいい。その際、malloc で確保するサイズは n 固定ではなく、n で失敗したら次は n/2 、n/2 で失敗したら次は n/4 …というふうにする。これを n=1 になるまで繰り返せば、確実に枯渇できる。

n=6 としたら、イメージはこんな感じ(フォントがずれるかも)。まずは n 固定の場合。

<-------空きメモリ---------->
oooooo
      oooooo
            oooooo
                  oooooo
                        ~~~~~ n=6 だとここは確保できない

続いて n を減らしていく場合。

<-------空きメモリ---------->
oooooo
      oooooo
            oooooo
                  oooooo
                        ooo    n=6 で確保できなかったら n=3 で.
                           o   n=3 で確保できなかったから n=1(3/2の切り捨て) で.
                            o
                             これなら全部確保できる

しかし純粋に空きメモリを全部食っていたのでは時間がかかる。Linux ではプロセスが使用できるメモリを抑止できるので、これを使う。

  • ulimit -v コマンド
  • setrlimit 関数

これでメモリ枯渇時のテストができそうだが、実はここからが難しい。というよりたぶん無理。

空きメモリを限界まで食ってしまうと、テストで通したい部分に至る前に、別の部分でエラーが出てしまうからだ。Segmentation fault なんかよく起こる。

hoge 関数malloc 部分をテストしたい場合を例にすると、以下のようなイメージ。

void hoge(){
	…
	…                // 下手にメモリ枯渇させると, このへんのどこかで// エラーが出てしまい, 通したい部分まで行かない.
	…
	ptr = malloc(…); // ここに来た時に, 空きメモリがちょうど
	                  // このmallocが失敗する程度だけ残っている必要がある
	if(ptr == NULL){
		…
	}
	…
}

無事 ptr = malloc(…); までたどり着くことができ、かつそこでメモリ確保に失敗する…という絶妙の空きメモリを準備しなければならないということだ。適当にメモリを食っていたのではまず当たらない。当たらなかった。

何か良い方法はないんでしょうかね。

preload する

※先輩が試していたコードをちらっと眺めただけなのでうろ覚え
Linux には LD_PRELOAD という環境変数がある。ここに共有ライブラリを指定すると、既存の関数の動作を上書きすることができる。

早い話、必ず NULL を返す malloc 関数や、必ず std::bad_alloc 例外を返す new 関数を実装した共有ライブラリを準備し、LD_PRELOAD で preload してやればよい。

ただし、ただ preload するだけだと、通常の malloc や new が動作しなくなるので、一工夫必要。グローバル変数フラグを導入して、フラグが立っている時だけ NULL や std::bad_alloc を返すようにするとか。