この節では、メモリーリークとデータ破壊の原因などの拡張メモリーの解析について説明します。
::findleaks dcmd を使用して、フルセットの kmem デバッギング機能が有効になっている場合に、カーネルクラッシュダンプの際に効率的にメモリーリークの検出を行うことができます。::findleaks の最初の実行では、ダンプを処理してメモリーリークを探します。この処理には数分かかる場合があります。次に、割り当てスタックトレース別にリークがまとめられます。findleaks レポートには、識別されたメモリーリークごとに bufctl アドレスと先頭のスタックフレームが示されます。
> ::findleaks CACHE LEAKED BUFCTL CALLER 70039ba8 1 703746c0 pm_autoconfig+0x708 70039ba8 1 703748a0 pm_autoconfig+0x708 7003a028 1 70d3b1a0 sigaddq+0x108 7003c7a8 1 70515200 pm_ioctl+0x187c ---------------------------------------------------------------------- Total 4 buffers, 376 bytes |
bufctl ポインタを使用し、bufctl_audit マクロを適用して、その割り当ての完全なスタックバックトレースを得ることができます。
> 70d3b1a0$<bufctl_audit 0x70d3b1a0: next addr slab 70a049c0 70d03b28 70bb7480 0x70d3b1ac: cache timestamp thread 7003a028 13f7cf63b3 70b38380 0x70d3b1bc: lastlog contents stackdepth 700d6e60 0 5 0x70d3b1c8: kmem_alloc+0x30 sigaddq+0x108 sigsendproc+0x210 sigqkill+0x90 kill+0x28 |
プログラマは、通常、bufctl_audit 情報と割り当てスタックトレースの割り当てを使用して、そのバッファのリークの原因となったコードパスをすばやく突き止めることができます。
メモリー破壊の診断を行う際は、他のどのカーネルエンティティが特定のポインタのコピーを保持しているかを知る必要があります。これは重要なことです。なぜならデータ構造体が解放された後どのスレッドがこれにアクセスしたかを明らかにできるからです。また、特定の (有効な) データ項目の知識をどのカーネルエンティティが共有しているかを知ることが容易になります。このためには ::whatis dcmd と ::kgrep dcmd を使用します。次のようにして、問題の値に対して ::whatis を適用します。
> 0x705d8640::whatis 705d8640 is 705d8000+640, allocated from kmem_va_8192 705d8640 is 705d8640+0, allocated from streams_mblk |
この場合は、0x705d8640 が STREAMS mblk 構造体へのポインタであることが明らかになりました。この割り当ては、kmem_va 仮想記憶領域の前の段階の kmem キャッシュである kmem_va_8192 キャッシュにも見られます。::kmastat dcmd を使用すれば、kmem キャッシュと vmem 領域のリストが表示されます。::kgrep を使用して、この mblk へのポインタを含む他のカーネルアドレスを突き止めることができます。これによって、システムのメモリー割り当ての階層的特徴が明らかになります。一般的に、特殊な kmem キャッシュの名前から、そのアドレスによって参照されるオブジェクトのタイプを判断することができます。
> 0x705d8640::kgrep 400a3720 70580d24 7069d7f0 706a37ec 706add34 |
再び ::whatis を適用します。
> 400a3720::whatis 400a3720 is in thread 7095b240's stack > 706add34::whatis 706add34 is 706ac000+1d34, allocated from kmem_va_8192 706add34 is 706add20+14, allocated from streams_dblk_120 |
1 つのポインタは既知のカーネルスレッドのスタック上にあり、もう 1 つのポインタは対応する STREAMS dblk 構造体の内部の mblk ポインタであることがわかりました。
MDB の ::kmem_verify dcmd を使用すると、kmem アロケータが実行時に行う検査とほぼ同じ検査を行います。::kmem_verify を起動して、該当する kmem_flags が設定されている場合にすべての kmem キャッシュを走査し、あるいは特定のキャッシュを調べることができます。
::kmem_verify を使用して問題を突き止める例を、以下に示します。
> ::kmem_verify Cache Name Addr Cache Integrity kmem_alloc_8 70039428 clean kmem_alloc_16 700396a8 clean kmem_alloc_24 70039928 1 corrupt buffer kmem_alloc_32 70039ba8 clean kmem_alloc_40 7003a028 clean kmem_alloc_48 7003a2a8 clean ... |
::kmem_verify によれば、明らかに kmem_alloc_24 キャッシュには問題が存在します。明示的なキャッシュ引数を指定すると、::kmem_verify dcmd はこの問題に関するより詳細な情報を提供します。
> 70039928::kmem_verify Summary for cache 'kmem_alloc_24' buffer 702babc0 (free) seems corrupted, at 702babc0 |
次に、::kmem_verify によって障害があると認識されたバッファを調べます。
> 0x702babc0,5/KKn 0x702babc0: 0 deadbeef deadbeef deadbeef deadbeef deadbeef feedface feedface 703785a0 84d9714e |
::kmem_verify がこのバッファにフラグを立てた理由が明らかになりました。バッファの最初のワード (0x702babc0 で始まる) には、0xdeadbeef のパターンが使用されるはずであったのに、0 が使用されています。この時点で、このバッファの bufctl_audit を調べることによって、このバッファにどのコードが最近書き込みを行なったか、どこでいつ解放されたかについての手がかりが得られます。
この状況でのもう 1 つの有用な手法は、::kgrep を使用してアドレス空間を調べてアドレス 0x702babc0 への参照を検索し、この解放されたデータへの参照を依然として保持しているスレッドまたはデータ構造体を発見することです。
キャッシュの KMF_AUDIT が設定されている場合、カーネルメモリーのアロケータは、アクティビティの最近の履歴を記録するログを維持します。このトランザクションログには、bufctl_audit レコードが記録されます。KMF_AUDIT と KMF_CONTENTS の両方のフラグが設定されている場合には、アロケータは、割り当て済みバッファと解放されたバッファの実際の内容の一部を記録したログを生成します。このログの構造と使用法については、このマニュアルでは記載していません。この節では、トランザクションログについて説明します。
MDB は、トランザクションログを表示するための複数の機能を備えています。最も簡単な方法は、::walk kmem_log です。これは、このログに記録されているトランザクションを一連の bufctl_audit_t ポインタの形で出力します。
> ::walk kmem_log 70128340 701282e0 70128280 70128220 701281c0 ... > 70128340$<bufctl_audit 0x70128340: next addr slab 70ac1d40 70bc4ea8 70bb7c00 0x7012834c: cache timestamp thread 70039428 e1bd7abe721 70aacde0 0x7012835c: lastlog contents stackdepth 701282e0 7018f340 4 0x70128368: kmem_cache_free+0x24 nfs3_sync+0x3c vfs_sync+0x84 syssync+4 |
トランザクションログ全体を表示するもっと簡潔な方法は、::kmem_log コマンドを使用することです。
> ::kmem_log CPU ADDR BUFADDR TIMESTAMP THREAD 0 70128340 70bc4ea8 e1bd7abe721 70aacde0 0 701282e0 70bc4ea8 e1bd7aa86fa 70aacde0 0 70128280 70bc4ea8 e1bd7aa27dd 70aacde0 0 70128220 70bc4ea8 e1bd7a98a6e 70aacde0 0 701281c0 70d03738 e1bd7a8e3e0 70aacde0 ... 0 70127140 70cf78a0 e1bd78035ad 70aacde0 0 701270e0 709cf6c0 e1bd6d2573a 40033e60 0 70127080 70cedf20 e1bd6d1e984 40033e60 0 70127020 70b09578 e1bd5fc1791 40033e60 0 70126fc0 70cf78a0 e1bd5fb6b5a 40033e60 0 70126f60 705ed388 e1bd5fb080d 40033e60 0 70126f00 705ed388 e1bd551ff73 70aacde0 ... |
::kmem_log の出力は、時刻表示の降順にソートされます。ADDR 欄は、このトランザクションに対応する bufctl_audit 構造体です。BUFADDR は、実際のバッファを指しています。
これらの数字は、バッファに対するトランザクション (割り当てと解放) を表しています。特定のバッファが壊れた場合、トランザクションログの中でそのバッファを突き止め、そのトランザクションを行なったスレッドが他のどのトランザクションにかかわっていたかを判断することは有用です。このことは、バッファの割り当て (または解放) の前後に発生したイベントのシーケンスの全体像を理解するのに役立ちます。
::bufctl コマンドを使用して、トランザクションログの調査の出力をフィルタリングすることができます。::bufctl -a コマンドは、トランザクションログの中のバッファをバッファアドレスによってフィルタリングします。次の例は、バッファ 0x70b09578 のフィルタリングの結果です。
> ::walk kmem_log | ::bufctl -a 0x70b09578 ADDR BUFADDR TIMESTAMP THREAD CALLER 70127020 70b09578 e1bd5fc1791 40033e60 biodone+0x108 70126e40 70b09578 e1bd55062da 70aacde0 pageio_setup+0x268 70126de0 70b09578 e1bd52b2317 40033e60 biodone+0x108 70126c00 70b09578 e1bd497ee8e 70aacde0 pageio_setup+0x268 70120480 70b09578 e1bd21c5e2a 70aacde0 elfexec+0x9f0 70120060 70b09578 e1bd20f5ab5 70aacde0 getelfhead+0x100 7011ef20 70b09578 e1bd1e9a1dd 70aacde0 ufs_getpage_miss+0x354 7011d720 70b09578 e1bd1170dc4 70aacde0 pageio_setup+0x268 70117d80 70b09578 e1bcff6ff27 70bc2480 elfexec+0x9f0 70117960 70b09578 e1bcfea4a9f 70bc2480 getelfhead+0x100 ... |
この例は、特定のバッファが多くのトランザクションに使用される場合があることを示しています。
kmem トランザクションログは、カーネルメモリーアロケータが行なったトランザクションのすべての記録ではないことを忘れないでください。ログのサイズを一定に保つために、ログの中の古い記録は消去されます。
::allocdby dcmd と ::freedby dcmd を使用して、特定のスレッドに関連するトランザクションの要約を示すことができます。次の例では、スレッド 0x70aacde0 によって行われた最近の割り当てのリストが示されています。
> 0x70aacde0::allocdby BUFCTL TIMESTAMP CALLER 70d4d8c0 e1edb14511a allocb+0x88 70d4e8a0 e1edb142472 dblk_constructor+0xc 70d4a240 e1edb13dd4f allocb+0x88 70d4e840 e1edb13aeec dblk_constructor+0xc 70d4d860 e1ed8344071 allocb+0x88 70d4e7e0 e1ed8342536 dblk_constructor+0xc 70d4a1e0 e1ed82b3a3c allocb+0x88 70a53f80 e1ed82b0b91 dblk_constructor+0xc 70d4d800 e1e9b663b92 allocb+0x88 |
bufctl_audit レコードを調べることにより、特定のスレッドの最近のアクティビティを理解することができます。