Walker Definitions
int walk_init(mdb_walk_state_t *wsp);
int walk_step(mdb_walk_state_t *wsp);
void walk_fini(mdb_walk_state_t *wsp);
A walker is composed of three functions, init, step, and fini, which
are defined according to the example prototypes above. A walker is invoked
by the debugger when one of the walk functions (such as mdb_walk()) is
called, or when the user executes the ::walk built-in dcmd. When the walk
begins, MDB calls the walker's init function, passing it the address of a
new mdb_walk_state_t structure, as defined in <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;
A separate mdb_walk_state_t is created for each walk, so that multiple instances of
the same walker can be active simultaneously. The state structure contains the callback
the walker should invoke at each step (walk_callback), and the private data for the
callback (walk_cbdata), as specified to mdb_walk(), for example. The walk_cbdata pointer is opaque
to the walker: it must not modify or dereference this value, nor can
it assume it is a pointer to valid memory.
The starting address for the walk is stored in walk_addr. This is either
NULL if mdb_walk() was called, or the address parameter specified to mdb_pwalk(). If
the ::walk built-in was used, walk_addr will be non-NULL if an explicit address was
specified on the left-hand side of ::walk. A walk with a starting address
of NULL is referred to as global. A walk with an explicit non-NULL
starting address is referred to as local.
The walk_data and walk_arg fields are provided for use as private storage for
the walker. Complex walkers might need to allocate an auxiliary state structure
and set walk_data to point to this structure. Each time a walk
is initiated, walk_arg is initialized to the value of the walk_init_arg member of the
corresponding walker's mdb_walker_t structure.
In some cases, it is useful to have several walkers share the
same init, step, and fini routines. For example, the MDB genunix module provides
walkers for each kernel memory cache. These share the same init, step,
and fini functions, and use the walk_init_arg member of the mdb_walker_t to specify
the address of the appropriate cache as the walk_arg.
If the walker calls mdb_layered_walk() to instantiate an underlying layer, then the underlying
layer will reset walk_addr and walk_layer prior to each call to the
walker's step function. The underlying layer sets walk_addr to the target virtual address
of the underlying object, and set walk_layer to point to the walker's local
copy of the underlying object. For more information on layered walks, refer
to the discussion of mdb_layered_walk() below.
The walker init and step functions are expected to return one of
the following status values:
- WALK_NEXT
Proceed to the next step. When the walk init function returns WALK_NEXT, MDB invokes the walk step function. When the walk step function returns WALK_NEXT, this indicates that MDB should call the step function again.
- WALK_DONE
The walk has completed successfully. WALK_DONE can be returned by either the step function to indicate that the walk is complete, or by the init function to indicate that no steps are needed (for example, if the given data structure is empty).
- WALK_ERR
The walk has terminated due to an error. If WALK_ERR is returned by the init function, mdb_walk() (or any of its counterparts) returns –1 to indicate that the walker failed to initialize. If WALK_ERR is returned by the step function, the walk terminates but mdb_walk() returns success.
The walk_callback is also expected to return one of the values above. Therefore,
the walk step function's job is to determine the address of the next
object, read in a local copy of this object, call the walk_callback function,
then return its status. The step function can also return WALK_DONE or
WALK_ERR without invoking the callback if the walk is complete or if
an error occurred.
The walker itself is defined using the mdb_walker_t structure, defined in :
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;
The walk_name and walk_descr fields should be initialized to point to strings containing
the name and a brief description of the walker, respectively. A walker is
required to have a non-NULL name and description, and the name cannot contain
any of the MDB meta-characters. The description string is printed by the
::walkers and ::dmods built-in dcmds.
The walk_init, walk_step, and walk_fini members refer to the walk functions themselves,
as described earlier. The walk_init and walk_fini members can be set to NULL to
indicate that no special initialization or cleanup actions need to be taken. The
walk_step member cannot be set to NULL. The walk_init_arg member is used to
initialize the walk_arg member of each new mdb_walk_state_t created for the given walker,
as described earlier. Figure 10-1 shows a flowchart for the algorithm of a
typical walker.
Figure 10-1 Sample Walker
The walker is designed to iterate over the list of proc_t structures in
the kernel. The head of the list is stored in the global
practive variable, and each element's p_next pointer points to the next proc_t in
the list. The list is terminated with a NULL pointer. In the walker's
init routine, the practive symbol is located using mdb_lookup_by_name() step (1), and its value
is copied into the mdb_walk_state_t pointed to by wsp.
In the walker's step function, the next proc_t structure in the list is
copied into the debugger's address space using mdb_vread() step (2), the callback
function is invoked with a pointer to this local copy, step (3), and
then the mdb_walk_state_t is updated with the address of the proc_t structure for the
next iteration. This update corresponds to following the pointer, step (4), to the
next element in the list.
These steps demonstrate the structure of a typical walker: the init routine locates
the global information for a particular data structure, the step function reads in
a local copy of the next data item and passes it to the
callback function, and the address of the next element is read. Finally, when
the walk terminates, the fini function frees any private storage.