【PE】Valgrind memcheckツールによるC/C++メモリリーク検出方法
37092 ワード
システムプログラミングの重要な側面は、メモリに関連する問題を効果的に処理することです.あなたの仕事がシステムに近づくほど、より多くのメモリの問題に直面する必要があります.これらの問題は非常に些細な場合がありますが、メモリをデバッグする問題の悪夢になることが多いです.そのため、メモリの問題をデバッグするために多くのツールが実際に使用されます.
本稿では,最もポピュラーなオープンソースメモリ管理フレームワークVALGRINDについて論じる.
Valgrind.org:
Valgrindは動解析ツールを構築するためのプローブフレームワークである.プログラムの改善を支援するために、各ツールがデバッグ、分析、または類似のタスクを実行するツールセットが含まれています.Valgrindのアーキテクチャはモジュール化されているので、既存の構造を乱さずに新しいツールを簡単に作成できます.
多くの有用なツールが標準として提供されています. Memcheckはメモリエラー検出器です.それはあなたのプログラム、特にCとC++で書かれたプログラムをより正確にするのに役立ちます. Cachegrindはキャッシュとブランチ予測アナライザです.プログラムの実行を高速化するのに役立ちます. Callgrindは呼び出しマップキャッシュ生成アナライザである.Cachegrindの機能と重複していますが、Cachegrindが収集していない情報も収集されています. Helgrindはスレッドエラー検出器です.マルチスレッドプログラムをより正確にするのに役立ちます. DRDもスレッドエラー検出器です.Helgrindと似ていますが、異なる分析技術を使用しているので、異なる問題が見つかる可能性があります. Massifはスタックアナライザです.プログラムがより少ないメモリを使用できるようにします. DHATは別の異なるスタックアナライザです.ブロックのライフサイクル、ブロックの使用、レイアウトの非効率性などの問題を理解するのに役立ちます. SGcheckはスタックとグローバル配列のオーバーフローを検出するための実験ツールである.その機能はMemcheckと相補的である:SGcheckはMemcheckが見つけられない問題を見つけ、逆も同様である. BBVは実験的性質のSimPoint基本ブロックベクトル生成器である.コンピュータアーキテクチャの研究と開発に役立ちます.
ほとんどのユーザーに使用されていない小さなツールもあります.Lackeyはデモ機器の基礎のサンプルツールです.Nulgrindは最小化されたValgrindツールであり、分析や操作を行わず、テストの目的にのみ使用されます.
この文章では「memcheck」ツールに注目します.
Valgrind Memcheckの使用
memcheckツールの使い方は次のとおりです.
上のコマンドから明らかなように、主なコマンドはvalgrindであり、私たちが使用したいツールは'-tool'オプションで指定されています.上の「a.out」とは、memcheckを使用して実行したい実行可能なファイルを指す.
このツールでは、メモリに関連する次の問題を検出できます.未解放メモリの使用 リリースメモリの読み取り/書き込み 割り当てられたメモリブロックの末尾の読み取り/書き込み メモリリーク が一致しないmalloc/new/new[]とfree/delete/delete[] を使用メモリを繰り返し解放する 注意:上記は全面的ではないが、このツールによって検出できる多くの一般的な問題が含まれている.
上のシーンについて一つ一つ議論しましょう.
注意:以下で説明するすべてのテストコードはgccを使用し、memcheckの出力で行番号を生成するための-gオプションを加えてコンパイルすべきである.私たちが前に議論したCプログラムを実行可能なファイルにコンパイルするには、4つの異なる段階を経験する必要があると思います.
1.初期化されていないメモリの使用
Code :
上記のコードでは、初期化されていないポインタ「p」を使用しようとする.
Memcheckを実行して結果を見てみましょう.
上記の出力から、Valgrindは初期化されていない変数を検出し、警告を与えた(上に太い行がいくつかあるようだ).
2.メモリが解放された後に読み取り/書き込みを行う
Code :
上記のコードでは、メモリを解放するポインタ「p」があり、ポインタを利用して値を取得しようとしています.
memcheckを実行してValgrindがこの状況にどのように反応しているかを見てみましょう.
上記の出力から、Valgrindは無効な読み出し動作を検出する、警告"Invalid read of size 1"を出力していることがわかる.
なお、gdbを使用してcプログラムをデバッグする.
3.割り当てられたメモリブロックの末尾から読み取り/書き込みを行う
Code :
上記のコードでは、「p」に1バイトのメモリを割り当てるが、値を「c」に読み出す際に使用するのはアドレスp+1である.
Valgrindを使用して、上のコードを実行します.
同様に、この場合においても無効な読み出し動作が検出する.
4.メモリ漏洩
Code:
今回のコードでは、バイトを申請しましたが、解放されませんでした.Valgrindを実行して何が起こるか見てみましょう.
メモリの漏洩を検出できる出力ライン(上に太くなった部分)が表示する.
注意:ここでは、メモリ漏洩の詳細を得るために、「–leak-check=full」というオプションを追加しました.
5.malloc/new/new[]とfree/delete/delete[]を不一致で使用
Code:
上記のコードではmalloc()を使用してメモリを割り当てるがdeleteオペレータを使用してメモリを削除する.
注意:deleteオペレータはC++に導入するため、C++をコンパイルするにはg++を使用する必要がある.
実行してみましょう.
上の出力から(太字の行)が見えますが、Valgrindは「free()/delete/delete[]を不一致で使用している」ことを明確に説明しています.
テストコードで「new」と「free」を組み合わせて、Valgrindが与えた結果を見てみましょう.
6.メモリを2回解放
Code :
上のコードでは、「p」が指すメモリを2回解放しました.次にmemcheckを実行します.
上記の出力から、同じポインタに対してメモリの解放操作が2回呼び出されたことを検出する(太い行が見える).
本稿では、メモリ管理フレームワークValgrindに注目し、memcheck(Valgrindフレームワークが提供する)ツールを使用して、メモリを頻繁に操作する必要があるプログラマーの負担をどのように低減するかを理解する.このツールでは、手動で検出できないメモリに関する多くの問題を検出できます.
本稿では,最もポピュラーなオープンソースメモリ管理フレームワークVALGRINDについて論じる.
Valgrind.org:
Valgrindは動解析ツールを構築するためのプローブフレームワークである.プログラムの改善を支援するために、各ツールがデバッグ、分析、または類似のタスクを実行するツールセットが含まれています.Valgrindのアーキテクチャはモジュール化されているので、既存の構造を乱さずに新しいツールを簡単に作成できます.
多くの有用なツールが標準として提供されています.
ほとんどのユーザーに使用されていない小さなツールもあります.Lackeyはデモ機器の基礎のサンプルツールです.Nulgrindは最小化されたValgrindツールであり、分析や操作を行わず、テストの目的にのみ使用されます.
この文章では「memcheck」ツールに注目します.
Valgrind Memcheckの使用
memcheckツールの使い方は次のとおりです.
valgrind --tool=memcheck ./a.out
上のコマンドから明らかなように、主なコマンドはvalgrindであり、私たちが使用したいツールは'-tool'オプションで指定されています.上の「a.out」とは、memcheckを使用して実行したい実行可能なファイルを指す.
このツールでは、メモリに関連する次の問題を検出できます.
上のシーンについて一つ一つ議論しましょう.
注意:以下で説明するすべてのテストコードはgccを使用し、memcheckの出力で行番号を生成するための-gオプションを加えてコンパイルすべきである.私たちが前に議論したCプログラムを実行可能なファイルにコンパイルするには、4つの異なる段階を経験する必要があると思います.
1.初期化されていないメモリの使用
Code :
#include
#include
int main(void)
{
char *p;
char c = *p;
printf("
[%c]
",c);
return 0;
}
上記のコードでは、初期化されていないポインタ「p」を使用しようとする.
Memcheckを実行して結果を見てみましょう.
$ valgrind --tool=memcheck ./val
==2862== Memcheck, a memory error detector
==2862== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2862== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2862== Command: ./val
==2862==
==2862== Use of uninitialised value of size 8
==2862== at 0x400530: main (valgrind.c:8)
==2862==
[#]
==2862==
==2862== HEAP SUMMARY:
==2862== in use at exit: 0 bytes in 0 blocks
==2862== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==2862==
==2862== All heap blocks were freed -- no leaks are possible
==2862==
==2862== For counts of detected and suppressed errors, rerun with: -v
==2862== Use --track-origins=yes to see where uninitialized values come from
==2862== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
上記の出力から、Valgrindは初期化されていない変数を検出し、警告を与えた(上に太い行がいくつかあるようだ).
2.メモリが解放された後に読み取り/書き込みを行う
Code :
#include
#include
int main(void)
{
char *p = malloc(1);
*p = 'a';
char c = *p;
printf("
[%c]
",c);
free(p);
c = *p;
return 0;
}
上記のコードでは、メモリを解放するポインタ「p」があり、ポインタを利用して値を取得しようとしています.
memcheckを実行してValgrindがこの状況にどのように反応しているかを見てみましょう.
$ valgrind --tool=memcheck ./val
==2849== Memcheck, a memory error detector
==2849== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2849== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2849== Command: ./val
==2849==
[a]
==2849== Invalid read of size 1
==2849== at 0x400603: main (valgrind.c:30)
==2849== Address 0x51b0040 is 0 bytes inside a block of size 1 free'd
==2849== at 0x4C270BD: free (vg_replace_malloc.c:366)
==2849== by 0x4005FE: main (valgrind.c:29)
==2849==
==2849==
==2849== HEAP SUMMARY:
==2849== in use at exit: 0 bytes in 0 blocks
==2849== total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==2849==
==2849== All heap blocks were freed -- no leaks are possible
==2849==
==2849== For counts of detected and suppressed errors, rerun with: -v
==2849== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
上記の出力から、Valgrindは無効な読み出し動作を検出する、警告"Invalid read of size 1"を出力していることがわかる.
なお、gdbを使用してcプログラムをデバッグする.
3.割り当てられたメモリブロックの末尾から読み取り/書き込みを行う
Code :
#include
#include
int main(void)
{
char *p = malloc(1);
*p = 'a';
char c = *(p+1);
printf("
[%c]
",c);
free(p);
return 0;
}
上記のコードでは、「p」に1バイトのメモリを割り当てるが、値を「c」に読み出す際に使用するのはアドレスp+1である.
Valgrindを使用して、上のコードを実行します.
$ valgrind --tool=memcheck ./val
==2835== Memcheck, a memory error detector
==2835== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2835== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2835== Command: ./val
==2835==
==2835== Invalid read of size 1
==2835== at 0x4005D9: main (valgrind.c:25)
==2835== Address 0x51b0041 is 0 bytes after a block of size 1 alloc'd
==2835== at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2835== by 0x4005C5: main (valgrind.c:22)
==2835==
[]
==2835==
==2835== HEAP SUMMARY:
==2835== in use at exit: 0 bytes in 0 blocks
==2835== total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==2835==
==2835== All heap blocks were freed -- no leaks are possible
==2835==
==2835== For counts of detected and suppressed errors, rerun with: -v
==2835== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
同様に、この場合においても無効な読み出し動作が検出する.
4.メモリ漏洩
Code:
#include
#include
int main(void)
{
char *p = malloc(1);
*p = 'a';
char c = *p;
printf("
[%c]
",c);
return 0;
}
今回のコードでは、バイトを申請しましたが、解放されませんでした.Valgrindを実行して何が起こるか見てみましょう.
$ valgrind --tool=memcheck --leak-check=full ./val
==2888== Memcheck, a memory error detector
==2888== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2888== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2888== Command: ./val
==2888==
[a]
==2888==
==2888== HEAP SUMMARY:
==2888== in use at exit: 1 bytes in 1 blocks
==2888== total heap usage: 1 allocs, 0 frees, 1 bytes allocated
==2888==
==2888== 1 bytes in 1 blocks are definitely lost in loss record 1 of 1
==2888== at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2888== by 0x400575: main (valgrind.c:6)
==2888==
==2888== LEAK SUMMARY:
==2888== definitely lost: 1 bytes in 1 blocks
==2888== indirectly lost: 0 bytes in 0 blocks
==2888== possibly lost: 0 bytes in 0 blocks
==2888== still reachable: 0 bytes in 0 blocks
==2888== suppressed: 0 bytes in 0 blocks
==2888==
==2888== For counts of detected and suppressed errors, rerun with: -v
==2888== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
メモリの漏洩を検出できる出力ライン(上に太くなった部分)が表示する.
注意:ここでは、メモリ漏洩の詳細を得るために、「–leak-check=full」というオプションを追加しました.
5.malloc/new/new[]とfree/delete/delete[]を不一致で使用
Code:
#include
#include
#include
int main(void)
{
char *p = (char*)malloc(1);
*p = 'a';
char c = *p;
printf("
[%c]
",c);
delete p;
return 0;
}
上記のコードではmalloc()を使用してメモリを割り当てるがdeleteオペレータを使用してメモリを削除する.
注意:deleteオペレータはC++に導入するため、C++をコンパイルするにはg++を使用する必要がある.
実行してみましょう.
$ valgrind --tool=memcheck --leak-check=full ./val
==2972== Memcheck, a memory error detector
==2972== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2972== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==2972== Command: ./val
==2972==
[a]
==2972== Mismatched free() / delete / delete []
==2972== at 0x4C26DCF: operator delete(void*) (vg_replace_malloc.c:387)
==2972== by 0x40080B: main (valgrind.c:13)
==2972== Address 0x595e040 is 0 bytes inside a block of size 1 alloc'd
==2972== at 0x4C274A8: malloc (vg_replace_malloc.c:236)
==2972== by 0x4007D5: main (valgrind.c:7)
==2972==
==2972==
==2972== HEAP SUMMARY:
==2972== in use at exit: 0 bytes in 0 blocks
==2972== total heap usage: 1 allocs, 1 frees, 1 bytes allocated
==2972==
==2972== All heap blocks were freed -- no leaks are possible
==2972==
==2972== For counts of detected and suppressed errors, rerun with: -v
==2972== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
上の出力から(太字の行)が見えますが、Valgrindは「free()/delete/delete[]を不一致で使用している」ことを明確に説明しています.
テストコードで「new」と「free」を組み合わせて、Valgrindが与えた結果を見てみましょう.
6.メモリを2回解放
Code :
#include
#include
int main(void)
{
char *p = (char*)malloc(1);
*p = 'a';
char c = *p;
printf("
[%c]
",c);
free(p);
free(p);
return 0;
}
上のコードでは、「p」が指すメモリを2回解放しました.次にmemcheckを実行します.
$ valgrind --tool=memcheck --leak-check=full ./val
==3167== Memcheck, a memory error detector
==3167== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==3167== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==3167== Command: ./val
==3167==
[a]
==3167== Invalid free() / delete / delete[]
==3167== at 0x4C270BD: free (vg_replace_malloc.c:366)
==3167== by 0x40060A: main (valgrind.c:12)
==3167== Address 0x51b0040 is 0 bytes inside a block of size 1 free'd
==3167== at 0x4C270BD: free (vg_replace_malloc.c:366)
==3167== by 0x4005FE: main (valgrind.c:11)
==3167==
==3167==
==3167== HEAP SUMMARY:
==3167== in use at exit: 0 bytes in 0 blocks
==3167== total heap usage: 1 allocs, 2 frees, 1 bytes allocated
==3167==
==3167== All heap blocks were freed -- no leaks are possible
==3167==
==3167== For counts of detected and suppressed errors, rerun with: -v
==3167== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)
上記の出力から、同じポインタに対してメモリの解放操作が2回呼び出されたことを検出する(太い行が見える).
本稿では、メモリ管理フレームワークValgrindに注目し、memcheck(Valgrindフレームワークが提供する)ツールを使用して、メモリを頻繁に操作する必要があるプログラマーの負担をどのように低減するかを理解する.このツールでは、手動で検出できないメモリに関する多くの問題を検出できます.