ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 11.1 Information Library (日本語) |
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
23. デバイスドライバのデバッグ、テスト、およびチューニング
Oracle Solaris Ethernet ドライバのカーネル統計
このセクションでは、デバイスドライバに適用可能な 2 つのデバッガについて説明します。両デバッガの詳細については、『Oracle Solaris Modular Debugger Guide 』を参照してください。
kmdb(1) カーネルデバッガは、ブレークポイント、ウォッチポイント、シングルステップ実行などの典型的な実行時デバッガ機能を提供します。 kmdb デバッガは、以前のリリースで使用可能だった kadb を置き換えるものです。kmdb では新しい機能のほかに、kadb で以前に使用可能だったコマンドも使用されています。kadb はブート時にしかロードできませんでしたが、kmdb はいつでもロードできます。kmdb デバッガは、実行制御機能を備えているため、ライブでの対話式デバッグに適しています。
mdb(1) モジュラーデバッガは、リアルタイムデバッガとしては kmdb よりも制限がありますが、mdb には事後デバッグ用の豊富な機能が備わっています。
デバッガ kmdb と mdb は基本的に同じユーザーインタフェースを共有しています。したがって、両ツールの同じコマンドでは、多くのデバッグ手法が適用可能となっています。どちらのデバッガもマクロ、dcmd、および dmod をサポートしています。dcmd (「ディーコマンド」と発音) は、現在のターゲットプログラムのすべてのプロパティーにアクセス可能なデバッガ内のルーチンです。dcmd は実行時に動的にロードできます。デバッガモジュールの短縮形である dmod は dcmd をパッケージ化したものであり、標準以外の動作を提供するためにロードできます。
mdb と kmdb はどちらも、adb や kadb といった旧バージョンのデバッガと下位互換性を保っています。mdb デバッガは、kmdb で使用可能なすべてのマクロを実行できるだけでなく、adb 向けの旧バージョンの任意のユーザー定義マクロも実行できます。標準マクロセットの検索場所については、『Oracle Solaris モジューラデバッガ』を参照してください。
事後解析は、ドライバ開発者にさまざまなメリットを提供します。1 つの問題を複数の開発者が並行して検査できます。単一のクラッシュダンプに対してデバッガの複数のインスタンスを同時に使用できます。解析をオフラインで実行できるため、可能な場合にはクラッシュしたシステムを稼働状態に戻すことができます。事後解析では、ユーザーが開発したデバッガ機能を dmod の形式で使用できます。kmdb などのリアルタイムデバッガではメモリー消費量が多すぎて使用できない機能でも、dmod ではバンドルできます。
kmdb がロードされた状態でシステムがパニック状態になると、その調査をただちに行えるようにデバッガに制御が渡されます。kmdb で現在の問題を解析するのが適切でないと考えられる場合、推奨される方針は、:c を使用して実行を継続し、クラッシュダンプを保存することです。システムがリブートしたら、保存したクラッシュダンプに対して mdb を使用して事後解析を実行できます。この処理は、アプリケーションのクラッシュをプロセスコアファイルに基づいてデバッグするのに似ています。
注 - 以前のバージョンの Oracle Solaris オペレーティングシステムでは、adb(1) が事後解析の推奨ツールでした。現在の Oracle Solaris オペレーティングシステムでは、mdb(1) が事後解析の推奨ツールです。mdb() の機能セットは、旧バージョンの crash(1M) ユーティリティーのコマンドセットよりも優れています。Oracle Solaris オペレーティングシステムでは crash ユーティリティーは使用できなくなりました。
kmdb デバッガは対話型のカーネルデバッガであり、次の機能を提供します。
カーネル実行の制御
カーネル状態の検査
コードのライブ変更
このセクションでは、ユーザーがすでに kmdb デバッガについて精通していることを前提にしています。このセクションの重点は、デバイスドライバの設計時に役立つ kmdb 機能にあります。kmdb の使用方法について詳しく学ぶには、kmdb(1) のマニュアルページと『Oracle Solaris Modular Debugger Guide 』を参照してください。kadb に精通している場合は、kadb(1M) のマニュアルページを参照し、kadb と kmdb の主な違いを確認してください。
kmdb デバッガのロードやアンロードは、任意で実行できます。kmdb のロードやアンロードの手順については、『Oracle Solaris モジューラデバッガ』を参照してください。安全性と利便性の観点から、代替カーネルでのブートを強くお勧めします。このセクションで説明するように、ブート処理は SPARC プラットフォームと x86 プラットフォームとでわずかに異なります。
注 - kmdb はデフォルトで、kmdb の実行中のプロンプトとして CPU ID を使用します。この章の例では、特に明記されていないかぎり、[0] をプロンプトとして使用しています。
kmdb と代替カーネルの両方を使用して SPARC システムをブートするには、次のいずれかのコマンドを使用します。
boot kmdb -D kernel.test/sparcv9/unix boot kernel.test/sparcv9/unix -k
kmdb と代替カーネルの両方を使用して x86 システムをブートするには、次のいずれかのコマンドを使用します。
b kmdb -D kernel.test/unix b kernel.test/unix -k
ブレークポイントを設定するには、次の例に示すように bp コマンドを使用します。
例 23-5 kmdb での標準ブレークポイントの設定
[0]> myModule`myBreakpointLocation::bp
ターゲットモジュールがロードされていない場合、この状態を示すエラーメッセージが表示され、ブレークポイントは作成されません。この場合には遅延ブレークポイントを使用できます。遅延ブレークポイントは、指定されたモジュールがロードされた時点で自動的に有効になります。遅延ブレークポイントを設定するには、bp コマンドのあとにターゲット位置を指定します。次に遅延ブレークポイントの例を示します。
例 23-6 kmdb での遅延ブレークポイントの設定
[0]>::bp myModule`myBreakpointLocation
ブレークポイントの使用方法の詳細については、『Oracle Solaris モジューラデバッガ』を参照してください。次の 2 つの行のいずれかを入力してもヘルプが得られます。
> ::help bp > ::bp dcmd
kmdb(1M) デバッガでサポートされているマクロを使用すると、カーネルデータ構造体を表示できます。kmdb のマクロを表示するには $M を使用します。マクロは次の形式で使用します。
[ address ] $<macroname
注 - これらのマクロから表示される情報もその表示形式も、インタフェースの一部ではありません。したがって、その情報と形式は常に変更される可能性があります。
次の表に含まれる kmdb マクロは、デバイスドライバの開発者に特に役立ちます。便宜上、該当する場合は旧バージョンのマクロ名も示します。
表 23-1 kmdb マクロ
|
::devinfo dcmd で表示されるノード状態は、次のいずれかの値を取ります。
ドライバの attach(9E) ルーチンが正常に復帰しました。
このノードはドライバにバインドされていますが、ドライバの probe(9E) ルーチンがまだ呼び出されていません。
親のネクサスがドライバ用のバスアドレスを割り当てました。実装に固有の初期化が完了しています。ドライバの probe(9E) ルーチンは、現時点でまだ呼び出されていません。
このデバイスノードがカーネルのデバイスツリー内にリンクされましたが、システムはまだこのノードのドライバを検出していません。
ドライバの probe(9E) ルーチンが正常に復帰しました。
このデバイスは完全に構成されています。
mdb(1) モジュラーデバッガは、次のタイプのファイルに適用できます。
ライブのオペレーティングシステムコンポーネント
オペレーティングシステムのクラッシュダンプ
ユーザープロセス
ユーザープロセスのコアダンプ
オブジェクトファイル
mdb デバッガは、カーネルの問題を解析するための高度なデバッグサポートを提供します。このセクションでは mdb 機能の概要を説明します。mdb の完全な説明については、『Oracle Solaris Modular Debugger Guide』を参照してください。
mdb を使用してライブカーネル状態を変更することも可能ですが、mdb には、kmdb で提供されているカーネル実行制御の機能がありません。このため、実行時デバッグには kmdb をお勧めします。mdb デバッガは通常、静的な状況で使用されます。
注 - mdb のプロンプトは > です。
mdb デバッガにはデバッガモジュールを実装するための拡張プログラミング API が用意されているため、ドライバ開発者はカスタムのデバッグサポートを実装できます。mdb デバッガには、コマンド行編集、コマンド履歴、出力ページャー、オンラインヘルプなど、多数のユーザビリティー機能も用意されています。
注 - adb マクロは使用するべきではありません。その機能の大部分が、mdb の dcmd に置き換えられました。
mdb デバッガには一連の豊富なモジュールや dcmd が用意されています。これらのツールを使用すると、Oracle Solaris カーネル、任意の関連モジュール、およびデバイスドライバをデバッグできます。これらの機能を使用すると、次のようなタスクを実行できます。
複雑なデバッグクエリーを構築する
特定のスレッドによって割り当てられたすべてのメモリーを検出する
カーネル STREAM のビジュアル画像を出力する
特定のアドレスが参照している構造タイプを判定する
カーネルの中でリークしているメモリーブロックを検出する
スタックトレースを検出するためのメモリーを分析する
カスタマイズされた操作を作成するために、複数の dcmd を組み合わせて dmod と呼ばれるモジュールを構築する
使用を開始するには、次の例に示すように、クラッシュディレクトリに移動して mdb と入力し、システムクラッシュダンプを指定します。
例 23-7 クラッシュダンプに対する mdb の呼び出し
% cd /var/crash/testsystem % ls bounds unix.0 vmcore.0 % mdb unix.0 vmcore.0 Loading modules: [ unix krtld genunix ufs_log ip usba s1394 cpc nfs ] > ::status debugging crash dump vmcore.0 (64-bit) from testsystem operating system: 5.10 Generic (sun4u) panic message: zero dump content: kernel pages only
mdb からの応答として > プロンプトが返されたら、コマンドを実行できます。
ライブシステム上の実行中のカーネルを検査するには、次のようにシステムプロンプトから mdb を実行します。
例 23-8 実行中のカーネルに対する mdb の呼び出し
# mdb -k Loading modules: [ unix krtld genunix ufs_log ip usba s1394 ptm cpc ipc nfs ] > ::status debugging live kernel (64-bit) on testsystem operating system: 5.10 Generic (sun4u)
このセクションでは便利なデバッグタスクの例について説明します。このセクションに含まれるタスクは、特に明記されていないかぎり、mdb、kmdb のいずれかを使用して実行できます。このセクションでは、ユーザーに kmdb と mdb の使用に関する基礎知識があることを前提にしています。ここで示す情報は、使用するシステムの種類に依存します。これらの例を作成する際には、64 ビットカーネルが稼働する Sun Blade 100 ワークステーションを使用しました。
注意 - カーネル構造体のデータを変更すると不可逆なデータ破壊が起こる可能性があるため、細心の注意を払うべきです。Oracle Solaris DDI の一部でない構造体に含まれるデータを変更したり、そのようなデータに依存したりしないようにしてください。Oracle Solaris DDI の一部である構造体については、Intro(9S) のマニュアルページを参照してください。 |
kmdb デバッガを使用すると、マシンのレジスタを 1 つのグループとして表示することも、個々のレジスタ単位で表示することもできます。すべてのレジスタを 1 つのグループとして表示するには、次の例に示すように $r を使用します。
例 23-9 kmdb による SPARC プロセッサ上のすべてのレジスタの読み取り
[0]: $r g0 0 l0 0 g1 100130a4 debug_enter l1 edd00028 g2 10411c00 tsbmiss_area+0xe00 l2 10449c90 g3 10442000 ti_statetbl+0x1ba l3 1b g4 3000061a004 l4 10474400 ecc_syndrome_tab+0x80 g5 0 l5 3b9aca00 g6 0 l6 0 g7 2a10001fd40 l7 0 o0 0 i0 0 o1 c i1 10449e50 o2 20 i2 0 o3 300006b2d08 i3 10 o4 0 i4 0 o5 0 i5 b0 sp 2a10001b451 fp 2a10001b521 o7 1001311c debug_enter+0x78 i7 1034bb24 zsa_xsint+0x2c4 y 0 tstate: 1604 (ccr=0x0, asi=0x0, pstate=0x16, cwp=0x4) pstate: ag:0 ie:1 priv:1 am:0 pef:1 mm:0 tle:0 cle:0 mg:0 ig:0 winreg: cur:4 other:0 clean:7 cansave:1 canrest:5 wstate:14 tba 0x10000000 pc edd000d8 edd000d8: ta %icc,%g0 + 125 npc edd000dc edd000dc: nop
デバッガは各レジスタの値を、そのレジスタと同じ名前を持つ変数にエクスポートします。変数を読み取ると、レジスタの現在の値が返されます。変数に書き込みを行うと、関連するマシンレジスタの値が変更されます。次の例では、x86 マシン上の %o0 レジスタの値を、0 から 1 に変更しています。
例 23-10 kmdb による x86 マシン上のレジスタの読み取りと書き込み
[0]> &<eax=K c1e6e0f0 [0]> 0>eax [0]> &<eax=K 0 [0]> c1e6e0f0>eax
別のプロセッサのレジスタを検査する必要がある場合は、::cpuregs dcmd を使用できます。次の例に示すように、検査対象となるプロセッサの ID は、この dcmd へのアドレスとして指定することも、-c オプションの値として指定することもできます。
例 23-11 別のプロセッサのレジスタの検査
[0]> 0::cpuregs %cs = 0x0158 %eax = 0xc1e6e0f0 kmdbmod`kaif_dvec %ds = 0x0160 %ebx = 0x00000000
次の例では、SPARC マシン上でプロセッサを 0 から 3 に切り替えています。%g3 レジスタが検査されたあと、クリアされています。新しい値を確認するため、%g3 が再度読み取られています。
例 23-12 指定されたプロセッサからの、特定のレジスタ値の取得
[0]> 3::switch [3]> <g3=K 24 [3]> 0>g3 [3]> <g3 0
::findleaks dcmd は、カーネルクラッシュダンプ内のメモリーリークを検出する強力で効率的な機能を提供します。カーネルメモリーデバッグ機能のフルセットを有効化しないと、::findleaks が有効になりません。詳細については、「kmem_flags デバッグフラグの設定」を参照してください。ドライバの開発中やテスト中に ::findleaks を実行すると、メモリーリークが発生したためにカーネルリソースが浪費されているコードが検出されます。『Oracle Solaris Modular Debugger Guide』の第 9 章「Debugging With the Kernel Memory Allocator」Oracle Solaris Modular Debugger Guide を参照してください。
注 - カーネルメモリーリークが発生したコードがあると、サービス拒否攻撃に対するシステムの脆弱性が高まる可能性があります。
mdb デバッガには、ドライバのデバッグ用にカスタマイズ可能なデバッガ機能を実装するための強力な API が用意されています。このプログラミング API の詳細については、『Oracle Solaris モジューラデバッガ』で説明されています。
SUNWmdbdm パッケージは、ディレクトリ /usr/demo/mdb に mdb のサンプルソースコードをインストールします。mdb を使用すると、ドライバが正しく動作していることを確認するための時間のかかるデバッグ作業やデバッグ支援を自動化できます。また、mdb デバッグモジュールをドライバ製品と一緒にパッケージ化することもできます。パッケージ化すると、サービス担当者がこれらの機能を顧客サイトで使用できるようになります。
Oracle Solaris カーネルはデータ型の情報を構造体として提供しており、これらの構造体は、kmdb と mdb のいずれかを使用して検査できます。
注 - kmdb と mdb の dcmd は、mdb 用として設計された圧縮シンボリックデバッギング情報を含むオブジェクトでしか使用できません。現在、この情報を利用できるのは、特定の Oracle Solaris カーネルモジュールのみです。このシンボリックデバッギング情報を処理するには、SUNWzlib パッケージをインストールする必要があります。
次の例は、scsi_pkt 構造体のデータを表示する方法を示しています。
例 23-13 デバッガによるカーネルデータ構造体の表示
> 7079ceb0::print -t 'struct scsi_pkt' { opaque_t pkt_ha_private = 0x7079ce20 struct scsi_address pkt_address = { struct scsi_hba_tran *a_hba_tran = 0x70175e68 ushort_t a_target = 0x6 uchar_t a_lun = 0 uchar_t a_sublun = 0 } opaque_t pkt_private = 0x708db4d0 int (*)() *pkt_comp = sd_intr uint_t pkt_flags = 0 int pkt_time = 0x78 uchar_t *pkt_scbp = 0x7079ce74 uchar_t *pkt_cdbp = 0x7079ce64 ssize_t pkt_resid = 0 uint_t pkt_state = 0x37 uint_t pkt_statistics = 0 uchar_t pkt_reason = 0 }
データ構造体のサイズは、デバッグ時に役立つ可能性があります。ある構造体のサイズを取得するには、次の例に示すように ::sizeof dcmd を使用します。
例 23-14 カーネルデータ構造体のサイズの表示
> ::sizeof struct scsi_pkt sizeof (struct scsi_pkt) = 0x58
デバッグ時には、構造体内の特定のメンバーのアドレスも役立ちます。あるメンバーのアドレスを調査する場合に使用可能な方法は、いくつか存在します。
構造体の特定のメンバーのオフセットを取得するには、次の例のように ::offsetof dcmd を使用します。
例 23-15 カーネルデータ構造体へのオフセットの表示
> ::offsetof struct scsi_pkt pkt_state offsetof (struct pkt_state) = 0x48
構造体のすべてのメンバーのアドレスを表示するには、次の例のように ::print dcmd で -a オプションを指定します。
例 23-16 カーネルデータ構造体の相対アドレスの表示
> ::print -a struct scsi_pkt { 0 pkt_ha_private 8 pkt_address { ... } 18 pkt_private ... }
::print でアドレスと -a オプションを組み合わせて指定すると、各メンバーの絶対アドレスが表示されます。
例 23-17 カーネルデータ構造体の絶対アドレスの表示
> 10000000::print -a struct scsi_pkt { 10000000 pkt_ha_private 10000008 pkt_address { ... } 10000018 pkt_private ... }
::print、::sizeof、::offsetof の各 dcmd を使用すると、ドライバが Oracle Solaris カーネルと対話する際に発生した問題をデバッグできます。
注意 - この機能を使用すると、生のカーネルデータ構造体にアクセスできます。ユーザーは、構造体が DDI の一部として表示されるかどうかにかかわらず、任意の構造体を検査できます。したがって、明示的に DDI の一部になっていないデータ構造体には依存しないようにしてください。 |
注 - 前述の dcmd は、mdb 用として設計された圧縮シンボリックデバッギング情報を含むオブジェクトでしか使用しないようにしてください。現在、シンボリックデバッギング情報を使用できるのは、特定の Oracle Solaris カーネルモジュールのみです。シンボリックデバッギング情報を処理するには、SUNWzlib (32 ビット) または SUNWzlibx (64 ビット) 圧縮ソフトウェアをインストールする必要があります。kmdb デバッガは、SUNWzlib または SUNWzlibx パッケージの有無にかかわらず、シンボリックタイプのデータを処理できます。
mdb デバッガには、カーネルデバイスツリーを表示するための ::prtconf dcmd が用意されています。::prtconf dcmd の出力は、prtconf(1M) コマンドの出力に似ています。
例 23-18 ::prtconf dcmd の使用
> ::prtconf 300015d3e08 SUNW,Sun-Blade-100 300015d3c28 packages (driver not attached) 300015d3868 SUNW,builtin-drivers (driver not attached) 300015d3688 deblocker (driver not attached) 300015d34a8 disk-label (driver not attached) 300015d32c8 terminal-emulator (driver not attached) 300015d30e8 obp-tftp (driver not attached) 300015d2f08 dropins (driver not attached) 300015d2d28 kbd-translator (driver not attached) 300015d2b48 ufs-file-system (driver not attached) 300015d3a48 chosen (driver not attached) 300015d2968 openprom (driver not attached)
ノードを表示するには、次の例に示すように ::devinfo dcmd などのマクロを使用します。
例 23-19 特定のノードのデバイス情報の表示
> 300015d3e08::devinfo 300015d3e08 SUNW,Sun-Blade-100 System properties at 0x300015abdc0: name='relative-addressing' type=int items=1 value=00000001 name='MMU_PAGEOFFSET' type=int items=1 value=00001fff name='MMU_PAGESIZE' type=int items=1 value=00002000 name='PAGESIZE' type=int items=1 value=00002000 Driver properties at 0x300015abe00: name='pm-hardware-state' type=string items=1 value='no-suspend-resume'
::prtconf を使用すると、デバイスツリー内でドライバが接続されている場所を確認したり、デバイスのプロパティーを表示したりできます。また、次のように ::prtconf に詳細 (-v) フラグを指定することで、各デバイスノードのプロパティーを表示することもできます。
例 23-20 詳細モードの ::prtconf dcmd の使用
> ::prtconf -v DEVINFO NAME 300015d3e08 SUNW,Sun-Blade-100 System properties at 0x300015abdc0: name='relative-addressing' type=int items=1 value=00000001 name='MMU_PAGEOFFSET' type=int items=1 value=00001fff name='MMU_PAGESIZE' type=int items=1 value=00002000 name='PAGESIZE' type=int items=1 value=00002000 Driver properties at 0x300015abe00: name='pm-hardware-state' type=string items=1 value='no-suspend-resume' ... 300015ce798 pci10b9,5229, instance #0 Driver properties at 0x300015ab980: name='target2-dcd-options' type=any items=4 value=00.00.00.a4 name='target1-dcd-options' type=any items=4 value=00.00.00.a2 name='target0-dcd-options' type=any items=4 value=00.00.00.a4
ドライバのインスタンスを特定する別の方法として、::devbindings dcmd が挙げられます。次の例に示すように、ドライバ名が指定されると、このコマンドは指定されたドライバのすべてのインスタンスの一覧を表示します。
例 23-21 ::devbindings dcmd を使用したドライバインスタンスの特定
> ::devbindings dad 300015ce3d8 ide-disk (driver not attached) 300015c9a60 dad, instance #0 System properties at 0x300015ab400: name='lun' type=int items=1 value=00000000 name='target' type=int items=1 value=00000000 name='class_prop' type=string items=1 value='ata' name='type' type=string items=1 value='ata' name='class' type=string items=1 value='dada' ... 300015c9880 dad, instance #1 System properties at 0x300015ab080: name='lun' type=int items=1 value=00000000 name='target' type=int items=1 value=00000002 name='class_prop' type=string items=1 value='ata' name='type' type=string items=1 value='ata' name='class' type=string items=1 value='dada'
ドライバをデバッグするときの一般的な問題は、ある特定のドライバインスタンスの ソフト状態を取得することです。ソフト状態は、ddi_soft_state_zalloc(9F) ルーチンで割り当てられます。ドライバからソフト状態を取得するには、ddi_get_soft_state(9F) を使用します。ソフト状態ポインタの名前が、ddi_soft_state_init(9F) の第一の引数になります。名前を使用すると、mdb で ::softstate dcmd を使用して特定のドライバインスタンスのソフト状態を取得できます。
> *bst_state::softstate 0x3 702b7578
この場合、::softstate を使用して bst サンプルドライバのインスタンス 3 のソフト状態を取得しています。このポインタは、ドライバがこのインスタンスの状態を追跡するために使用する bst_soft 構造体を参照しています。
カーネル変数などのカーネル状態を変更する場合、kmdb と mdb のどちらを使用してもかまいません。mdb は変更前にカーネルを停止しないため、mdb によるカーネル状態の変更は注意して行うべきです。kmdb を使用すると一連の変更を原子的に行えますが、これは、kmdb がユーザーにアクセスを許可する前にカーネルを停止するためです。mdb デバッガでは、原子的な変更は 1 つしか行えません。
変更の実行時には、必ず適切な書式指定子を使用してください。書式は次のとおりです。
w – 各式の値の下位 2 バイトを、ドットで指定された位置から始まるターゲットに書き込む
W – 各式の値の下位 4 バイトを、ドットで指定された位置から始まるターゲットに書き込む
Z – 各式の値の 8 バイトすべてを、ドットで指定された位置から始まるターゲットに書き込む
変更する変数のサイズを確認するには、::sizeof dcmd を使用します。
次の例では、moddebug の値を値 0x80000000 で上書きしています。
例 23-22 デバッガによるカーネル変数の変更
> moddebug/W 0x80000000 moddebug: 0 = 0x80000000