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 由 initstepfini 三个函数组成,它们是根据上面的示例原型定义的。 调用 walk 函数(如 mdb_walk())之一时,或者用户执行 ::walk 内置 dcmd 时,调试器调用 walker。 walk 开始时,MDB 调用 walker 的 init 函数,将新 mdb_walk_state_t 结构的地址传递给该函数,如 <sys/mdb_modapi.h> 中所定义:

typedef struct mdb_walk_state {

			mdb_walk_cb_t walk_callback;    /* Callback to issue */

			void *walk_cbdata;              /* Callback private data */

			uintptr_t walk_addr;            /* Current address */

			void *walk_data;                /* Walk private data */

			void *walk_arg;                 /* Walk private argument */

			void *walk_layer;               /* Data from underlying layer */

} mdb_walk_state_t;

将会为每个 walk 创建单独的 mdb_walk_state_t,以便同一 walker 的多个实例可以同时处于活动状态。例如,如为 mdb_walk() 指定的那样,状态结构包含 walker 在每个步骤应该调用的回调 (walk_callback) 以及回调的专用数据 (walk_cbdata)。 walk_cbdata 指针对 walker 是不透明的:它既不能修改或取消引用此值,也不能假定它是指向有效内存的指针。

walk 的起始地址存储在 walk_addr 中。该地址的值为 NULL(如果调用了 mdb_walk()),或者是为 mdb_pwalk() 指定的地址参数。如果使用内置的 ::walk 命令,则在 ::walk 的左侧指定了显式地址的情况下,walk_addr 将不为 NULL。起始地址为 NULL 的 walk 称为全局 walk。具有显式非 NULL 起始地址的 walk 称为局部 walk。

示例中的 walk_datawalk_arg 字段用于 walker 的专用存储。 复杂的 walker 可能需要分配辅助状态结构,并将 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 可以由 step 函数返回,指示 walk 已完成;也可以由 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_DONEWALK_ERR,而不调用回调。

walker 本身是使用在以下位置中定义的 mdb_walker_t 结构定义的:

typedef struct mdb_walker {

        const char *walk_name;                 /* Walk type name */

        const char *walk_descr;                /* Walk description */

        int (*walk_init)(mdb_walk_state_t *);  /* Walk constructor */

        int (*walk_step)(mdb_walk_state_t *);  /* Walk iterator */

        void (*walk_fini)(mdb_walk_state_t *); /* Walk destructor */

        void *walk_init_arg;                   /* Constructor argument */

} mdb_walker_t;

walk_namewalk_descr 字段应分别初始化为指向包含 walker 的名称和简短说明的字符串。要求 walker 具有非 NULL 名称和说明,而且该名称不能包含任何 MDB 元字符。 说明字符串由 ::walkers::dmods 内置 dcmd 列显。

walk_initwalk_stepwalk_fini 成员引用 walk 函数本身,如前所述。可以将 walk_initwalk_fini 成员设置为 NULL,以指示不需要执行特殊的初始化或清除操作。不能将 walk_step 成员设置为 NULL。如前所述,walk_init_arg 成员用于初始化为给定 walker 创建的每个新 mdb_walk_state_twalk_arg 成员。图 10–1 显示典型 walker 的算法的流程图。

图 10–1 Walker 样例

图形由上下文说明。

walker 设计用于迭代内核中 proc_t 结构的列表。列表头存储在全局 practive 变量中,每个元素的 p_next 指针都指向列表中的下一个 proc_t。该列表以 NULL 指针结尾。在 walker 的 init 例程中,使用 mdb_lookup_by_name() step (1) 查找 practive 符号,然后将其值复制到 wsp 指向的 mdb_walk_state_t 中。

在 walker 的 step 函数中,使用 mdb_vread() step (2) 将列表中的下一个 proc_t 结构复制到调试器的地址空间中,使用指向此局部副本的指针 step (3) 调用回调函数,然后在下一次迭代中使用 proc_t 结构的地址更新 mdb_walk_state_t。此更新对应于指向列表中下一个元素的指针 step (4)。

这些 step 说明了典型 walker 的结构:init 例程查找特定数据结构的全局信息,step 函数将其读入下一个数据项的局部副本,然后将其传递到回调函数,读取下一个元素的地址。最后,在 walk 终止时,fini 函数释放任何专用存储。