|
|||
4. Using MDB Commands Interactively 9. Debugging With the Kernel Memory Allocator |
Walker Definitionsint 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:
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 WalkerThe 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. |
||
|