Oracle Solaris Modular Debugger Guide

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

Figure 10–1 Sample Walker

Graphic is described by context.

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.

API Functions

mdb_pwalk()

int mdb_pwalk(const char *name, mdb_walk_cb_t func, void *data,
              uintptr_t addr);

Initiate a local walk starting at addr using the walker specified by name, and invoke the callback function func at each step. If addr is NULL, a global walk is performed (that is, the mdb_pwalk() invocation is equivalent to the identical call to mdb_walk() without the trailing addr parameter). This function returns 0 for success, or -1 for error. The mdb_pwalk() function fails if the walker itself returns a fatal error, or if the specified walker name is not known to the debugger. The walker name may be scoped using the backquote (`) operator if there are naming conflicts. The data parameter is an opaque argument that has meaning only to the caller; it is passed back to func at each step of the walk.

mdb_walk()

int mdb_walk(const char *name, mdb_walk_cb_t func, void *data);

Initiate a global walk using the walker specified by name, and invoke the callback function func at each step. This function returns 0 for success, or -1 for error. The mdb_walk() function fails if the walker itself returns a fatal error, or if the specified walker name is not known to the debugger. The walker name can be scoped using the backquote (`) operator if there are naming conflicts. The data parameter is an opaque argument that has meaning only to the caller; it is passed back to func at each step of the walk.

mdb_pwalk_dcmd()

int mdb_pwalk_dcmd(const char *wname, const char *dcname, int argc,
        const mdb_arg_t *argv, uintptr_t addr);

Initiate a local walk starting at addr using the walker specified by wname, and invoke the dcmd specified by dcname with the specified argc and argv at each step. This function returns 0 for success, or -1 for error. The function fails if the walker itself returns a fatal error, if the specified walker name or dcmd name is not known to the debugger, or if the dcmd itself returns DCMD_ABORT or DCMD_USAGE to the walker. The walker name and dcmd name can each be scoped using the backquote (`) operator if there are naming conflicts. When invoked from mdb_pwalk_dcmd(), the dcmd will have the DCMD_LOOP and DCMD_ADDRSPEC bits set in its flags parameter, and the first call will have DCMD_LOOPFIRST set.

mdb_walk_dcmd()

int mdb_walk_dcmd(const char *wname, const char *dcname, int argc,
        const mdb_arg_t *argv);

Initiate a global walk using the walker specified by wname, and invoke the dcmd specified by dcname with the specified argc and argv at each step. This function returns 0 for success, or -1 for error. The function fails if the walker itself returns a fatal error, if the specified walker name or dcmd name is not known to the debugger, or if the dcmd itself returns DCMD_ABORT or DCMD_USAGE to the walker. The walker name and dcmd name can each be scoped using the backquote (`) operator if there are naming conflicts. When invoked from mdb_walk_dcmd(), the dcmd will have the DCMD_LOOP and DCMD_ADDRSPEC bits set in its flags parameter, and the first call will have DCMD_LOOPFIRST set.

mdb_call_dcmd()

int mdb_call_dcmd(const char *name, uintptr_t addr, uint_t flags, 
        int argc, const mdb_arg_t *argv);

Invoke the specified dcmd name with the given parameters. The dot variable is reset to addr, and addr, flags, argc, and argv are passed to the dcmd. The function returns 0 for success, or -1 for error. The function fails if the dcmd returns DCMD_ERR, DCMD_ABORT, or DCMD_USAGE, or if the specified dcmd name is not known to the debugger. The dcmd name can be scoped using the backquote (`) operator if there are naming conflicts.

mdb_layered_walk()

int mdb_layered_walk(const char *name, mdb_walk_state_t *wsp);

Layer the walk denoted by wsp on top of a walk initiated using the specified walker name. The name can be scoped using the backquote (`) operator if there are naming conflicts. Layered walks can be used, for example, to facilitate constructing walkers for data structures that are embedded in other data structures.

For example, suppose that each CPU structure in the kernel contains a pointer to an embedded structure. To write a walker for the embedded structure type, you could replicate the code to iterate over CPU structures and dereference the appropriate member of each CPU structure, or you could layer the embedded structure's walker on top of the existing CPU walker.

The mdb_layered_walk() function is used from within a walker's init routine to add a new layer to the current walk. The underlying layer is initialized as part of the call to mdb_layered_walk(). The calling walk routine passes in a pointer to its current walk state; this state is used to construct the layered walk. Each layered walk is cleaned up after the caller's walk fini function is called. If more than one layer is added to a walk, the caller's walk step function will step through each element returned by the first layer, then the second layer, and so forth.

The mdb_layered_walk() function returns 0 for success, or -1 for error. The function fails if the specified walker name is not known to the debugger, if the wsp pointer is not a valid, active walk state pointer, if the layered walker itself fails to initialize, or if the caller attempts to layer the walker on top of itself.

mdb_add_walker()

int mdb_add_walker(const mdb_walker_t *w);

Register a new walker with the debugger. The walker is added to the module's namespace, and to the debugger's global namespace according to the name resolution rules described in Dcmd and Walker Name Resolution. This function returns 0 for success, or -1 for error if the given walker name is already registered by this module, or if the walker structure w is improperly constructed. The information in the mdb_walker_t w is copied to internal debugger structures, so the caller can reuse or free this structure after the call to mdb_add_walker().

mdb_remove_walker()

int mdb_remove_walker(const char *name);

Remove the walker with the specified name. This function returns 0 for success, or -1 for error. The walker is removed from the current module's namespace. The function fails if the walker name is unknown, or is registered only in another module's namespace. The mdb_remove_walker() function can be used to remove walkers that were added dynamically using mdb_add_walker(), or walkers that were added statically as part of the module's linkage structure. The scoping operator cannot be used in the walker name; it is not legal for the caller of mdb_remove_walker() to attempt to remove a walker exported by a different module.

mdb_vread() and mdb_vwrite()

ssize_t mdb_vread(void *buf, size_t nbytes, uintptr_t addr);
ssize_t mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr);

These functions provide the ability to read and write data from a given target virtual address, specified by the addr parameter. The mdb_vread() function returns nbytes for success, or -1 for error; if a read is truncated because only a portion of the data can be read from the specified address, -1 is returned. The mdb_vwrite() function returns the number of bytes actually written upon success; -1 is returned upon error.

mdb_fread() and mdb_fwrite()

ssize_t mdb_fread(void *buf, size_t nbytes, uintptr_t addr);
ssize_t mdb_fwrite(const void *buf, size_t nbytes, uintptr_t addr);

These functions provide the ability to read and write data from the object file location corresponding to the given target virtual address, specified by the addr parameter. The mdb_fread() function returns nbytes for success, or -1 for error; if a read is truncated because only a portion of the data can be read from the specified address, -1 is returned. The mdb_fwrite() function returns the number of bytes actually written upon success; -1 is returned upon error.

mdb_pread() and mdb_pwrite()

ssize_t mdb_pread(void *buf, size_t nbytes, uint64_t addr);
ssize_t mdb_pwrite(const void *buf, size_t nbytes, uint64_t addr);

These functions provide the ability to read and write data from a given target physical address, specified by the addr parameter. The mdb_pread() function returns nbytes for success, or -1 for error; if a read is truncated because only a portion of the data can be read from the specified address, -1 is returned. The mdb_pwrite() function returns the number of bytes actually written upon success; -1 is returned upon error.

mdb_readstr()

ssize_t mdb_readstr(char *s, size_t nbytes, uintptr_t addr);

The mdb_readstr() function reads a null-terminated C string beginning at the target virtual address addr into the buffer addressed by s. The size of the buffer is specified by nbytes. If the string is longer than can fit in the buffer, the string is truncated to the buffer size and a null byte is stored at s[nbytes - 1]. The length of the string stored in s (not including the terminating null byte) is returned upon success; otherwise -1 is returned to indicate an error.

mdb_writestr()

ssize_t mdb_writestr(const char *s, uintptr_t addr);

The mdb_writestr() function writes a null-terminated C string from s (including the trailing null byte) to the target's virtual address space at the address specified by addr. The number of bytes written (not including the terminating null byte) is returned upon success; otherwise, -1 is returned to indicate an error.

mdb_readsym()

ssize_t mdb_readsym(void *buf, size_t nbytes, const char *name);

mdb_readsym() is similar to mdb_vread(), except that the virtual address at which reading begins is obtained from the value of the symbol specified by name. If no symbol by that name is found or a read error occurs, -1 is returned; otherwise nbytes is returned for success.

The caller can first look up the symbol separately if it is necessary to distinguish between symbol lookup failure and read failure. The primary executable's symbol table is used for the symbol lookup; if the symbol resides in another symbol table, you must first apply mdb_lookup_by_obj(), then mdb_vread().

mdb_writesym()

ssize_t mdb_writesym(const void *buf, size_t nbytes, const char *name);

mdb_writesym() is identical to mdb_vwrite(), except that the virtual address at which writing begins is obtained from the value of the symbol specified by name. If no symbol by that name is found, -1 is returned. Otherwise, the number of bytes successfully written is returned on success, and -1 is returned on error. The primary executable's symbol table is used for the symbol lookup; if the symbol resides in another symbol table, you must first apply mdb_lookup_by_obj(), then mdb_vwrite().

mdb_readvar() and mdb_writevar()

ssize_t mdb_readvar(void *buf, const char *name);
ssize_t mdb_writevar(const void *buf, const char *name);

mdb_readvar() is similar to mdb_vread(), except that the virtual address at which reading begins and the number of bytes to read are obtained from the value and size of the symbol specified by name. If no symbol by that name is found, -1 is returned. The symbol size (the number of bytes read) is returned on success; -1 is returned on error. This is useful for reading well-known variables whose sizes are fixed. For example:

        int hz; /* system clock rate */
        mdb_readvar(&hz, "hz");

The caller can first look up the symbol separately if it is necessary to distinguish between symbol lookup failure and read failure. The caller must also carefully check the definition of the symbol of interest in order to make sure that the local declaration is the exact same type as the target's definition. For example, if the caller declares an int, and the symbol of interest is actually a long, and the debugger is examining a 64-bit kernel target, mdb_readvar() copies back 8 bytes to the caller's buffer, corrupting the 4 bytes following the storage for the int.

mdb_writevar() is identical to mdb_vwrite(), except that the virtual address at which writing begins and the number of bytes to write are obtained from the value and size of the symbol specified by name. If no symbol by that name is found, -1 is returned. Otherwise, the number of bytes successfully written is returned on success, and -1 is returned on error.

For both functions, the primary executable's symbol table is used for the symbol lookup; if the symbol resides in another symbol table, you must first apply mdb_lookup_by_obj(), then mdb_vread() or mdb_vwrite().

mdb_lookup_by_name() and mdb_lookup_by_obj()

int mdb_lookup_by_name(const char *name, GElf_Sym *sym);
int mdb_lookup_by_obj(const char *object, const char *name, GElf_Sym *sym);

Look up the specified symbol name and copy the ELF symbol information into the GElf_Sym pointed to by sym. If the symbol is found, the function returns 0; otherwise, -1 is returned. The name parameter specifies the symbol name. The object parameter tells the debugger where to look for the symbol. For the mdb_lookup_by_name() function, the object file defaults to MDB_OBJ_EXEC. For mdb_lookup_by_obj(), the object name should be one of the following:

MDB_OBJ_EXEC

Look in the executable's symbol table (.symtab section). For kernel crash dumps, this corresponds to the symbol table from the unix.X file or from /dev/ksyms.

MDB_OBJ_RTLD

Look in the runtime link-editor's symbol table. For kernel crash dumps, this corresponds to the symbol table for the krtld module.

MDB_OBJ_EVERY

Look in all known symbol tables. For kernel crash dumps, this includes the .symtab and .dynsym sections from the unix.X file or /dev/ksyms, as well as per-module symbol tables if these have been processed.

object

If the name of a particular load object is explicitly specified, the search is restricted to the symbol table of this object. The object can be named according to the naming convention for load objects described in Symbol Name Resolution.

mdb_lookup_by_addr()

int mdb_lookup_by_addr(uintptr_t addr, uint_t flag, char *buf,
        size_t len, GElf_Sym *sym);

Locate the symbol corresponding to the specified address and copy the ELF symbol information into the GElf_Sym pointed to by sym and the symbol name into the character array addressed by buf. If a corresponding symbol is found, the function returns 0; otherwise -1 is returned.

The flag parameter specifies the lookup mode and should be one of the following:

MDB_SYM_FUZZY

Allow fuzzy matching to take place, based on the current symbol distance setting. The symbol distance can be controlled using the ::set -s built-in. If an explicit symbol distance has been set (absolute mode), the address can match a symbol if the distance from the symbol's value to the address does not exceed the absolute symbol distance. If smart mode is enabled (symbol distance = 0), then the address can match the symbol if it is in the range [symbol value, symbol value + symbol size).

MDB_SYM_EXACT

Disallow fuzzy matching. The symbol can match only the address if the symbol value exactly equals the specified address.

If a symbol match occurs, the name of the symbol is copied into the buf supplied by the caller. The len parameter specifies the length of this buffer in bytes. The caller's buf should be at least of size MDB_SYM_NAMLEN bytes. The debugger copies the name to this buffer and appends a trailing null byte. If the name length exceeds the length of the buffer, the name is truncated but always includes a trailing null byte.

mdb_getopts()

int mdb_getopts(int argc, const mdb_arg_t *argv, ...);

Parse and process options and option arguments from the specified argument array (argv). The argc parameter denotes the length of the argument array. This function processes each argument in order, and stops and returns the array index of the first argument that could not be processed. If all arguments are processed successfully, argc is returned.

Following the argc and argv parameters, the mdb_getopts() function accepts a variable list of arguments describing the options that are expected to appear in the argv array. Each option is described by an option letter (char argument), an option type (uint_t argument), and one or two additional arguments, as shown in the table below. The list of option arguments is terminated with a NULL argument. The type should be one of one of the following:

MDB_OPT_SETBITS

The option will OR the specified bits into a flag word. The option is described by these parameters:

char c, uint_t type, uint_t bits, uint_t *p

If type is MDB_OPT_SETBITS and option c is detected in the argv list, the debugger will OR bits into the integer referenced by pointer p.

MDB_OPT_CLRBITS

The option clears the specified bits from a flag word. The option is described by these parameters:

char c, uint_t type, uint_t bits, uint_t *p

If type is MDB_OPT_CLRBITS and option c is detected in the argv list, the debugger clears bits from the integer referenced by pointer p.

MDB_OPT_STR

The option accepts a string argument. The option is described by these parameters:

char c, uint_t type, const char **p

If type is MDB_OPT_STR and option c is detected in the argv list, the debugger stores a pointer to the string argument following c in the pointer referenced by p.

MDB_OPT_UINTPTR

The option accepts a uintptr_t argument. The option is described by these parameters:

char c, uint_t type, uintptr_t *p

If type is MDB_OPT_UINTPTR and option c is detected in the argv list, the debugger stores the integer argument following c in the uintptr_t referenced by p.

MDB_OPT_UINTPTR_SET

The option accepts a uintptr_t argument. The option is described by these parameters:

char c, uint_t type, boolean_t *flag, uintptr_t *p

If type is MDB_OPT_UINTPTR_SET and option c is detected in the argv list, the debugger stores the value '1' (TRUE) into the boolean_t referenced by flag, and the integer argument following c in the uintptr_t referenced by p.

MDB_OPT_UINT64

The option accepts a uint64_t argument. The option is described by these parameters:

char c, uint_t type, uint64_t *p

If type is MDB_OPT_UINT64 and option c is detected in the argv list, the debugger stores the integer argument following c in the uint64_t referenced by p.

For example, the following source code:

int
dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
        uint_t opt_v = FALSE;
        const char *opt_s = NULL;

        if (mdb_getopts(argc, argv,
            'v', MDB_OPT_SETBITS, TRUE, &opt_v,
            's', MDB_OPT_STR, &opt_s, NULL) != argc)
                return (DCMD_USAGE);

        /* ... */
}

demonstrates how mdb_getopts() might be used in a dcmd to accept a boolean option “-v” that sets the opt_v variable to TRUE, and an option “-s” that accepts a string argument that is stored in the opt_s variable. The mdb_getopts() function also automatically issues warning messages if it detects an invalid option letter or missing option argument before returning to the caller. The storage for argument strings and the argv array is automatically garbage-collected by the debugger upon completion of the dcmd.

mdb_strtoull()

u_longlong_t mdb_strtoull(const char *s);

Convert the specified string s to an unsigned long long representation. This function is intended for use in processing and converting string arguments in situations where mdb_getopts() is not appropriate. If the string argument cannot be converted to a valid integer representation, the function fails by printing an appropriate error message and aborting the dcmd. Therefore, error checking code is not required. The string can be prefixed with any of the valid base specifiers (0i, 0I, 0o, 0O, 0t, 0T, 0x, or 0X); otherwise, it is interpreted using the default base. The function will fail and abort the dcmd if any of the characters in s are not appropriate for the base, or if integer overflow occurs.

mdb_alloc(), mdb_zalloc() and mdb_free()

void *mdb_alloc(size_t size, uint_t flags);
void *mdb_zalloc(size_t size, uint_t flags);
void mdb_free(void *buf, size_t size);

mdb_alloc() allocates size bytes of debugger memory and returns a pointer to the allocated memory. The allocated memory is at least double-word aligned, so it can hold any C data structure. No greater alignment can be assumed. The flags parameter should be the bitwise OR of one or more of the following values:

UM_NOSLEEP

If sufficient memory to fulfill the request is not immediately available, return NULL to indicate failure. The caller must check for NULL and handle this case appropriately.

UM_SLEEP

If sufficient memory to fulfill the request is not immediately available, sleep until such time as the request can be fulfilled. As a result, UM_SLEEP allocations are guaranteed to succeed. The caller need not check for a NULL return value.

UM_GC

Garbage-collect allocation automatically at the end of this debugger command. The caller should not subsequently call mdb_free() on this block, as the debugger will take care of deallocation automatically. All memory allocation from within a dcmd must use UM_GC so that if the dcmd is interrupted by the user, the debugger can garbage-collect the memory.

mdb_zalloc() is like mdb_alloc(), but the allocated memory is filled with zeroes before returning it to the caller. No guarantees are made about the initial contents of memory returned by mdb_alloc(). mdb_free() is used to free previously allocated memory (unless it was allocated UM_GC). The buffer address and size must exactly match the original allocation. It is not legal to free only a portion of an allocation with mdb_free(). It is not legal to free an allocation more than once. An allocation of zero bytes always returns NULL; freeing a NULL pointer with size zero always succeeds.

mdb_printf()

void mdb_printf(const char *format, ...);

Print formatted output using the specified format string and arguments. Module writers should use mdb_printf() for all output, except for warning and error messages. This function automatically triggers the built-in output pager when appropriate. The mdb_printf() function is similar to printf(3C), with certain exceptions: the %C, %S, and %ws specifiers for wide character strings are not supported, the %f floating-point format is not supported, the %e, %E, %g, and %G specifiers for alternative double formats produce only a single style of output, and precision specifications of the form %.n are not supported. The list of specifiers that are supported follows:

Flag Specifiers

%#

If the # sign is found in the format string, this selects the alternate form of the given format. Not all formats have an alternate form; the alternate form is different depending on the format. Refer to the format descriptions below for details on the alternate format.

%+

When printing signed values, always display the sign (prefix with either '+' or '-'). Without %+, positive values have no sign prefix, and negative values have a '-' prefix prepended to them.

%-

Left-justify the output within the specified field width. If the width of the output is less than the specified field width, the output will be padded with blanks on the right-hand side. Without %-, values are right-justified by default.

%0

Zero-fill the output field if the output is right-justified and the width of the output is less than the specified field width. Without %0, right-justified values are prepended with blanks in order to fill the field.

Field Width Specifiers

%n

Field width is set to the specified decimal value.

%?

Field width is set to the maximum width of a hexadecimal pointer value. This is 8 in an ILP32 environment, and 16 in an LP64 environment.

%*

Field width is set to the value specified at the current position in the argument list. This value is assumed to be an int. Note that in the 64-bit compilation environment, it may be necessary to cast long values to int.

Integer Specifiers

%h

Integer value to be printed is a short.

%l

Integer value to be printed is a long.

%ll

Integer value to be printed is a long long.

Terminal Attribute Specifiers

If standard output for the debugger is a terminal, and terminal attributes can be obtained by the terminfo database, the following terminal escape constructs can be used:

%<n>

Enable the terminal attribute corresponding to n. Only a single attribute can be enabled with each instance of %<>.

%</n>

Disable the terminal attribute corresponding to n. Note that in the case of reverse video, dim text, and bold text, the terminal codes to disable these attributes might be identical. Therefore, it might not be possible to disable these attributes independently of one another.

If no terminal information is available, each terminal attribute construct is ignored by mdb_printf(). For more information on terminal attributes, see terminfo(4). The available terminfo attributes are:

a

Alternate character set

b

Bold text

d

Dim text

r

Reverse video

s

Best standout capability

u

Underlining

Format Specifiers

%%

The '%' symbol is printed.

%a

Prints an address in symbolic form. The minimum size of the value associated with %a is a uintptr_t; specifying %la is not necessary. If address-to-symbol conversion is on, the debugger will attempt to convert the address to a symbol name followed by an offset in the current output radix and print this string; otherwise, the value is printed in the default output radix. If %#a is used, the alternate format adds a ':' suffix to the output.

%A

This format is identical to %a, except when an address cannot be converted to a symbol name plus an offset, nothing is printed. If %#A is used, the alternate format prints a '?' when address conversion fails.

%b

Decode and print a bit field in symbolic form. This specifier expects two consecutive arguments: the bit field value (int for %b, long for %lb, and so forth), and a pointer to an array of mdb_bitmask_t structures:

typedef struct mdb_bitmask {
        const char *bm_name;       /* String name to print */
        u_longlong_t bm_mask;      /* Mask for bits */
        u_longlong_t bm_bits;      /* Result for value & mask */
} mdb_bitmask_t;

The array should be terminated by a structure whose bm_name field is set to NULL. When %b is used, the debugger reads the value argument, then iterates through each mdb_bitmask structure checking to see if:

(value & bitmask->bm_mask) == bitmask->bm_bits

If this expression is true, the bm_name string is printed. Each string printed is separated by a comma. The following example shows how %b can be used to decode the t_flag field in a kthread_t:

const mdb_bitmask_t t_flag_bits[] = {
        { "T_INTR_THREAD", T_INTR_THREAD, T_INTR_THREAD },
        { "T_WAKEABLE", T_WAKEABLE, T_WAKEABLE },
        { "T_TOMASK", T_TOMASK, T_TOMASK },
        { "T_TALLOCSTK", T_TALLOCSTK, T_TALLOCSTK },
        /* ... */
        { NULL, 0, 0 }
};

void
thr_dump(kthread_t *t)
{
        mdb_printf("t_flag = <%hb>\n", t->t_flag, t_flag_bits);
        /* ... */
}

If t_flag was set to 0x000a, the function would print:

t_flag = <T_WAKEABLE,T_TALLOCSTK>

If %#b is specified, the union of all bits that were not matched by an element in the bitmask array is printed as a hexadecimal value following the decoded names.

%c

Print the specified integer as an ASCII character.

%d

Print the specified integer as a signed decimal value. Same as %i. If %#d is specified, the alternate format prefixes the value with '0t'.

%e

Print the specified double in the floating-point format [+/-]d.ddddddde[+/-]dd, where there is one digit before the radix character, seven digits of precision, and at least two digits following the exponent.

%E

Print the specified double using the same rules as %e, except that the exponent character will be 'E' instead of 'e'.

%g

Print the specified double in the same floating-point format as %e, but with sixteen digits of precision. If %llg is specified, the argument is expected to be of type long double (quad-precision floating-point value).

%G

Print the specified double using the same rules as %g, except that the exponent character will be 'E' instead of 'e'.

%i

Print the specified integer as a signed decimal value. Same as %d. If %#i is specified, the alternate format prefixes the value with '0t'.

%I

Print the specified 32-bit unsigned integer as an Internet IPv4 address in dotted-decimal format (for example, the hexadecimal value 0xffffffff would print as 255.255.255.255).

%m

Print a margin of whitespace. If no field is specified, the default output margin width is used; otherwise, the field width determines the number of characters of white space that are printed.

%o

Print the specified integer as an unsigned octal value. If %#o is used, the alternate format prefixes the output with '0'.

%p

Print the specified pointer (void *) as a hexadecimal value.

%q

Print the specified integer as a signed octal value. If %#o is used, the alternate format prefixes the output with '0'.

%r

Print the specified integer as an unsigned value in the current output radix. The user can change the output radix using the $d dcmd. If %#r is specified, the alternate format prefixes the value with the appropriate base prefix: '0i' for binary, '0o' for octal, '0t' for decimal, or '0x' for hexadecimal.

%R

Print the specified integer as a signed value in the current output radix. If %#R is specified, the alternate format prefixes the value with the appropriate base prefix.

%s

Print the specified string (char *). If the string pointer is NULL, the string '<NULL>' is printed.

%t

Advance one or more tab stops. If no width is specified, output advances to the next tab stop; otherwise the field width determines how many tab stops are advanced.

%T

Advance the output column to the next multiple of the field width. If no field width is specified, no action is taken. If the current output column is not a multiple of the field width, white space is added to advance the output column.

%u

Print the specified integer as an unsigned decimal value. If %#u is specified, the alternate format prefixes the value with '0t'.

%x

Print the specified integer as a hexadecimal value. The characters a-f are used as the digits for the values 10-15. If %#x is specified, the alternate format prefixes the value with '0x'.

%X

Print the specified integer as a hexadecimal value. The characters A-F are used as the digits for the values 10-15. If %#X is specified, the alternate format prefixes the value with '0X'.

%Y

The specified time_t is printed as the string 'year month day HH:MM:SS'.

mdb_snprintf()

size_t mdb_snprintf(char *buf, size_t len, const char *format, ...);

Construct a formatted string based on the specified format string and arguments, and store the resulting string into the specified buf. The mdb_snprintf() function accepts the same format specifiers and arguments as the mdb_printf() function. The len parameter specifies the size of buf in bytes. No more than len - 1 formatted bytes are placed in buf; mdb_snprintf() always terminates buf with a null byte. The function returns the number of bytes required for the complete formatted string, not including the terminating null byte. If the buf parameter is NULL and len is set to zero, the function will not store any characters to buf and returns the number of bytes required for the complete formatted string; this technique can be used to determine the appropriate size of a buffer for dynamic memory allocation.

mdb_warn()

void mdb_warn(const char *format, ...);

Print an error or warning message to standard error. The mdb_warn() function accepts a format string and variable argument list that can contain any of the specifiers documented for mdb_printf(). However, the output of mdb_warn() is sent to standard error, which is not buffered and is not sent through the output pager or processed as part of a dcmd pipeline. All error messages are automatically prefixed with the string “mdb:”.

In addition, if the format parameter does not contain a newline (\n) character, the format string is implicitly suffixed with the string “: %s\n”, where %s is replaced by the error message string corresponding to the last error recorded by a module API function. For example, the following source code:

if (mdb_lookup_by_name("no_such_symbol", &sym) == -1)
       mdb_warn("lookup_by_name failed");

produces this output:

mdb: lookup_by_name failed: unknown symbol name

mdb_flush()

void mdb_flush(void);

Flush all currently buffered output. Normally, mdb's standard output is line-buffered; output generated using mdb_printf() is not flushed to the terminal (or other standard output destination) until a newline is encountered, or at the end of the current dcmd. However, in some situations you might want to explicitly flush standard output prior to printing a newline; mdb_flush() can be used for this purpose.

mdb_nhconvert()

void mdb_nhconvert(void *dst, const void *src, size_t nbytes);

Convert a sequence of nbytes bytes stored at the address specified by src from network byte order to host byte order and store the result at the address specified by dst. The src and dst parameters may be the same, in which case the object is converted in place. This function may be used to convert from host order to network order or from network order to host order, since the conversion is the same in either case.

mdb_dumpptr() and mdb_dump64()

int mdb_dumpptr(uintptr_t addr, size_t nbytes, uint_t flags,
                     mdb_dumpptr_cb_t func, void *data);
int mdb_dump64(uint64_t addr, uint64_t nbytes, uint_t flags,
                     mdb_dump64_cb_t func, void *data);

These functions can be used to generate formatted hexadecimal and ASCII data dumps that are printed to standard output. Each function accepts an addr parameter specifying the starting location, a nbytes parameter specifying the number of bytes to display, a set of flags described below, a func callback function to use to read the data to display, and a data parameter that is passed to each invocation of the callback func as its last argument. The functions are identical in every regard except that mdb_dumpptr uses uintptr_t for its address parameters and mdb_dump64 uses uint64_t. This distinction is useful when combining mdb_dump64 with mdb_pread, for example. The built-in ::dump dcmd uses these functions to perform its data display.

The flags parameter should be the bitwise OR of one or more of the following values:

MDB_DUMP_RELATIVE

Number lines relative to the start address instead of with the explicit address of each line.

MDB_DUMP_ALIGN

Align the output at a paragraph boundary.

MDB_DUMP_PEDANT

Display full-width addresses instead of truncating the address to fit the output in 80 columns.

MDB_DUMP_ASCII

Display ASCII values next to the hexadecimal data.

MDB_DUMP_HEADER

Display a header line about the data.

MDB_DUMP_TRIM

Only read from and display the contents of the specified addresses, instead of reading and printing entire lines.

MDB_DUMP_SQUISH

Elide repeated lines by placing a “*” on a line that is a repeat of the previous line.

MDB_DUMP_NEWDOT

Update the value of dot to the address beyond the last address read by the function.

MDB_DUMP_ENDIAN

Adjust for endianness. This option assumes that the word size is equal to the current group size, specified by MDB_DUMP_GROUP(). This option will always turn off alignment, headers, and ASCII display to avoid confusing output. If MDB_DUMP_TRIM is set with MDB_DUMP_ENDIAN, the number of bytes dumped will be rounded down to the nearest word size bytes.

MDB_DUMP_WIDTH(width)

Increase the number of 16-byte paragraphs per line that are displayed. The default value of width is one, and the maximum value is 16.

MDB_DUMP_GROUP(group)

Set the byte group size to group. The default group size is four bytes. The group size must be a power of two that divides the line width.

mdb_one_bit()

const char *mdb_one_bit(int width, int bit, int on);

The mdb_one_bit() function can be used to print a graphical representation of a bit field in which a single bit of interest is turned on or off. This function is useful for creating verbose displays of bit fields similar to the output from snoop(1M) -v. For example, the following source code:

#define FLAG_BUSY       0x1

uint_t flags;

/* ... */

mdb_printf("%s = BUSY\n", mdb_one_bit(8, 0, flags & FLAG_BUSY));

produces this output:

.... ...1 = BUSY

Each bit in the bit field is printed as a period (.), with each 4-bit sequence separated by a white space. The bit of interest is printed as 1 or 0, depending on the setting of the on parameter. The total width of the bit field in bits is specified by the width parameter, and the bit position of the bit of interest is specified by the bit parameter. Bits are numbered starting from zero. The function returns a pointer to an appropriately sized, null-terminated string containing the formatted bit representation. The string is automatically garbage-collected upon completion of the current dcmd.

mdb_inval_bits()

const char *mdb_inval_bits(int width, int start, int stop);

The mdb_inval_bits() function is used, along with mdb_one_bit(), to print a graphical representation of a bit field. This function marks a sequence of bits as invalid or reserved by displaying an 'x' at the appropriate bit location. Each bit in the bit field is represented as a period (.), except for those bits in the range of bit positions specified by the start and stop parameters. Bits are numbered starting from zero. For example, the following source code:

mdb_printf("%s = reserved\n", mdb_inval_bits(8, 7, 7));

produces this output:

x... .... = reserved

The function returns a pointer to an appropriately sized, null-terminated string containing the formatted bit representation. The string is automatically garbage-collected upon completion of the current dcmd.

mdb_inc_indent() and mdb_dec_indent()

ulong_t mdb_inc_indent(ulong_t n);
ulong_t mdb_dec_indent(ulong_t n);

These functions increment and decrement the numbers of columns that MDB will auto-indent with white space before printing a line of output. The size of the delta is specified by n, a number of columns. Each function returns the previous absolute value of the indent. Attempts to decrement the indent below zero have no effect. Following a call to either function, subsequent calls to mdb_printf() are indented appropriately. If the dcmd completes or is forcibly terminated by the user, the indent is restored automatically to its default setting by the debugger.

mdb_eval()

int mdb_eval(const char *s);

Evaluate and execute the specified command string s, as if it had been read from standard input by the debugger. This function returns 0 for success, or -1 for error. mdb_eval() fails if the command string contains a syntax error, or if the command string executed by mdb_eval() is forcibly aborted by the user using the pager or by issuing an interrupt.

mdb_set_dot() and mdb_get_dot()

void mdb_set_dot(uintmax_t dot);
uintmax_t mdb_get_dot(void);

Set or get the current value of dot (the “.” variable). Module developers might want to reposition dot so that, for example, it refers to the address following the last address read by the dcmd.

mdb_get_pipe()

void mdb_get_pipe(mdb_pipe_t *p);

Retrieve the contents of the pipeline input buffer for the current dcmd. The mdb_get_pipe() function is intended to be used by dcmds that want to consume the complete set of pipe input and execute only once, instead of being invoked repeatedly by the debugger for each pipe input element. Once mdb_get_pipe() is invoked, the dcmd will not be invoked again by the debugger as part of the current command. This can be used, for example, to construct a dcmd that sorts a set of input values.

The pipe contents are placed in an array that is garbage-collected upon termination of the dcmd, and the array pointer is stored in p->pipe_data. The length of the array is placed in p->pipe_len. If the dcmd was not executed on the right-hand side of a pipeline (that is, the DCMD_PIPE flag was not set in its flags parameter), p->pipe_data is set to NULL and p->pipe_len is set to zero.

mdb_set_pipe()

void mdb_set_pipe(const mdb_pipe_t *p);

Set the pipeline output buffer to the contents described by the pipe structure p. The pipe values are placed in the array p->pipe_data, and the length of the array is stored in p->pipe_len. The debugger makes its own copy of this information, so the caller must remember to free p->pipe_data if necessary. If the pipeline output buffer was previously non-empty, its contents are replaced by the new array. If the dcmd was not executed on the left side of a pipeline (that is, the DCMD_PIPE_OUT flag was not set in its flags parameter), this function has no effect.

mdb_get_xdata()

ssize_t mdb_get_xdata(const char *name, void *buf, size_t nbytes);

Read the contents of the target external data buffer specified by name into the buffer specified by buf. The size of buf is specified by the nbytes parameter; no more than nbytes will be copied to the caller's buffer. The total number of bytes read will be returned upon success; -1 will be returned upon error. If the caller wants to determine the size of a particular named buffer, buf should be specified as NULL and nbytes should be specified as zero. In this case, mdb_get_xdata() will return the total size of the buffer in bytes but no data will be read. External data buffers provide module writers access to target data that is not otherwise accessible through the module API. The set of named buffers exported by the current target can be viewed using the ::xdata built-in dcmd.

Additional Functions

Additionally, module writers can use the following string(3C) and bstring(3C) functions. They are guaranteed to have the same semantics as the functions described in the corresponding Solaris man page.

strcat()           strcpy()                strncpy()
strchr()           strrchr()               strcmp()
strncmp()          strcasecmp()            strncasecmp()
strlen()           bcmp()                  bcopy()
bzero()            bsearch()               qsort()