Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
  Previous   Contents   Next 
   
 
Chapter 9

Module Programming API

This chapter describes the structures and functions contained in the MDB debugger module API. The header file <sys/mdb_modapi.h> contains prototypes for these functions, and the SUNWmdbdm package provides source code for an example module in the directory /usr/demo/mdb.

Debugger Module Linkage

_mdb_init()

const mdb_modinfo_t *_mdb_init(void);

Each debugger module is required to provide, for linkage and identification purposes, a function named _mdb_init(). This function returns a pointer to a persistent (that is, not declared as an automatic variable) mdb_modinfo_t structure, as defined in <sys/mdb_modapi.h>:

typedef struct mdb_modinfo {
        ushort_t mi_dvers;               /* Debugger API version number */
        const mdb_dcmd_t *mi_dcmds;      /* NULL-terminated list of dcmds */
        const mdb_walker_t *mi_walkers;  /* NULL-terminated list of walks */
} mdb_modinfo_t;

The mi_dvers member is used to identify the API version number, and should always be set to MDB_API_VERSION. The current version number is therefore compiled into each debugger module, allowing the debugger to identify and verify the application binary interface used by the module. The debugger does not load modules that are compiled for an API version that is more recent than the debugger itself.

The mi_dcmds and mi_walkers members, if not NULL, point to arrays of dcmd and walker definition structures, respectively. Each array must be terminated by a NULL element. These dcmds and walkers are installed and registered with the debugger as part of the module loading process. The debugger will refuse to load the module if one or more dcmds or walkers are defined improperly or if they have conflicting or invalid names. Dcmd and walker names are prohibited from containing characters that have special meaning to the debugger, such as quotation marks and parentheses.

The module can also execute code in _mdb_init() using the module API to determine if it is appropriate to load. For example, a module can only be appropriate for a particular target if certain symbols are present. If these symbols are not found, the module can return NULL from the _mdb_init() function. In this case, the debugger will refuse to load the module and an appropriate error message is printed.

_mdb_fini()

void _mdb_fini(void);

If the module performs certain tasks prior to unloading, such as freeing persistent memory previously allocated with mdb_alloc(), it can declare a function named _mdb_fini() for this purpose. This function is not required by the debugger. If declared, it is called once prior to unloading the module. Modules are unloaded when the user requests that the debugger terminate or when the user explicitly unloads a module using the ::unload built-in dcmd.

Dcmd Definitions

int dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv);

A dcmd is implemented with a function similar to the dcmd() declaration. This function receives four arguments and returns an integer status. The function arguments are:

addr

Current address, also called dot. At the start of the dcmd, this address corresponds to the value of the dot " ." variable in the debugger.

flags

Integer containing the logical OR of one or more of the following flags:

DCMD_ADDRSPEC

An explicit address was specified to the left of ::dcmd.

DCMD_LOOP

The dcmd was invoked in a loop using the ,count syntax, or the dcmd was invoked in a loop by a pipeline.

DCMD_LOOPFIRST

This invocation of the dcmd function corresponds to the first loop or pipeline invocation.

DCMD_PIPE

The dcmd was invoked with input from a pipeline.

DCMD_PIPE_OUT

The dcmd was invoked with output set to a pipeline.

As a convenience, the DCMD_HDRSPEC() macro is provided to allow a dcmd to test its flags to determine if it should print a header line (that is, it was not invoked as part of a loop, or it was invoked as the first iteration of a loop or pipeline).

argc

Number of arguments in the argv array.

argv

Array of arguments specified to the right of ::dcmd on the command line. These arguments can be either strings or integer values.

The dcmd function is expected to return one of the following integer values, defined in <sys/mdb_modapi.h>.

DCMD_OK

The dcmd completed successfully.

DCMD_ERR

The dcmd failed for some reason.

DCMD_USAGE

The dcmd failed because invalid arguments were specified. When this value is returned, the dcmd usage message (described below) prints automatically.

DCMD_NEXT

The next dcmd definition (if one is present) is automatically invoked with the same arguments.

DCMD_ABORT

The dcmd failed, and the current loop or pipeline should be aborted. This is like DCMD_ERR, but indicates that no further progress is possible in the current loop or pipe.

Each dcmd consists of a function defined according to the example dcmd() prototype, and a corresponding mdb_dcmd_t structure, as defined in <sys/mdb_modapi.h>. This structure consists of the following fields:

const char *dc_name

The string name of the dcmd, without the leading "::". The name cannot contain any of the MDB meta-characters, such as $ or `.

const char *dc_usage

An optional usage string for the dcmd, to be printed when the dcmd returns DCMD_USAGE. For example, if the dcmd accepts options -a and -b, dc_usage might be specified as "[-ab]". If the dcmd accepts no arguments, dc_usage can be set to NULL. If the usage string begins with ":", this is shorthand for indicating that the dcmd requires an explicit address (that is, it requires DCMD_ADDRSPEC to be set in its flags parameter). If the usage string begins with "?", this indicates that the dcmd optionally accepts an address. These hints modify the usage message accordingly.

const char *dc_descr

A mandatory description string, briefly explaining the purpose of the dcmd. This string should consist of only a single line of text.

mdb_dcmd_f *dc_funcp

A pointer to the function that will be called to execute the dcmd.

void (*dc_help)(void)

An optional function pointer to a help function for the dcmd. If this pointer is not NULL, this function will be called when the user executes ::help dcmd. This function can use mdb_printf() to display further information or examples.

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 9-1 shows a flowchart for the algorithm of a typical walker.

Figure 9-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.

 
 
 
  Previous   Contents   Next