malloc デバッグ・ツール

malloc サブシステムによって割り当てられたメモリーを正しく管理していないアプリケーションのデバッグは、困難かつ単調になることがあります。 その理由は、エラーの挿入とその結果の症状の露出との間には一般に同時性がないためです。

この困難性に加えて、メモリー割り当てが本来持っている複雑さがあります。その複雑さとは、そこでは大量の割り当てが行われたり、行われなかったり、そして (たぶん) 非同期的にしかも同時にアクセスされ、それらはすべて堅固で効果的な同期を必要とするマルチスレッドのコンテキストの中で行われるということです。

デバッグ・ツールが、主に症状の検出のタイミングとエラーの挿入のタイミングを近づけることに焦点をおいているのは、これらの理由によります。 これはアプリケーション開発者が、コードのどの部分がエラーを起こしている原因であるかをより正確にねらうのに役立ちます。

多くの異なったデバッグ・ツールが、malloc で使用するために開発されています。 他のデバッグ・ツールと組み合わせて、すべての割り当てポリシーで使用することができるものも、使用範囲がより限定されているものもあります。 多くのデバッグ・ツールは、プロセスが必要とするリソースの他に追加のリソースを使用します。 必要なときに適切なリソースを提供するのは、アプリケーション開発者の責任です。

パフォーマンスの考慮事項

malloc をデバッグするツールは、常時、持続的に使用したり、システム全体で使用したりするのには適していません。 これらは、デバッグする アプリケーションへのパフォーマンスの影響を最小限に押さえるように設計されていますが、 システム全体の広い範囲で使用すると、システム全体のスループットに重大な悪影響を及ぼす場合があります。 特に、 /etc/environment ファイルに MALLOCDEBUG=catch_overflow を設定することはお勧めできません。これにより、ページング・スペースの過剰使用などの重大なシステム問題が発生する可能性があります。 malloc をデバッグするツールの使用は、一度に 1 つのアプリケーション、 あるいは、小さなアプリケーション・グループのデバッグに制限する必要があります。

malloc をデバッグするツールを使用可能にすると、 実行時にさまざまなチェックの処理が追加されるので、malloc サブシステムのパフォーマンスは (どのツールを使用するかに依存して) 可変で低下しますが、アプリケーションが作動しなくなるほどではありません。 問題が解決したら、malloc をデバッグするツールをオフにして、malloc サブシステムのパフォーマンスを復元させます。

ディスクとメモリーの考慮事項

catch_overflow または Malloc Log ツールを使用可能にすると、malloc サブシステムは非常に大量のメモリーを使用します。

catch_overflow では、各 malloc 要求は 4096 + (unsigned long のサイズの 2 倍) だけ増加し、PAGESIZE マクロの次の倍数に切り上げられます。 catch_overflow は、大変大きなアプリケーションで使用するにはメモリー集約的過ぎるということになるかもしれません。しかし、メモリー・デバッグを必要とする大半のアプリケーションに対しては、メモリーの追加使用は問題にはならないはずです。 大規模なアプリケーションでは、 debug_rangeオプションと functionsetオプションをcatch_overflowに使用すると、メモリ使用量が大幅に削減され、プログラムを段階的にデバッグできます。

Malloc Log では、プロセス内でアクティブなすべての割り当てに対して、割り当て記録が保管されます。 このためのメモリー・オーバーヘッドは、保管されるスタック・ポインターに小さい数値を指定することによって最小化することができます。

デバッグ中のアプリケーションが malloc サブシステムの割り当てルーチンを頻繁に呼び出すと、malloc をデバッグするツールを使用可能にしているためにメモリー使用上の問題が発生し、アプリケーションが単一セグメント内で正常に 実行されない場合があります。 この場合に有効なのは、ulimit コマンドおよび ld コマンドの -bmaxdata オプションを使用して、アプリケーションが追加のメモリーにアクセスできるようにすることです。

malloc をデバッグするツールを使用可能にして実行する目的のために、 以下のようにデータ (-d) とスタック (-s) の両方の変数に対して ulimit を設定します。
ulimit -d unlimited
ulimit -s unlimited 

32 ビット・プロセス用の最大の 8 セグメントを予約するには、-bmaxdata オプションを -bmaxdata:0x80000000 と指定する必要があります。

malloc をデバッグするツールがオフの場合は、ulimit および -bmaxdata にはデフォルト値がリストアされます。

ulimit コマンドおよび -bmaxdata オプションについて詳しくは、 Large Program Supportを参照してください。

malloc をデバッグするツールは、デバッグする状態によっては使用するのが適切ではありません。 いくつかの malloc をデバッグするツールは、割り当てごとに 1 ページ以上のオーバーヘッドを必要とするので、 小さな割り当て要求をたくさん出すプログラムでは、メモリー使用量が急激に増加します。 これらのプログラムでは、 メモリーやページング・スペースの不足によってメモリー割り当て要求が拒否され、別の障害が発生する 場合があります。 これは必ずしもデバッグ中のプログラムで起こるエラーではなく、また malloc をデバッグするツールの エラーでもありません。

具体的な例として X サーバー があげられます。X サーバーは、初期化して作動する間、 おびただしい回数の小さな割り当て要求を出します。 catch_overflow を使用可能にし、X または xinit コマンドを 使用して X サーバーを実行させると、X サーバーは使用可能なメモリー不足のために障害を起こします。 しかし、debug_range または functionset オプションを使用して、少しずつ X をデバッグすることは可能です。 一般的に、X クライアントは catch_overflow を使用可能にして実行しても機能的な問題を起こしません。 catch_overflow を X クライアント・プログラムで使用するには、次の手順に従います。
  1. catch_overflow をオフにして X サーバーを開始する。
  2. ターミナル・ウィンドウを開始する (例: dtterm、xterm、aixterm)。
  3. ターミナル・ウィンドウ・セッションで該当の環境変数を設定して、catch_overflow を使用可能にする。
  4. X クライアント・プログラムを起動し、同じウィンドウからデバッグする。

malloc のデバッグの使用可能化

Debug Malloc はデフォルトでは使用可能になりませんが、MALLOCDEBUG 環境変数を該当のオプションに設定することによって使用可能になり構成されます。 複数のオプションが必要な場合は、オプションをコンマ (,) で区切ることができます。 タンデムで要求されるオプションは、相互に互換性が必要です。

注: デバッグ malloc を使用不可にするには、 unset MALLOCDEBUG コマンドを使用して MALLOCDEBUG 環境変数を設定解除してください。

Malloc デバッグ・ツール

バッファー・オーバーフロー検出

メモリー管理エラーは、ときどきアプリケーション・プログラムが割り当てられたバッファーの終端を超えて書き込みを行うことによって起こることがあります。 これは、しばしば、すぐに結果がでるものではないので、 ずっと後に、上書きされた (通常は他に割り当てられている) メモリーが参照され、本来そこに保管されていたデータが含まれていなくなるまで、症状は現れません。

catch_overflow デバッグ・オプションがあるので、ユーザーは、メモリーの上書き、オーバーリード、重複メモリー解放、および malloc サブルーチンが割り当てた解放済みメモリーの再利用を識別することができます。 catch_overflow ツールが 検出したメモリーの問題は、abort 呼び出しまたはセグメンテーション違反 (SIGSEGV) の結果となります。 多くの場合、エラーが検出されると、アプリケーションはただちに停止し、コア・ファイルが作成されます。

catch_overflow オプションは、以下の割り当てポリシーおよびオプションの割り当てに影響を及ぼします。
  • デフォルト割り当てポリシー
  • Watson 割り当てポリシー
  • Malloc マルチヒープ・オプション
  • Malloc スレッド・キャッシュ・オプション
  • Malloc 特記オプション

catch_overflow デバッグ・オプションは、MALLOCDEBUG=catch_overflow を設定することによって使用可能にします。 これによりメモリー上書きおよびメモリー・オーバーリードの識別がオンになります。

位置合わせ

デフォルトでは、malloc サブルーチンは、2 ワード境界に位置合わせしたポインターを戻します。 これは標準への準拠のためと、位置合わせされていないメモリー・アクセスを受け付けることができないプログラム (例えば、DCE コンポーネントを使用するプログラム) のために必要です。 しかし、catch_overflow オプションのインプリメンテーションが採用しているロジックのために、プログラムが、catch_overflow によって検出されることなしに、位置合わせ値より少ない量によってバッファーを上書きすることが起こり得ます。 align オプションを使用して、 malloc サブシステムに、検出されずにバッファーを上書きできるバイト数を減らすか除去するために、 このデフォルトの位置合わせを無視するように指示することができます。 カスタム位置合わせを 0 から 4096 (4096 も含む) までの間の 2 の累乗 (例えば、0、1、2、4、...、4096) で指定できます。 値 0 と 1 は同じものとして扱われます。すなわち、メモリーの位置合わせは行われません。したがって、割り当てられたエリアを越えるメモリー・アクセスは SEGFAULT を起こします。

align オプションは catch_overflow オプションの一部であり、 catch_overflow が使用可能である場合にのみ意味があります。 デフォルト以外の位置合わせを使用可能にするには、MALLOCDEBUG 環境変数を次のように設定します。
MALLOCDEBUG=catch_overflow,align:n
ここで、n は要求する位置合わせです。
catch_overflow オプションが、ある割り当て要求に対して、何バイトまでのオーバーリードまたは上書きを許すかを計算するには、次の公式を使用します。ここで、n が要求する位置合わせで size が割り当てるバイト数です。
((((size / n) + 1) * n) - size) % n
次の例は、catch_overflow オプションが使用可能な場合の、アプリケーションがオーバーリードまたは上書きを実行する能力に関する align オプションの効果を説明しています。 この例では、align オプションで値 2 を指定しています。
MALLOCDEBUG=align:2,catch_overflow
catch_overflow オプションは、オーバーリードおよび上書きを次のように処理します。
  • 偶数バイト数を割り当てる場合は、malloc は要求されたとおりのバイト数を割り当てますが、0 バイトのオーバーリードまたは上書きも許されます。
  • 奇数バイト数を割り当てる場合は、malloc は要求されたバイト数に加えて、 必要な位置合わせができるように、追加の 1 バイトを割り当てます。 これは、1 バイトのオーバーリードまたは上書きを許します。
シグナル処理の指定変更
catch_overflow オプションは、以下のいずれかの方法でエラーを報告します。
  • メモリー・アクセス・エラー (割り当てられたメモリーの終端を超えて書き込みや読み取りを行おうとした場合など) では、 セグメンテーション違反 (SIGSEGV) を発生させて、コア・ダンプを取ります。
  • 他のタイプのエラー (既に解放されているスペースを解放しようとした場合など) では、catch_overflow オプションは、エラー・メッセージを出力してから、abort 関数を呼び出して SIGIOT シグナルを送って現行のプロセスを終了させます。

呼び出し側プログラムが、SIGSEGV および SIGIOT シグナルをブロックしたり、キャッチしたりしている場合は、catch_overflow オプションはエラーを報告することができません。 override_signal_handling オプションは、アプリケーションをコーディングし直したり、再ビルドしたりしないで、この状態をバイパスする手段を提供します。

override_signal_handling オプションが指定された場合は、catch_overflow オプションは、 malloc サブシステム・ルーチンへの呼び出しがあるたびに以下のアクションを実行します。
  1. SIGSEGV または SIGIOT 用にアプリケーションが設定した既存のすべてのシグナル・ハンドラーを使用不可にする。
  2. SIGIOT および SIGSEGV の両方に対するアクションをデフォルト (SIG_DFL) に設定する。
  3. SIGIOT および SIGSEGV の両方を非ブロックにする。

アプリケーション・シグナル・ハンドラーがメモリー割り当てルーチンの呼び出しと呼び出しの間に SIGSEGV に対するアクションを変更して、 無効なメモリー・アクセスを行うと、catch_overflow オプションはエラーを報告することができません (アプリケーションは終了せず、コア・ファイルは作成されません)。

注:
  1. override_signal_handling オプションは、スレッド化されたアプリケーション環境では効果がない場合があります。なぜなら、catch_overflow オプションは sigprocmask サブルーチンを使用し、多くのスレッド化されたプロセスは pthread_sigmask サブルーチンを使用するからです。
  2. スレッドが、シグナル・セットに SIGSEGV と SIGIOT を組み込まないで sigwait サブルーチンを呼び出し、続いて catch_overflow オプションがエラーを検出した場合は、catch_overflow オプションが SIGSEGV または SIGIOT だけしか生成できないので、そのスレッドはハングします。
  3. 無効なメモリーへのポインターがカーネル・ルーチンへ渡されると、カーネル・ルーチンは、 障害を起こし、通常、errno に EFAULT をセットして戻ります。 アプリケーションがシステム・コールからの戻りを検査していない場合、このエラーは 検出されない恐れがあります。
デバッグ範囲

catch_overflow オプションが使用可能な場合は、バッファー・オーバーフロー検出が、デフォルトで、プログラムでのすべての割り当てに対して実行されます。 debug_range オプションが指定されている場合は、ユーザー定義の最小と最大サイズ間に入る割り当て要求だけが、catch_overflow オプションによってバッファー・オーバーフローを検出されます。 それ以外の場合は、 バッファー・オーバーフロー検出は実行されません。 このオプションを使用して、ユーザーは、特定のケースではツールを使用するだけで、catch_overflow オプションが 使用する追加のメモリー・リソースの量を制御することができます。

debug_range オプションは、catch_overflow オプションのコンテキスト内のみで意味があります。 これは次のようにして使用可能にします。
MALLOCDEBUG=catch_overflow,debug_range:min:max
ここで、min はバッファー・オーバーフロー検出が実行される範囲の下限で、max はその上限です。 最小値として 0 が指定された場合は、最大値より小さいすべての要求でバッファー・オーバーフロー検出が実行されます。 最大値として 0 が指定された場合は、最小値より大きいすべての要求でバッファー・オーバーフロー検出が実行されます。

制限: 内部実装要件のため、各割り振りの長さは、少なくとも 1 ページ・サイズにする必要があります。 したがって、debug_range オプションは、それを除去するというよりむしろ、 単に catch_overflow オプションのオーバーヘッドを減らすだけです。

realloc サブルーチンが、ユーザー指定の範囲に入る割り当て要求で呼び出された場合は、たとえ元の割り当てが指定された範囲内になかったとしても、バッファー・オーバーフロー検出が実行されます。 その逆もまた真です。

注: override_signal オプションを debug_range オプションと一緒に設定すると、SIGIOT および SIGSEGV シグナル動作のオーバーライドがすべての割り振りに対して実行されます。
関数セット

内部のインプリメンテーションの必要によって、いまだに、各割り当ては必ず少なくとも 1 ページ・サイズの長さになります。 したがって、functionset オプションは、それを除去するというよりむしろ、 単に catch_overflow オプションのオーバーヘッドを減らすだけです。

realloc サブルーチンが、ユーザー指定の関数リストのメンバーの関数から呼び出された場合は、たとえ元の割り当てが指定された関数から行われていなかったとしても、バッファー・オーバーフロー検出が実行されます。 その逆もまた真です。

注: override_signal オプションを functionset オプションと一緒に設定すると、SIGIOT および SIGSEGV シグナル動作のオーバーライドがすべての割り振りに対して実行されます。

functionset オプションは、リスト内に指定された関数の妥当性は検査しません。

オーバーリーディングを許可

デフォルトでは、catch_overflow デバッグ・オプションが使用可能にされていて、呼び出し側プログラムが割り当てられたメモリーの終端を超えて読み取りを行った場合は、セグメンテーション違反が起こり、プロセスはコア・ダンプを取ります。 しかし、 ユーザーはこのタイプのエラーをキャッチすることには興味がなく、さらに危険な上書きをキャッチするために、catch_overflow を使用可能にさせることがあります。 allow_overreading オプションを指定することにより、catch_overflow オプションがオーバーリードを無視するようになり、さらに重大と考えられる他のタイプのエラーを最初に検出することができます。

allow_overreading オプションは、catch_overflow オプションのコンテキスト内のみで意味があります。 これは次のようにして使用可能にします。
MALLOCDEBUG=catch_overflow,allow_overreading,
postfree_checking

postfree_checking オプションは、大量の追加メモリーを使用します。 非常に大量のメモリーを必要とするプログラムは、postfree_checking オプションを使用できない可能性があります。

Malloc trace

Malloc Trace は、システム・トレース機能を介して、malloc サブシステム API へのすべての呼び出しをトレースできるようにするために設計されたデバッグ・オプションです。

マロー・ログ

Malloc Log は、malloc サブシステム内でアクティブな割り当ての実行時データベースを、 ユーザーに提供するために設計されたデバッグ・オプションです。

report_allocations

report_allocations オプションは、アプリケーション・プログラムでのメモリー・リークを検出するツールです。 report_allocations オプションは、Malloc Log によって構成されたデータベースを使用して、現在ユーザーが保持している割り当てのリストを報告します。 正常終了した各割り当てのレコードは、Malloc Log による要求時に作成されます。 割り当てが割り当て解除されると、Malloc Log はデータベースからそのレコードを除去します。 プロセスの終了時に、 まだアクティブな割り当てのリストは stderr に出力され、呼び出し元によって解放されなかった割り当てのリストが提供されます。

report_allocations オプションは、動作するために Malloc Log の機能を必要とします。 したがって、 Malloc Log は、report_allocations が使用可能にされるとき、暗黙的に使用可能にされます。 report_allocations オプションは、 次のようにして使用可能にします。
MALLOCDEBUG=report_allocations
validate_ptrs

デフォルトでは、malloc サブシステム API は、それらが事前に割り当てられたメモリーを実際に参照していることを確認するための、入力ポインターの妥当性検査は行いません。 これらのポインターのうちの 1 つが無効な場合、重大なヒープ破壊が起こることになります。 validate_ptrs オプションを指定すると、malloc サブシステム API は、入力ポインターの広範囲な妥当性検査を実行します。 ポインターが無効なことが分かった (すなわち、malloc サブシステム API の呼び出しによって事前に割り当てられたメモリーを参照していない) 場合、無効な理由を示したエラー・メッセージが表示され、abort 関数が呼び出され、コア・ファイルが作成されます。 validate_ptrs オプションは、verbose サブオプションに似ています。 postfree_checking オプションが使用可能になっている場合、validate_ptrs オプションは有効になりません。

validate_ptrs オプションは、次のようにして使用可能にします。
MALLOCDEBUG=validate_ptrs
Malloc detect

Malloc Detect は、malloc サブシステム API のすべての呼び出しで、malloc サブシステムの内部データ構造の破壊を検出して報告するために設計されたデバッグ・オプションです。

verbose

Malloc Detect のサブオプションです。

チェッカーアリーナ

Malloc Detect のサブオプションです。

出力 (output)

デフォルトで、malloc デバッグ・オプションは出力を stderr に送ります。 これは、 すべてのプログラムにとっては望ましくないことがあります。 output オプションを使用して、表示される情報の代替宛先を指定することができます。 出力は、stderr、stdout、またはシステム上の任意のファイルのいずれかに送られます。

output オプションは、次のようにして使用可能にします。
MALLOCDEBUG=output:<filename>
続く

多くの malloc デバッグ・オプションは、エラーを検出すると、abort() を呼び出します。 これは、必ずしもすべてのプログラムにとって望ましい動作とは限りません。 continue オプションを使用して、同期エラーの検出の後でプロセスを異常終了するのではなく続行するように、malloc サブシステムに指示することができます。 それでも、エラー・メッセージは該当のチャネルにログされます。

continue オプションは、次のようにして使用可能にします。
MALLOCDEBUG=continue
Malloc デバッグ充てん

malloc debug fill は、デバッグを目的として、malloc() 呼び出しによって割り当てられたメモリーをユーザー指定のパターンで満たすように設計されたデバッグ・オプションです。

パターンは文字列として指定する必要があり (例えば、export MALLOCDEBUG=fill:”abc” と指定すると、malloc によって割り当てられたメモリーがパターン “abc” で設定されます)、最大 128 文字を使用できます。 このパターンを指定しない場合、fill オプションは無視されます。

malloc debug fill オプションは、次のようにして使用可能にできます。

MALLOCDEBUG=fill:pattern

パターンは、ストリング形式で指定される 8 進数または 16 進数です。 すなわち、パターン “&#xa5;101” は文字「A」の 8 進表記として処理され、パターン “&#xa5;x41” は文字「A」の 16 進表記として処理されます。

無効な 8 進数が指定された場合、例えば、1 バイトに収まらない &#xa5;777 などは、1 バイトとして保管できる最大 8 進値である &#xa5;377 として保管されます。