160 likes | 306 Views
メモリ関連エラーの追及 , 性能プロファイル. メモリ関連エラー. 空間軸上の間違い 領域オーバーフロー N バイト割り当てて , N バイト目以降 (N バイト目を含む ) を触る ポインタの初期化し忘れ , ポインタ変数の破壊 時間軸上の間違い 早すぎる解放 (free) 解放し忘れ ( リーク ). 領域オーバーフローの例. char * a = (char *)malloc(N); a[N] = …; char a[N]; a[N] = …;
E N D
メモリ関連エラー • 空間軸上の間違い • 領域オーバーフロー • Nバイト割り当てて, Nバイト目以降(Nバイト目を含む)を触る • ポインタの初期化し忘れ, ポインタ変数の破壊 • 時間軸上の間違い • 早すぎる解放(free) • 解放し忘れ(リーク)
領域オーバーフローの例 • char * a = (char *)malloc(N); a[N] = …; • char a[N]; a[N] = …; • typedef struct foo { … } foo, * foo_t;foo_t = (foo_t)malloc(sizeof(foo_t)); • char * s_copy = (char *)malloc(strlen(s));strcpy(s_copy, s);
おきていること • mallocした領域の「ちょっと後ろ」に書き込む(必ず「ちょっと」かどうかはわからないが, 割と多くの場合「ちょっと」)
典型的症状 • そこに何があるかで挙動は千差万別だが… • mallocの場合, malloc自身の管理領域, 別のmallocで割り当てた領域 • 将来のmalloc/free中にsegmentation fault • 別の, mallocした領域を破壊 • 局所配列の場合, 別の変数 • 別の変数を破壊 • それがポインタなら将来そのポインタを通じてメモリをアクセスしたときにsegmentation fault • 「大幅に」はみ出せばアクセス不能領域 • segmentation fault
ポインタの初期化し忘れ, 破壊 • char * a; a[0] = … • foo * a = mk_foo();…foo * mk_foo() { returnし忘れ; } • 症状: (aを通じてメモリをアクセスした際), まったくでたらめなアドレスを参照しにいくことになる(segmentation fault, 変数の破壊)
はやすぎる解放 • char * a = (char *) malloc(…);free(a);a[0] = … • 解放後, その領域が別のmallocによって割り当てられ, その後aを通じてその領域に書き込んだ際, その領域を破壊
メモリ関連エラーの厄介な点 • 間違った際の挙動は「不定」 • 領域はみ出し, 初期化し忘れなどで何が起こるかは, プログラマの知らないmallocやスタックなどのmemoryレイアウトで決まる • 症状 • 最適化を外したら「なぜか」動いた • このメモリを1バイト余計に割り当てたら「なぜか」動いた(これで正しい場合もあるが) • 間違ってもすぐに症状が現れない • 破壊した領域を使ったときに最終的な症状が現れる
メモリ関連エラーの回避・デバッグツール • GC • 時間軸方向のエラーをなくす(freeを呼ばなくてよい) • 空間軸方向のエラーには無力 • MALLOC_CHECK_, valgrind • 空間軸方向のエラーの一部を検出
MALLOC_CHECK_ • 朗報: 最近のLinuxのdefaultのmallocに組み込まれている(installの必要なし) • 動作: • mallocした領域を「ちょっと」踏み越えたアクセスを検出 • いつ? malloc/free時に検出 • 環境変数MALLOC_CHECK_によって動作を変えられる(cf. man malloc)
効果的な使い方 • エラーが出るようになったら, -g でコンパイルし, gdbで走らせる • エラーがおきるとabort関数を呼び, gdb内で実行が停止する • gdbのupコマンドでエラーが起きた場所を突き止める
制限 • エラーがチェックされるのはmalloc/free時のみ • 「領域はみ出しアクセス」を行った瞬間ではない • 基本はmallocの管理領域の破壊を検出しているだけなので, malloc以外のメモリ領域のはみ出しには無力 • mallocに限ってもすべての領域はみ出しが検出されるわけではなく, 実際カバー率は低い
valgrind • (Ubuntu) apt-get install valgrind • それ以外の場合, Googleで検索 • -gでコンパイルしたプログラムを, valgrind <コマンド>で実行するだけ
valgrind • プログラムをその場で書き換えて実行する(個々のメモリアクセスを監視)ため, • 「領域はみ出しの瞬間」を検出できる • 検出力(カバー率)が高い • 制限: • おそい
そのほか • 常に最大限の警告を出すオプション(gccであれば –Wall)をつけてコンパイル • 警告を無視しない
性能プロファイル • gcc –pg … (コンパイル時とリンク時の両方) • ./minipy … (実行) • “gmon.out”というファイルが出来る • gprof minipy で結果を表示