Table of Contents
1 Valgrindとは
プログラム実行経路上のメモリリークを検出するツールです。
mallocやnewを独自の関数に置き換え、メモリ領域の確保と同時に呼び出し情報を保持しておき、プログラム終了時に解放されていない呼び出し元を出力します。
2 使い方
以下のようなmallocされた領域がfreeされないコードを使います。
#include <stdio.h>#include <stdlib.h>int main(int argc, char *argv[]){ char *buffer = (char *) malloc(1024); if (buffer == NULL) return 1; buffer[0] = 1; /** free(buffer); */ return 0;}
コンパイル後、valgrindにかけてみます。
$ valgrind –leak-check=full ./memleak==30429== Memcheck, a memory error detector==30429== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.==30429== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info==30429== Command: ./memleak==30429== ==30429== ==30429== HEAP SUMMARY:==30429== in use at exit: 36,305 bytes in 425 blocks==30429== total heap usage: 505 allocs, 80 frees, 42,433 bytes allocated==30429== ==30429== 1,024 bytes in 1 blocks are definitely lost in loss record 68 of 79==30429== at 0x100007351: malloc (vg_replace_malloc.c:303)==30429== by 0x100000F44: main (memleak.c:6)==30429== ==30429== LEAK SUMMARY:==30429== definitely lost: 1,024 bytes in 1 blocks==30429== indirectly lost: 0 bytes in 0 blocks==30429== possibly lost: 0 bytes in 0 blocks==30429== still reachable: 0 bytes in 0 blocks==30429== suppressed: 35,281 bytes in 424 blocks==30429== ==30429== For counts of detected and suppressed errors, rerun with: -v==30429== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 15)
“by 0x100000F44: main (memleak.c:6)”と表示され、mallocされた領域がfreeされていない旨が検出されます。
3 メモリリークの種類
以下の様な単方向リストのノードを10回確保するサンプルプログラムmemleak.cを用いて、各メモリリークの違いを説明します。
#include <stdlib.h>#ifdef DEFINITELY_LOST# define FREE_TIME (9)#else# define FREE_TIME (10)#endifstruct node { struct node *next;};int main(void){ struct node *node = NULL; int i; char *sigsegv = NULL; for (i = 0; i < 10; i++) { struct node *prev = calloc(1, sizeof(*prev)); prev->next = node; node = prev; }#ifdef STILL_REACHABLE *sigsegv = 0;#endif /** STILL_REACHABLE */#ifndef INDIRECTLY_LOST for (i = 0; i < FREE_TIME; i++) { struct node *next = node->next; free(node); node = next; }#endif /** INDIRECTLY_LOST */ return 0;}
3.1 definitely lost
変数に確保した領域がメモリリークしている場合に検出されます。
memleak.cでDEFINITELY_LOSTをdefineした場合に、変数nodeに最後のノードが代入されますが、解放されません。
$ gcc -DDEFINITELY_LOST -o memleak memleak.c$ $ valgrind –leak-check=full ./memleak$ ==46562== Memcheck, a memory error detector$ ==46562== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.$ ==46562== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info$ ==46562== Command: ./memleak$ ==46562== $ –46562– ./memleak:$ –46562– dSYM directory is missing; consider using –dsymutil=yes$ ==46562== $ ==46562== HEAP SUMMARY:$ ==46562== in use at exit: 35,289 bytes in 425 blocks$ ==46562== total heap usage: 514 allocs, 89 frees, 41,489 bytes allocated$ ==46562== $ ==46562== 8 bytes in 1 blocks are definitely lost in loss record 2 of 79$ ==46562== at 0x100007D27: calloc (vg_replace_malloc.c:630)$ ==46562== by 0x100000EF5: main (in ./memleak)$ ==46562== $ ==46562== LEAK SUMMARY:$ ==46562== definitely lost: 8 bytes in 1 blocks$ ==46562== indirectly lost: 0 bytes in 0 blocks$ ==46562== possibly lost: 0 bytes in 0 blocks$ ==46562== still reachable: 0 bytes in 0 blocks$ ==46562== suppressed: 35,281 bytes in 424 blocks$ ==46562== $ ==46562== For counts of detected and suppressed errors, rerun with: -v$ ==46562== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 15)
3.2 indirectly lost
構造体変数が持つメンバ変数のポインター経由でメモリリークしている場合に検出されます。
memleak.cでINDIRECTLY_LOSTをdefineした場合に、単一リスト全体が解放されません。
$ gcc -DINDIRECTLY_LOST -o memleak memleak.c$ valgrind –leak-check=full ./memleak==46566== Memcheck, a memory error detector==46566== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.==46566== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info==46566== Command: ./memleak==46566== –46566– ./memleak:–46566– dSYM directory is missing; consider using –dsymutil=yes==46566== ==46566== HEAP SUMMARY:==46566== in use at exit: 35,361 bytes in 434 blocks==46566== total heap usage: 514 allocs, 80 frees, 41,489 bytes allocated==46566== ==46566== 80 (8 direct, 72 indirect) bytes in 1 blocks are definitely lost in loss record 43 of 80==46566== at 0x100007D27: calloc (vg_replace_malloc.c:630)==46566== by 0x100000F45: main (in ./memleak)==46566== ==46566== LEAK SUMMARY:==46566== definitely lost: 8 bytes in 1 blocks==46566== indirectly lost: 72 bytes in 9 blocks==46566== possibly lost: 0 bytes in 0 blocks==46566== still reachable: 0 bytes in 0 blocks==46566== suppressed: 35,281 bytes in 424 blocks==46566== ==46566== For counts of detected and suppressed errors, rerun with: -v==46566== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 15)
変数nodeに代入された領域は”definitely lost”となり、メンバ変数ポインタの領域は”indirectly lost”になります。
3.3 still reachable
確保された領域が解放されないままプログラムがabortした場合に検出されます。
memleak.cでSTILL_REACHABLEをdefineした場合に、単一リスト全体が解放されないままプログラムがabortします。
$ gcc -DSTILL_REACHABLE -o memleak memleak.c$ valgrind –leak-check=full ./memleak==46597== Memcheck, a memory error detector==46597== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.==46597== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info==46597== Command: ./memleak==46597==–46597– ./memleak:–46597– dSYM directory is missing; consider using –dsymutil=yes==46597== Invalid write of size 1==46597== at 0x100000F21: main (in ./memleak)==46597== Address 0x0 is not stack’d, malloc’d or (recently) free’d==46597====46597====46597== Process terminating with default action of signal 11 (SIGSEGV)==46597== Access not within mapped region at address 0x0==46597== at 0x100000F21: main (in ./memleak)==46597== If you believe this happened as a result of a stack==46597== overflow in your program’s main thread (unlikely but==46597== possible), you can try to increase the size of the==46597== main thread stack using the –main-stacksize= flag.==46597== The main thread stack size used in this run was 8388608.==46597====46597== HEAP SUMMARY:==46597== in use at exit: 35,361 bytes in 434 blocks==46597== total heap usage: 514 allocs, 80 frees, 41,489 bytes allocated==46597====46597== LEAK SUMMARY:==46597== definitely lost: 0 bytes in 0 blocks==46597== indirectly lost: 0 bytes in 0 blocks==46597== possibly lost: 0 bytes in 0 blocks==46597== still reachable: 80 bytes in 10 blocks==46597== suppressed: 35,281 bytes in 424 blocks==46597== Reachable blocks (those to which a pointer was found) are not shown.==46597== To see them, rerun with: –leak-check=full –show-leak-kinds=all==46597====46597== For counts of detected and suppressed errors, rerun with: -v==46597== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 15)
プログラムによってはabortした場合は解放をシステムに任せてしまうスタンスのものもあり、メモリリークとは扱いません。
サイズ削減を目指して、プログラムが正常終了する場合にもメモリ解放のコードを書かないプログラムも稀にありますが、個人的にはabort以外の経路は全てメモリ解放のコードを書くべきだと思います。
3.4 possibly lost
Valgrindが認識できない操作によって、追跡できなくなった領域がメモリリークしているかもしれない場合に検出されます。False Positiveな性質を持ちます。
どこまでValgrindが追跡できるかどうかは実装を詳しく見ないと分かりませんが、通常のポインタ操作ではまず出ないもののようです。
出る場合はメモリリークかどうかを調べ、メモリリークではない場合はコーディングを改善した方が良いかもしれません。
3.5 suppressed
suppressionファイルで検出を抑制された領域がある場合に出力されます。
suppressionファイルはsuppressionsオプションで指定できます。
supressionsオプションとは別に、/lib/valgrind/default.suppが読み込まれます
4 –suppressionsオプション
Valgrindで検出されるがメモリリークではない該当箇所に対し、suppressionファイルを–suppressionsで指定することで検出を抑制できます。
$ gcc -DDEFINITELY_LOST -o memleak memleak.c$ cat main.supp { <insert_a_suppression_name_here> Memcheck:Leak match-leak-kinds: definite fun:calloc fun:main}$ valgrind –suppressions=main.supp –leak-check=full ./memleak==46683== Memcheck, a memory error detector==46683== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.==46683== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info==46683== Command: ./memleak==46683== –46683– ./memleak:–46683– dSYM directory is missing; consider using –dsymutil=yes==46683== ==46683== HEAP SUMMARY:==46683== in use at exit: 35,289 bytes in 425 blocks==46683== total heap usage: 514 allocs, 89 frees, 41,489 bytes allocated==46683== ==46683== LEAK SUMMARY:==46683== definitely lost: 0 bytes in 0 blocks==46683== indirectly lost: 0 bytes in 0 blocks==46683== possibly lost: 0 bytes in 0 blocks==46683== still reachable: 0 bytes in 0 blocks==46683== suppressed: 35,289 bytes in 425 blocks==46683== ==46683== For counts of detected and suppressed errors, rerun with: -v==46683== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 16 from 16)
4.1 –gen-suppressionsオプション
–gen-supressionsオプションを用いることで、メモリリーク検出時にsuppressionsファイルに記述内容も出力されます。
$ gcc -DDEFINITELY_LOST -o memleak memleak.c$ valgrind –gen-suppressions=all –leak-check=full ./memleak==46695== Memcheck, a memory error detector==46695== Copyright (C) 2002-2013, and GNU GPL’d, by Julian Seward et al.==46695== Using Valgrind-3.11.0.SVN and LibVEX; rerun with -h for copyright info==46695== Command: ./memleak==46695== –46695– ./memleak:–46695– dSYM directory is missing; consider using –dsymutil=yes==46695== ==46695== HEAP SUMMARY:==46695== in use at exit: 35,289 bytes in 425 blocks==46695== total heap usage: 514 allocs, 89 frees, 41,489 bytes allocated==46695== ==46695== 8 bytes in 1 blocks are definitely lost in loss record 2 of 79==46695== at 0x100007D27: calloc (vg_replace_malloc.c:630)==46695== by 0x100000EF5: main (in ./memleak)==46695== { <insert_a_suppression_name_here> Memcheck:Leak match-leak-kinds: definite fun:calloc fun:main}==46695== LEAK SUMMARY:==46695== definitely lost: 8 bytes in 1 blocks==46695== indirectly lost: 0 bytes in 0 blocks==46695== possibly lost: 0 bytes in 0 blocks==46695== still reachable: 0 bytes in 0 blocks==46695== suppressed: 35,281 bytes in 424 blocks==46695== ==46695== For counts of detected and suppressed errors, rerun with: -v==46695== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 15 from 15)
メモリリークの内容確認をしてからですが、上記の出力をgrep -v “^–” | grep -v “^==”でパイプしてやれば、そのままsupressionファイルとして利用できます。
5 留意すべき点
Valgrindは実際に実行された経路上のメモリリークを検出するものです。
経路によっては検出できない場合もあります。
メモリリークを検出すべき実行経路を考慮する必
要があります。
ライブラリを使用した最も単純なコードと–gen-suppressionsでsupressionファイルを作成しておき、自身のコードで使うと良いでしょう。
仮想環境の場合、グラフィックライブラリ周りでメモリリークが検出される場合があります。
これらは仮想環境の実装上の問題の場合があり、メモリリークではないかもしれません。
実環境の場合と比べてみると良いでしょう。
–gen-supressionsで検出すべきメモリリークを抑制しきれない場合はvalgrindは難しいかもしれません。
他のメモリリーク検出ツールを検討すべきです。
例えば、OSXでSDL2のプログラムにValgrindをかけた場合、多くの検出が出てしまいます。
–gen-suppressionsを使ってsuppressionファイルを作成して、–supressionsで読み込ませて再度Valgrindを掛けても新たな検出結果が出てしまう状況です。
代替え手段として、Linux上でSDL2のプログラムにValgrindを掛けるようにしています。