Solaris モジューラデバッガ

walker の定義

int walk_init(mdb_walk_state_t *wsp);
int walk_step(mdb_walk_state_t *wsp);
void walk_fini(mdb_walk_state_t *wsp);

walker は initstep、および fini の 3 つの関数で構成されており、これらの関数は上記のプロトタイプの例に従って定義されています。walker は、mdb_walk() などのいずれかの walk 関数が呼び出されたとき、またはユーザーが ::walk 組み込み dcmd を実行したときに、デバッガによって起動されます。<sys/modapi.h> に定義されているように、walk が開始されると、MDB は walker の init 関数を呼び出し、新規 mdb_walk_state_t 構造体のアドレスをこの関数に渡します。

typedef struct mdb_walk_state {
			mdb_walk_cb_t walk_callback;    /* 実行のためのコールバック */
			void *walk_cbdata;              /* 専用データのコールバック */
			uintptr_t walk_addr;            /* 現在のアドレス */
			void *walk_data;                /* walk 専用データ */
			void *walk_arg;                 /* walk 専用引数 */
			void *walk_layer;               /* 配下の層からのデータ */
} mdb_walk_state_t;

walk ごとに個別に mdb_walk_state_t が作成されるため、同じ walker の複数のインスタンスを同時にアクティブにすることができます。たとえば mdb_walk() に指定されているように、state 構造体には、各ステップにおいて walker が呼び出すコールバック (walk_callback)、およびそのコールバックに対する専用データ (walk_cbdata) が含まれています。walk_cbdata ポインタは walker からは隠されているため、この値を変更したり、参照を解除したりすることはもちろん、有効なメモリーを指すポインタとみなすこともできません。

walk の開始アドレスは walk_addr に格納されています。このアドレスは mdb_walk() が呼び出された場合の NULL か、または mdb_pwalk() に指定されているアドレスパラメタのどちらかの値となります。::walk 組み込みコマンドが使用された場合、明示的なアドレスが ::walk の左側に指定されているときは、walk_addr は NULL 以外の値となります。開始アドレスが NULL の walk のことをグローバル walk といいます。NULL 以外の明示的な開始アドレスを持つ walk のことをローカル walk といいます。

walker 専用の記憶領域として walk_data および walk_arg フィールドが用意されています。複雑な walker の場合、補助的な state 構造体を割り当てて、この構造体を指すように walk_data を設定する必要があります。walk が開始されるたびに、walk_arg は、対応する walker の mdb_walker_t 構造体の walk_init_arg メンバーが持つ値に初期設定されます。

場合によっては、複数の walker に同じ init、step、および fini ルーチンを共有させると便利な場合があります。たとえば、MDB genunix モジュールは、各カーネルのメモリーキャッシュに対する walker を提供しています。これらの walker は同じ init、step、および fini 関数を共有しているため、mdb_walker_twalk_init_arg メンバーを使用して、適切なキャッシュのアドレスを walk_arg として指定できます。

walker が mdb_layered_walk() を呼び出して配下の層をインスタンス化した場合、配下の層は walker の step 関数を呼び出す前に walk_addrwalk_layer をリセットします。配下の層は walk_addr を配下のオブジェクトのターゲットの仮想アドレスに設定し、walk_layer を配下のオブジェクトの walker のローカルコピーに設定します。階層化された walk については、以降の mdb_layered_walk() の説明を参照してください。

walker の init および step 関数は、次の状態値のどれかを返します。

WALK_NEXT

次のステップへ進む。walk init 関数が WALK_NEXT を返すと、MDB は walk step 関数を呼び出す。walk step 関数から WALK_NEXT が返されたときは、MDB がもう一度 step 関数を呼び出す必要があることを示す

WALK_DONE

walk が正常に完了した。WALK_DONE は、walk が完了したことを示すために step 関数から返される場合と、与えられたデータ構造体が空である場合などに step が不要であることを示すために init 関数から返される場合がある

WALK_ERR

walk がエラーのため終了した。WALK_ERR が init 関数から返された場合、mdb_walk() (またはそれに相当するもの) は -1 を返して、walker が初期化に失敗したことを示す。WALK_ERR が step 関数から返された場合、walk は終了するが、mdb_walk() 関数からは成功が返される

walk_callback からも、上記のどれかの値が返されます。したがって walk step 関数は次のオブジェクトのアドレスを決定し、このオブジェクトのローカルコピーを読み取って、walk_callback 関数を呼び出し、その状態を返します。walk が完了したか、またはエラーが発生した場合、step 関数もコールバックを呼び出さずに WALK_DONE または WALK_ERR を返すことがあります。

次に示すように、walker 自体は mdb_walker_t 構造体を使用して定義されます。

typedef struct mdb_walker {
			const char *walk_name;                 /* walk のタイプ名 */
			const char *walk_descr;                /* walk の記述 */
			int (*walk_init)(mdb_walk_state_t *);  /* walk コンストラクタ */
			int (*walk_step)(mdb_walk_state_t *);  /* walk イタレータ */
			void (*walk_fini)(mdb_walk_state_t *); /* walk デストラクタ */
			void *walk_init_arg;                   /* コンストラクタの引数 */
} mdb_walker_t;

walk_name および walk_descr フィールドは、それぞれ walker の名前と短い説明を含む文字列を指すように初期化されます。walker は NULL 以外の名前と説明を持つ必要があり、名前には MDB メタキャラクタを入れることはできません。説明の文字列は ::walkers および ::dmods 組み込み dcmd によって出力されます。

walk_initwalk_step、および walk_fini メンバーは、前述のように walk 関数自体を指しています。特別な初期化またはクリーンアップ措置が必要でないことを示すには、walk_init および walk_fini メンバーを NULL に設定します。

walk_step メンバーは NULL には設定できません。前述のように、walk_init_arg メンバーは、指定された walker に対して新規に作成された mdb_walk_state_t ごとに walk_arg メンバーを初期化するのに使用されます。一般的な walker の各ステップについては、図 7-1 を参照してください。

図 7-1 walker の例

Graphic

walker はカーネル内の proc_t 構造体のリストに対して繰り返すように設計されています。リストの先頭は practive 大域変数に格納されており、各要素の p_next ポインタはリスト内の次の proc_t を指しています。リストの末尾は NULL ポインタになっています。ステップ (1) で walker の init ルーチン、practive のシンボルは mdb_lookup_by_name() を使用して検出され、この値が wsp の指す mdb_walk_state_t にコピーされます。

walker の step 関数では、ステップ (2) としてリスト内の次の proc_t 構造体が mdb_vread() を使用してデバッガのアドレス空間にコピーされます。ステップ (3) でこのローカルコピーを指すポインタによってコールバック関数が起動され、mdb_walk_state_t が次の繰り返しに対する proc_t 構造体のアドレスで更新されます。ステップ (4) では、この更新は、リスト内の次の要素を指す次のポインタに対応しています。

これらのステップは一般的な walker の構造を示しています。init ルーチンが特定のデータ構造体に関するグローバルな情報を検出し、step 関数が次のデータ項目のコピーを読み取ってコールバック関数に渡し、次の要素のアドレスが読み取られます。最終的に walk が終了すると、fini 関数によってすべての専用記憶領域が解放されます。