Valgrindの結果の見方、日本語訳、など役に立つことまとめ
勉強がてらメモ。
適宜更新していこう。うん。
Valgrindって?
デバッグやプロファイラなどの複数のツールから構成されるツール群の総称。
メモリリークチェックツールとして有名だと思いますが、
それはValgrindツール群の中のMemcheckというツールによる機能だったのですね。
どんなツールがあるのか?マニュアルを見ると・・・
Memcheck |
|
Cachegrind |
プログラムのキャッシュグラフ、または分岐予測をしてシミュレートする。プログラムを速くするために使うツール。 |
Callgrind |
関数呼び出しをトレースしたりできる。Cachegrindをかぶる部分もあるが、Cachegrindとは別の情報も収集できる。 |
Helgrind |
マルチスレッドのプログラム向け。スレッドエラーを検出できる。 |
DRD |
こちらもマルチスレッドのエラー検出ができる。Helgrindと似ているがアプローチが少々違うようでHelgrindとは異なる問題を検出できるそうな。 |
Massif |
ヒーププロファイラ。メモリ使用量を抑えたい人向け。 |
DHAT |
ヒーププロファイラ。メモリ確保したブロックのライフタイムなどの問題を検出するものらしい。 |
SGcheck |
実験段階のツール。機能はMemcheckと同じだけど、こちらはスタック領域やグローバル領域の配列などに適用できるらしい!使えそう。 |
BBV |
実験段階のツール。基本ブロックベクター生成ツール(なにそれ) |
あと2つあまり使われないツールがあるらしい。
Lackey |
ツールのサンプルコード |
Nulgrind |
ベンチマークやサンプル向けのもの |
筆者はMemcheckくらいしか使ってことがないので
いまいち概要を読んでもピンときませんがなんだか使えそうな気がしますね!
このエントリでは主にデフォルトのオプションであるMemcheckを使っていこうと思います。
Valgrindのインストール
筆者のシステムはCentOS 6.6。
Valgrindが入っていない場合は,yumを使ってインストールしましょう。
yum -y install valgrind
インターネットに接続できないけどCentOS 6のDVDがある人は
以下のエントリを参考にしてDVDメディアをyumのリポジトリに追加しましょう。
便利ですよ。シェルスクリプトをコピペして実行するだけ!1分で終わるよ!
http://d.hatena.ne.jp/taiyakisun/20141201/1417452753
で、以下を実行するとDVDメディアからvalgrindがインストールされます。
yum --disablerepo=\* --enablerepo=centos-media -y install valgrind
結果の見方など
実行するときは基本的にコレ。
valgrind --leak-check=full プログラム プログラムの引数
結果の例はこちら。
==25832== Invalid read of size 4 ==25832== at 0x8048724: BandMatrix::ReSize(int, int, int) (bogon.cpp:45) ==25832== by 0x80487AF: main (bogon.cpp:66) ==25832== Address 0xBFFFF74C is not stack'd, malloc'd or free'd
25832はプロセスID
Memcheck
Invalid read of size 4 - 読んではいけない範囲を4byte分読んだ
こういうときに起こる。
int *p = (int *)malloc( sizeof(int) * 10 ); printf( "%d\n", p[11] );
Invalid write of size 4 - 書き込んではいけない範囲を4byte分書いた
こういうときに起こる。
int *p = (int *)malloc( sizeof(int) * 10 ); p[10] = 5;
上記のInvalid readとかwriteのあとに出ている結果(アドレス)について
int *p = (int *)malloc( sizeof(int) * 10 ); printf( "%d\n", p[11] ); プロセスIDは7885である。 ==7885== Invalid read of size 4 →読んではいけない範囲を4byte分読んだ ==7885== at 0x400580: leakfunc (in /home/valtest/a.out) →アドレス0x400580の関数leakfunc()で発生。 ==7885== by 0x4005A5: main (in /home/valtest/a.out) →さらにそのleakfunc()はアドレス0x4005A5のmain()でコールされている。
==7885== Address 0x51c306c is 4 bytes after a block of size 40 alloc'd
==7885== at 0x4C27A2E: malloc (vg_replace_malloc.c:270)
==7885== by 0x400565: leakfunc (in /home/valtest/a.out)
==7885== by 0x4005A5: main (in /home/valtest/a.out)
→あなたが読んだアドレスは40byte分メモリ確保された位置から4byte分後ろである。
sizeof(int)*10確保してp[11]でアクセスしたから…わかりますね?
after、alloc'dがポイント。
afterがあるということはbeforeもあるよ。
printf( "%d\n", p[-2] );
==7942== Address 0x51c3038 is 8 bytes before a block of size 40 alloc'd
説明は省略します。
不正free()
int *p = (int *)malloc( sizeof(int) * 10 ); free( p ); free( p ); pのアドレスは0x51c3040である。 ==7891== Invalid free() / delete / delete[] / realloc() ==7891== at 0x4C27430: free (vg_replace_malloc.c:446) →解放してはいけないアドレスをfree()もしくはdelete([])、realloc()した。 ==7891== by 0x4005ED: leakfunc (in /home/valtest/a.out) →そのfree()はアドレス0x4005EDのleakfunc()で発生。 ==7891== by 0x4005FD: main (in /home/valtest/a.out) →さらにそのleakfunc()はアドレス0x4005FDのmain()でコールされている。
==7891== Address 0x51c3040 is 0 bytes inside a block of size 40 free'd
==7891== at 0x4C27430: free (vg_replace_malloc.c:446)
==7891== by 0x4005E1: leakfunc (in /home/valtest/a.out)
==7891== by 0x4005FD: main (in /home/valtest/a.out)
→不正なfree()に渡されたアドレスは、過去に確保された40byte分に含まれている部分であり、
すでにfreeされている。そのfreeしたアドレスのメモリ領域の先頭から0byteの位置(つまり先頭)である。
解放済の領域をもう一度解放してまっせということだね。
初期化していない変数を参照
こういうときに起こる。 int a; /* 初期化してない */ printf( "uninitialized value : %d\n", a ); ==7977== Use of uninitialised value of size 8 ==7977== at 0x4E72A9B: _itoa_word (in /lib64/libc-2.12.so) ==7977== by 0x4E75652: vfprintf (in /lib64/libc-2.12.so) ==7977== by 0x4E7E189: printf (in /lib64/libc-2.12.so) ==7977== by 0x4005A7: leakfunc (in /home/valtest/a.out) ==7977== by 0x4005C2: main (in /home/valtest/a.out) →intなのになんで8なんだ? ==7977== Conditional jump or move depends on uninitialised value(s) ==7977== at 0x4E72AA5: _itoa_word (in /lib64/libc-2.12.so) ==7977== by 0x4E75652: vfprintf (in /lib64/libc-2.12.so) ==7977== by 0x4E7E189: printf (in /lib64/libc-2.12.so) ==7977== by 0x4005A7: leakfunc (in /home/valtest/a.out) ==7977== by 0x4005C2: main (in /home/valtest/a.out)
HEAP, LEAKサマリーの見方
==7913== HEAP SUMMARY: ==7913== in use at exit: 0 bytes in 0 blocks →プログラム終了時使用されているヒープは0byte、つまりリークなし! ==7913== total heap usage: 1 allocs, 2 frees, 40 bytes allocated →確保した回数、解放した回数、確保されたメモリ量。 ==7913== All heap blocks were freed -- no leaks are possible →確保されたメモリはすべて解放されておりリークはない。
==7919== HEAP SUMMARY: ==7919== in use at exit: 40 bytes in 1 blocks ==7919== total heap usage: 1 allocs, 0 frees, 40 bytes allocated →プログラム終了時に1ブロック(40byte)分解放してない。 ==7919== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==7919== at 0x4C27A2E: malloc (vg_replace_malloc.c:270) ==7919== by 0x400565: leakfunc (in /home/valtest/a.out) ==7919== by 0x4005BE: main (in /home/valtest/a.out) →「definitely lost」は確実に解放漏れがあるという意味。 ==7919== LEAK SUMMARY: ==7919== definitely lost: 40 bytes in 1 blocks ==7919== indirectly lost: 0 bytes in 0 blocks ==7919== possibly lost: 0 bytes in 0 blocks ==7919== still reachable: 0 bytes in 0 blocks ==7919== suppressed: 0 bytes in 0 blocks →リーク状況まとめ!
SGCheck
Memcheckは主にヒープ領域の不正アクセスを検知するために使用します。
SGCheckはスタック領域、グローバル変数、staticな変数なんかの不正アクセスを検知するものです。
すごいですね。どうやってるんでしょう。
なお、このツールはまだ実験段階だとかなんとか。
サンプルコードとコンパイル
#include <stdio.h> int main() { int z, y, i, a[10], c, d; for ( i=0; i <= 10; ++i ) { a[i] = 42; printf( "a[%d] = %d\n", i, a[i] ); } return 0; }
ビルドは「gcc -g ./上記のソースコードのファイル」で。
不正アクセスは、配列a(サイズ10)の、11番目にアクセスしている箇所です。
不正書き込み
a[i] = 42;
不正読み込み printf( "a[%d] = %d\n", i, a[i] );
エラーメッセージ
==2582== Invalid write of size 4 ==2582== at 0x400546: main (sgcheck.c:9) ==2582== Address 0x1fff000588 expected vs actual: ==2582== Expected: stack array "a" of size 40 in this frame ==2582== Actual: unknown ==2582== Actual: is 0 after Expected
→配列aはサイズ40だが、4byte分不正領域に書き込んでいる、という意味になります。
==2599== Invalid read of size 4 ==2599== at 0x400553: main (sgcheck.c:10) ==2599== Address 0x1fff000588 expected vs actual: ==2599== Expected: stack array "a" of size 40 in this frame ==2599== Actual: unknown ==2599== Actual: is 0 after Expected
→配列aはサイズ40だが、4byte分不正領域に読み込んでいる、という意味になります。
グローバル変数gの場合もほとんど一緒ですが、微妙にエラーメッセージが変わります。
こんな感じです。
==2599== Expected: stack array "a" of size 40 in this frame ↓ ==2700== Expected: global array "g" of size 40 in object with soname "NONE"
静的(static)変数sの場合はこう!
ELFではグローバル変数も静的変数も同じ.bss領域に入るので、ほとんど同じですね!
==2599== Expected: stack array "a" of size 40 in this frame ↓ ==2708== Expected: global array "s" of size 40 in object with soname "NONE"
注意点
その関数のスタックフレーム外の領域を不正参照した場合は、
SGCheckでは検出できないようです…。気を付けないといけないですねぇ(以下例)。
以下は上記サンプルソースのループ条件を「i <= 10」→「i <= 11」としたものです。
a[10] = 42の処理でスタックが破壊され、隣の変数iの値が不正に42になっています。
次のループで、a[42]にアクセスしていますが、関数のスタックフレーム内でないので、
SGCheckはエラーを報告していません。
==2745== Invalid write of size 4 ==2745== at 0x400546: main (sgcheck.c:12) ==2745== Address 0x1fff000588 expected vs actual: ==2745== Expected: stack array "a" of size 40 in this frame ==2745== Actual: unknown ==2745== Actual: is 0 after Expected ==2745== ==2745== Invalid read of size 4 ==2745== at 0x400553: main (sgcheck.c:13) ==2745== Address 0x1fff000588 expected vs actual: ==2745== Expected: stack array "a" of size 40 in this frame ==2745== Actual: unknown ==2745== Actual: is 0 after Expected ==2745== a[10] = 42 a[42] = 0 ==2745== ==2745== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
また、明らかに関数内のスタックフレームを破壊しているのに
エラーが検出されないことがあります…以下例。
サイズ10の配列で、インデックス11に値代入。 a[11] = 42;
まだマニュアルをしっかり読んでいないためかもしれませんが、
原因がわかり次第本エントリーに追記します。
参考サイト
■Valgrind User Manual
http://valgrind.org/docs/manual/manual.html
■Derick Rethans
http://derickrethans.nl/valgrind-null.html
■Stack Overflow
http://stackoverflow.com/questions/27636306/valgrind-address-is-0-bytes-after-a-block-of-size-8-allocd
http://stackoverflow.com/questions/18886298/valgrind-4-bytes-inside-a-block-of-size-8-freed
http://stackoverflow.com/questions/16213746/valgrind-results-of-a-segmentation-fault-program
■仙石浩明の日記
http://www.gcd.org/blog/2007/11/138/