インストール
Ubuntuでのやり方
$ sudo apt-get install valgrind
使い方
$ valgrind --leak-check=full <リーク確認したいプログラムのパス>
または実行ファイルのディレクトリまで移動し、
$ valgrind --leak-check=full ./<実行ファイル名>
--leak-check=fullが指定された場合、Memcheckは、ブロックがどこに割り当てられたかを含めて、"definitely lost"ブロックまたは"possibly lost"ブロックの詳細を表示する。
definitely lost, possibly lost については後述
ログをファイルに出力する
--log-file オプションを利用する。
$ valgrind --log-file=”<ログファイルのパス>” --leak-check =full <プログラムのパス>
ライブラリ除外設定
valgrind でプロファイル対象から特定の共有ライブラリを除外する方法
[翻訳] Memory leak detection
Memcheckは、malloc/newなどの呼び出しに応答して発行されたすべてのヒープブロックを追跡します。したがってプログラムが終了するとき、Memcheckは解放されていないブロックを知ることができます。
--leak-check
が適切に設定されている場合、開放されずに残っている各ブロックに対し、Memcheckはルートセット内のポインタからブロックに到達可能かどうかを測定します。ルートセットは、(a)全スレッドの汎用レジスタと、(b)スタックを含むアクセス可能なクライアントメモリ内の初期化された、整列されたポインタサイズのデータワードからなります。
ブロックに到達するには2つの方法があります。
第1の方法は、「開始ポインタ」、すなわちブロックの開始点を指すポインタを使うことです。
第2の方法は、「内部ポインタ」、すなわちブロックの中間へのポインタを使うことです。内部ポインタを発生させる可能性があると知られているいくつかの原因があります:
- ポインタはもともとは開始ポインタであったかもしれず、故意に(あるいは故意でなく)プログラムによって動かされたかもしれません。特に、プログラムが tagged pointer (Tagged pointer - Wikipedia)を使用している場合、つまり、ポインタの下位1ビット、2ビットまたは3ビットを使用する場合、そこは追加情報を格納するために通常は常にゼロであるため、内部ポインタが発生する可能性があります。
- メモリ内のランダムなジャンク値で、まったく無関係であり、ただの偶然かもしれません。* C++
std::string
の内部char配列へのポインタかもしれません。たとえば、std::string
の先頭に3ワードを追加して、長さ・容量・および参照カウントを、文字の配列を含むメモリの前に格納するコンパイラがあります。これら3つの単語の直後にポインタを返し、char配列を指します。 - コードによっては、メモリブロックを割り当てて、64ビットの数値として格納するために最初の8バイトを使用するかもしれません。
sqlite3MemMalloc
はこれを行います。 - new[]で割り当てられたC++オブジェクト(デストラクタを所有する)の配列へのポインタかもしれません。この場合、一部のコンパイラは、割り当てられたブロックの先頭に配列の長さを含む「マジッククッキー」を格納し、そのマジッククッキーのすぐ後ろへのポインタ、つまり内部ポインタを返します。詳細については、このページ※を参照してください。※リンク切れ
- 多重継承を使用するC++オブジェクトの内部部分へのポインタである可能性があります。
それを念頭に置いて、次の図で説明される9つの可能なケースを考えてみましょう。
Pointer chain AAA Leak Case BBB Leak Case
------------- ------------- -------------
(1) RRR ------------> BBB DR
(2) RRR ---> AAA ---> BBB DR IR
(3) RRR BBB DL
(4) RRR AAA ---> BBB DL IL
(5) RRR ------?-----> BBB (y)DR, (n)DL
(6) RRR ---> AAA -?-> BBB DR (y)IR, (n)DL
(7) RRR -?-> AAA ---> BBB (y)DR, (n)DL (y)IR, (n)IL
(8) RRR -?-> AAA -?-> BBB (y)DR, (n)DL (y,y)IR, (n,y)IL, (_,n)DL
(9) RRR AAA -?-> BBB DL (y)IL, (n)DL
Pointer chain legend:
- RRR: a root set node or DR block
- AAA, BBB: heap blocks
- --->: a start-pointer
- -?->: an interior-pointer
Leak Case legend:
- DR: Directly reachable
- IR: Indirectly reachable
- DL: Directly lost
- IL: Indirectly lost
- (y)XY: it's XY if the interior-pointer is a real pointer
- (n)XY: it's XY if the interior-pointer is not a real pointer
- (_)XY: it's XY in either case
すべての可能なケースは、上記9のいずれかに帰着させることができます。Memcheckは、これらのケースのいくつかを出力中でマージし、その結果、リークの種類は次の4つになります。
-
"Still reachable". これは、上のケース1と2(のBBBブロック)に相当します。ブロックに対する開始ポインタまたは開始ポインタからの連鎖が見つかりました。ブロックはまだ指されているので、プログラマは、少なくとも原則的には、プログラムを終了する前にそれを解放することができます。「まだ到達可能な」ブロックは非常に一般的であり、間違いなく問題にはならないでしょう。したがって、デフォルトでMemcheckはそのようなブロックを個別に報告しません。※
--show-leak-kinds=reachable
オプションを指定すると表示 -
"Definitely lost". これは、上記のケース3(のBBBブロック)に相当します。これは、ブロックへのポインタが見つからないことを意味します。ブロックは「失われた」と分類されます。ブロックへのポインタが存在しないため、プログラム終了時にプログラマはブロックを解放できません。おそらくプログラムの早い時点でポインタを失ったという兆候です。本当ならプログラマにより修正されるべきです。※
--show-leak-kinds=indirect
オプションを指定すると表示 -
"Indirectly lost". これは、上記のケース4と9(のBBBブロック)に相当します。これは、ブロックが「失われている」ことを意味しますが、それを指すポインタがないためというよりも、それを間接的に指すブロックがすべて失われているためです。たとえば、二分木があり、そのルートノードが失われた場合、すべての子ノードは間接的に失われます。間接リークを引き起こした(真犯人であるところの)"Definitely lost"ブロックが修正された場合に問題が解消されるため、Memcheckはこのようなブロックをデフォルトで個別に報告しません。
-
"Possibly lost". これは、上記のBBBブロックのケース5〜8に相当します。これは、ブロックへ到達する単一のポインタまたは複数のポインタの連鎖が見つかったが、その鎖に含まれるポインタの少なくとも1つが内部ポインタであることを意味します。内部ポインタはたまたまブロックを指すことになったメモリ内のランダムな値である可能性があるので、内部ポインタを持っていることが分かっていない限り、これをOKと考えるべきではありません。