MacOSプログラミング/Guard Malloc(libgmalloc) について

Guard Mallocはmalloc, callocなどで確保したメモリに対して不正な操作を行ってしまう類のバグの検出を助けるデバッグ用のライブラリです。Guard Mallocを使ってアプリケーションを実行すると、そうしたメモリに対してのバグがある場合、アプリケーションがバグの位置でハングアップします。

- Manual page for libgmalloc

Xcode上でのGuard Mallocの使い方
メニューから「実行>Guard Mallocを有効にする」を選択して、チェックをつけるとGuard Mallocを有効に出来ます。Guard Mallocには各種オプションがありますが、これは実行時の環境変数をセットすることで設定します。

Guard Mallocの原理
Guard Mallocは、malloc, free, NSZoneMallocとその派生系(callocなど)をリプレースし、バグの検出に特化したやりかたでメモリを確保します。

ここから先の原理の話は、仮想メモリに対する理解がないと分からない説明ですが、仮想メモリの説明まで書くと長くなってしまうので、それは割愛します。仮想メモリやページングの動作が分からない方は、適当にgoogle等で調べてください。
なお、仮想メモリに関する勉強をしたいと思う方に対する個人的なお勧めは、手っ取り早い方法ではないですが、
- コンピュータの構成と設計~ハードウエアとソフトウエアのインタフェース 第3版 (下)
の 7.4章 「仮想記憶」 を読むことです。

Guard Malloc版のmallocは、すべてのアロケーションに対して異なるページを割り当てます。そして、freeでは、この割り当てたページを解放するので、プログラムはそのアドレスに触ろうとした瞬間にパスエラー(アドレス例外)を引き起こします。

その他のGuard Mallocの重要な特性は、以下の通りです。
- Guard Mallocのmalloc/freeは、スレッドセーフな操作です。
- (デフォルト設定では)OSX 10.5以降では、確保されるバッファのアドレスは、16byte境界にアラインされます。このため、SSEやAltivec命令で使用するメモリに対して割り当てても問題なく利用できます。
- (デフォルト設定では)OSX 10.5以降では、16バイト単位でのページングになるので、それ以下のサイズでのオーバーランは検出不可能です。たとえば、メモリを10byte確保したブロックの11byte目に意図せず書き込んでしまっても、それは検出されません。これは、SSE/Altivec/Carbon/Cocoaでメモリを利用しやすくするための調整として意図されています。
- アライメント設定はオプションで変更可能です。オプションの詳細については後述します。
- Mac OS X 10.6からは、libgmallocでもmalloc_historyなどのコマンドが利用可能になりました。

環境変数を使ってGuard Mallocを有効にするには
- "DYLD_INSERT_LIBRARIES" 環境変数にlibgmallocのパスを設定します。
 例(gdb上): set env DYLD_INSERT_LIBRARIES to /usr/lib/libgmalloc.dylib
- DYLD_FORCE_FLAT_NAMESPACEの設定は、現在のOSXではもう不要だそうです。

設定可能な環境変数
MALLOC_PROTECT_BEFORE
- このフラグがセットされていると、Guard Mallocはユーザーメモリの先頭にも未使用ページを差し込んで、メモリアンダーランも検出可能にします。

MALLOC_FILL_SPACE
- このフラグがセットされていると、確保したバッファを0x55で埋めます。未初期化バッファのバグ検出に便利。

MALLOC_ALLOW_READS
- ガード用のページに対して、メモリの読み出し行為の許可フラグを設定する。保護領域の読み出し操作によるハングはしなくなる

MALLOC_VECTOR_SIZE
- バッファのデフォルトアライメントサイズ(byte) を設定します。

MALLOC_WORD_SIZE
- バッファはかならずワード値以上にアライメントますが、そのワードのサイズを指定します。
 (ちゃんと確認してないが、デフォルトはおそらく4もしくは8(32bit/64bit) でしょう)

MALLOC_STRICT_SIZE
- バッファの終端がページの終端になるように厳密に処理するかどうかを指定します。このオプションを使うと、オーバーランに対するより厳密な検出が出来るようになりますが、Carbon/Cocoa/SSE/Altivec等を使ったコードはうまく動作しなくなる可能性があります。

MALLOC_PERMIT_INSANE_REQUESTS
- 明らかに不正と思われるサイズのリクエスト(100MB以上)に対して、デバッガでトラップするようにします。デフォルトは有効で、このフラグを立てるとチェックが行われなくなります。

MALLOC_CHECK_HEADER
- Guard Mallocが確保したメモリブロックのマジックナンバーをチェックし、破壊確認を行います。

MALLOC_NO_BACKTRACE
- Guard Mallocはメモリ確保時にスタックトレースを保存しますが、この機能をOFFにします。

MallocStackLogging
- このフラグがONになっていると、標準のmallocスタックロギングが有効になります。これにより、malloc_historyコマンドが利用可能になり、それを経由したmalloc/freeイベントの確認が可能になります。MallocStackLoggingの利用は、Guard Mallocが内部で利用するスタックバックトレースの保存とは独立に動作しており、連動はしていないので、MALLOC_NO_BACKTRACEとの依存関係はありません。

Guard Mallocで作成されるチャンクの構造
Guard Mallocで確保されるメモリブロックは、以下のような構造をしています。

メモリブロックの先頭から:
- チャンクサイズ(byte): (0x60(ヘッダサイズ) + 確保サイズ をページ境界に拡張した値) が格納される
- スレッドID
- トップ20フレーム分のメモリ確保時のスタックバックトレース
--- 20フレームより深いところで呼ばれた場合、切り取られて直近の20フレームのみが入る
--- 20フレームより浅いところで呼ばれた場合、未使用部分には0が詰められる
- マジックナンバー(0xdeadbeef)
- ユーザーバッファの先頭 (ここからユーザーに渡されるメモリ)

Guard Mallocの制限
Guard Mallocは、残念ながらなんの制限も課さずに使えるシステム、という訳ではありません。
制限事項も存在します。Guard Mallocの使用には、以下の制限があります。

- メモリブロックが最大500,000個程度しか確保出来ない(ページ数の上限に達するため)
- 非効率なページの使い方をするので、仮想メモリのスワッピング回数が増加し、パフォーマンスに悪影響が出やすい

まとめ
Guard Mallocは面倒なバグの検出を助けてくれる強力なツールです。制限を理解し、オプションを上手く使って楽をしましょう。

Comment

管理者だけに表示を許可する

Trackback

Trackback URL:

http://deathcube.blog36.fc2.com/tb.php/20-1d21b48b