Solaris 模块调试器指南

API 函数

mdb_pwalk()

int mdb_pwalk(const char *name, mdb_walk_cb_t func, void *data,

              uintptr_t addr);

使用 name 指定的 walker 启动从 addr 开始的局部 walk,然后在每一步调用回调函数 func。 如果 addr 为 NULL,则执行全局 walk(即,mdb_pwalk() 调用相当于对没有结尾 addr 参数的 mdb_walk() 的调用)。 如果成功,此函数返回 0;如果出错,此函数返回 -1。 如果 walker 本身返回致命错误,或者如果调试器无法识别指定的 walker 名称,则 mdb_pwalk() 函数失败。如果存在命名冲突,则可以使用反引号 (`) 运算符限定 walker 名称的作用域。data 参数是仅对调用方有意义的不透明参数;在 walk 的每一步中都将它传递回 func

mdb_walk()

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

使用 name 指定的 walker 启动从 addr 开始的全局 walk,然后在每一步调用回调函数 func。 如果成功,此函数返回 0;如果出错,此函数返回 -1。 如果 walker 本身返回致命错误,或者如果调试器无法识别指定的 walker 名称,则 mdb_walk() 函数失败。如果存在命名冲突,则可以使用反引号 (`) 运算符限定 walker 名称的作用域。data 参数是仅对调用方有意义的不透明参数;在 walk 的每一步中都将它传递回 func

mdb_pwalk_dcmd()

int mdb_pwalk_dcmd(const char *wname, const char *dcname, int argc,

			const mdb_arg_t *argv, uintptr_t addr);

使用 wname 指定的 walker 启动从 addr 开始的局部 walk,然后使用指定的 argcargv 在每一步调用由 dcname 指定的 dcmd。如果成功,此函数返回 0;如果出错,此函数返回 -1。 如果 walker 本身返回致命错误,如果调试器无法识别指定的 walker 名称或 dcmd 名称,或者如果 dcmd 本身将 DCMD_ABORTDCMD_USAGE 返回给 walker,则该函数失败。如果存在命名冲突,则可以使用反引号 (`) 运算符限定 walker 名称和 dcmd 名称的作用域。 从 mdb_pwalk_dcmd() 调用时,dcmd 将在其 flags 参数中设置 DCMD_LOOPDCMD_ADDRSPEC 位,而且首次调用时将设置 DCMD_LOOPFIRST

mdb_walk_dcmd()

int mdb_walk_dcmd(const char *wname, const char *dcname, int argc,

			const mdb_arg_t *argv);

使用 wname 指定的 walker 启动全局 walk,然后使用指定的 argcargv 在每一步调用由 dcname 指定的 dcmd。 如果成功,此函数返回 0;如果出错,此函数返回 -1。如果 walker 本身返回致命错误,如果调试器无法识别指定的 walker 名称或 dcmd 名称,或者如果 dcmd 本身将 DCMD_ABORTDCMD_USAGE 返回给 walker,则该函数失败。如果存在命名冲突,则可以使用反引号 (`) 运算符限定 walker 名称和 dcmd 名称的作用域。 从 mdb_walk_dcmd() 调用时,dcmd 将在其 flags 参数中设置 DCMD_LOOPDCMD_ADDRSPEC 位,而且首次调用时将设置 DCMD_LOOPFIRST

mdb_call_dcmd()

int mdb_call_dcmd(const char *name, uintptr_t addr, uint_t flags, 

			int argc, const mdb_arg_t *argv);

使用给定的参数调用指定的 dcmd 名称。 将点变量重置为 addr,并将 addrflagsargcargv 传递到 dcmd。 如果成功,此函数返回 0;如果出错,此函数返回 -1。如果 dcmd 返回 DCMD_ERRDCMD_ABORTDCMD_USAGE,或者如果调试器无法识别指定的 dcmd 名称,则该函数失败。 如果存在命名冲突,则可以使用反引号 (`) 运算符限定 dcmd 名称的作用域。

mdb_layered_walk()

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

在使用指定 walker name 启动的 walk 的顶部,对由 wsp 表示的 walk 进行分层。如果存在命名冲突,则可以使用反引号 (`) 运算符限定名称的作用域。例如,可以使用分层的 walk,以便协助为在其他数据结构中嵌入的数据结构构造 walker。

例如,假定内核中的每个 CPU 结构都包含一个指向嵌入式结构的指针。要为嵌入式结构类型编写 walker,您可以复制代码以迭代 CPU 结构和取消引用每个 CPU 结构的相应成员,或者可以在现有 CPU walker 的顶部对嵌入式结构的 walker 进行分层。

mdb_layered_walk() 函数从 walker 的 init 例程内使用,用于将新层添加到当前 walk。 基础层是在调用 mdb_layered_walk() 的过程中初始化的。调用 walk 例程会传入指向其当前 walk 状态的指针;此状态用于构造分层 walk。在调用调用方的 walk fini 函数后,将清除每个分层 walk。 如果将多个层添加到 walk,则调用方的 walk step 函数将逐步通过第一层返回的每个元素,然后是第二层返回的每个元素,依此类推。

如果成功,mdb_layered_walk() 函数返回 0;如果出错,mdb_layered_walk() 函数返回 -1。如果调试器无法识别指定的 walker 名称,如果 wsp 指针不是有效的、处于活动 walk 状态的指针,如果分层 walker 本身无法初始化,或者如果调用方尝试在其自身的顶部对 walker 进行分层,则该函数失败。

mdb_add_walker()

int mdb_add_walker(const mdb_walker_t *w);

向调试器注册新的 walker。根据dcmd 和 Walker 名称解析中所述的名称解析规则,将 walker 添加到模块的名称空间和调试器的全局名称空间。如果成功,此函数返回 0;如果由于此模块已注册给定的 walker 名称或 walker 结构 w 未正确构造而出错,此函数返回 -1。mdb_walker_t w 中的信息将复制到内部调试器结构,因此在调用 mdb_add_walker() 后调用方可以重新使用或释放此结构。

mdb_remove_walker()

int mdb_remove_walker(const char *name);

删除具有指定 name 的 walker。如果成功,此函数返回 0;如果出错,此函数返回 -1。将从当前模块的名称空间中删除 walker。如果 walker 名称是未知的,或者它仅在另一模块的名称空间中注册,则该函数失败。mdb_remove_walker() 函数可以用于删除使用 mdb_add_walker() 动态添加的 walker,或者以静态方式作为模块链接结构的一部分添加的 walker。在 walker 名称中不能使用作用域运算符;mdb_remove_walker() 的调用方尝试删除其他模块导出的 walker 是不合法的。

mdb_vread()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);

使用这些函数可以从给定的目标虚拟地址(由 addr 参数指定)读取和写入数据。mdb_vread() 函数在读取成功时返回 nbytes,出错时返回 -1;如果读取由于只能从指定的地址读取一部分数据而被截断,则返回 -1。mdb_vwrite() 函数在写入成功时返回实际写入的字节数;出错时返回 -1。

mdb_fread()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);

使用这些函数可以从对应于给定的目标虚拟地址(由 addr 参数指定)的目标文件位置读取和写入数据。 mdb_fread() 函数在读取成功时返回 nbytes,出错时返回 -1;如果读取由于只能从指定的地址读取一部分数据而被截断,则返回 -1。mdb_fwrite() 函数在写入成功时返回实际写入的字节数;出错时返回 -1。

mdb_pread()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);

使用这些函数可以从给定的目标物理地址(由 addr 参数指定)读取和写入数据。mdb_pread() 函数在读取成功时返回 nbytes,出错时返回 -1;如果读取由于只能从指定的地址读取一部分数据而被截断,则返回 -1。mdb_pwrite() 函数在写入成功时返回实际写入的字节数;出错时返回 -1。

mdb_readstr()

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

mdb_readstr() 函数将从目标虚拟地址 addr 开始、以空字符结尾的 C 字符串读入由 s 寻址的缓冲区。该缓冲区的大小由 nbytes 指定。如果字符串过长而无法放在缓冲区中,则会将字符串截断到缓冲区大小,并在 s[nbytes - 1] 处存储空字节。成功时将返回在 s 中存储的字符串长度(不包括结尾的空字节);否则返回 -1 以指示出现了错误。

mdb_writestr()

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

mdb_writestr() 函数将 s 中以空字符结尾的 C 字符串(包括结尾的空字节)写入 addr 所指定地址处的目标虚拟地址空间。成功时将返回写入的字节数(不包括结尾的空字节);否则返回 -1 以指示出现了错误。

mdb_readsym()

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

mdb_readsym()mdb_vread() 类似,只不过是读取开始的虚拟地址是根据 name 指定的符号的值获取的。如果找不到该名称的符号或者出现读取错误,则返回 -1;否则返回 nbytes 以表示成功。

如果需要区分符号查找失败和读取失败,则调用方可以首先单独查找符号。主要可执行文件的符号表用于符号查找;如果符号驻留在其他符号表中,则必须首先应用 mdb_lookup_by_obj(),然后应用 mdb_vread()

mdb_writesym()

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

mdb_writesym()mdb_vwrite() 相同,只不过写入开始的虚拟地址是根据按名称指定的符号的值获取的。如果找不到该名称的符号,则返回 -1。否则,在成功时返回成功写入的字节数,出错时返回 -1。主要可执行文件的符号表用于符号查找;如果符号驻留在其他符号表中,则必须首先应用 mdb_lookup_by_obj(),然后应用 mdb_vwrite()

mdb_readvar()mdb_writevar()

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

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

mdb_readvar()mdb_vread() 类似,只不过读取开始的虚拟地址和要读取的字节数是根据 name 指定的符号的值和大小获取的。 如果找不到该名称的符号,则返回 -1。成功时返回符号大小(读取的字节数);出错时返回 -1。对于读取具有固定大小的已知变量,这是很有用的。例如:

				int hz; 	/* system clock rate */

				mdb_readvar(&hz, "hz");

如果需要区分符号查找失败和读取失败,则调用方可以首先单独查找符号。 调用方还必须仔细检查相关符号的定义,以便确保局部声明的类型与目标定义的类型完全相同。例如,如果调用方声明 int,相关符号实际上是 long,而且调试器检查的是 64 位内核目标,则 mdb_readvar() 会将 8 个字节复制回调用方的缓冲区,在存储 int 后损坏 4 个字节。

mdb_writevar()mdb_vwrite() 相同,只不过写入开始的虚拟地址和要写入的字节数是根据按名称指定的符号的值和大小获取的。 如果找不到该名称的符号,则返回 -1。否则,在成功时返回成功写入的字节数,出错时返回 -1。

对于这两个函数,主要可执行文件的符号表用于符号查找;如果符号驻留在其他符号表中,则必须首先应用 mdb_lookup_by_obj(),然后应用 mdb_vread()mdb_vwrite()

mdb_lookup_by_name()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);

查找指定的符号名称,并将 ELF 符号信息复制到 sym 指向的 GElf_Sym 中。如果找到符号,则函数返回 0;否则返回 -1。name 参数指定符号名称。object 参数通知调试器查找符号的位置。对于 mdb_lookup_by_name() 函数,目标文件缺省为 MDB_OBJ_EXEC。对于 mdb_lookup_by_obj(),对象名称应该为以下名称之一:

MDB_OBJ_EXEC

在可执行文件的符号表(.symtab 部分)中查找。对于内核崩溃转储,这对应于 unix.X 文件或 /dev/ksyms 中的符号表。

MDB_OBJ_RTLD

在运行时链接编辑器的符号表中查找。对于内核崩溃转储,这对应于 krtld 模块的符号表。

MDB_OBJ_EVERY

在所有的已知符号表中查找。对于内核崩溃转储,这包括 unix.X 文件或 /dev/ksyms 中的 .symtab.dynsym 部分,以及每个模块符号表(如果已处理它们)。

object

如果显式指定了特定装入对象的名称,则仅搜索此对象的符号表。可以根据符号名称解析中所述的装入对象命名约定来命名对象。

mdb_lookup_by_addr()

int mdb_lookup_by_addr(uintptr_t addr, uint_t flag, char *buf,

				size_t len, GElf_Sym *sym);

查找与指定地址相对应的符号,然后将 ELF 符号信息复制到 sym 指向的 GElf_Sym 中,将符号名称复制到 buf 寻址的字符数组中。如果找到对应符号,则函数返回 0;否则返回 -1。

标志参数指定查找模式,应为以下参数之一:

MDB_SYM_FUZZY

允许基于当前的符号距离设置进行模糊匹配。可以使用内置的 ::set -s 命令控制符号距离。如果已设置显式符号距离(绝对模式),则在符号值与地址的距离不超过绝对符号距离时,地址可以与符号匹配。如果启用智能模式(符号距离 = 0),则在地址位于范围 [符号值, 符号值 + 符号大小) 之内时,它可以与符号匹配。

MDB_SYM_EXACT

不允许模糊匹配。仅当符号值正好等于指定地址时,符号才能与地址匹配。

如果出现符号匹配,则将符号的名称复制到调用方提供的 buf 中。len 参数指定此缓冲区的长度(以字节为单位)。调用方的 buf 的大小至少应为 MDB_SYM_NAMLEN 字节。调试器将名称复制到此缓冲区,并附加结尾空字节。如果名称长度超过了缓冲区的长度,则该名称将被截断,但是它始终包括结尾空字节。

mdb_getopts()

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

解析和处理指定参数数组 (argv) 中的选项和选项参数。 argc 参数表示参数数组的长度。此函数按顺序处理每个参数,然后停止和返回无法处理的第一个参数的数组索引。 如果成功处理了所有参数,则返回 argc

argcargv 参数后面,mdb_getopts() 函数接受参数(说明应出现在 argv 数组中的选项)的变量列表。 每个选项都由一个选项字母(char 参数)、一种选项类型(uint_t 参数)以及一个或两个附加参数来说明,如下表所示。选项参数列表以 NULL 参数结尾。 类型应为以下类型之一:

MDB_OPT_SETBITS

该选项将指定的位 OR 到标志字中。该选项由以下参数说明:

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

如果类型为 MDB_OPT_SETBITS,而且在 argv 列表中检测到选项 c,则调试器将位 OR 到指针 p 引用的整数中。

MDB_OPT_CLRBITS

该选项从标志字中清除指定的位。该选项由以下参数说明:

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

如果类型为 MDB_OPT_CLRBITS,而且在 argv 列表中检测到选项 c,则调试器将从指针 p 引用的整数中清除位。

MDB_OPT_STR

该选项接受字符串参数。该选项由以下参数说明:

char c, uint_t type, const char **p

如果类型为 MDB_OPT_STR,而且在 argv 列表中检测到选项 c,则调试器在 p 引用的指针中存储指向 c 后面的字符串参数的指针。

MDB_OPT_UINTPTR

该选项接受 uintptr_t 参数。该选项由以下参数说明:

char c, uint_t type, uintptr_t *p

如果类型为 MDB_OPT_UINTPTR,而且在 argv 列表中检测到选项 c,则调试器在 p 引用的 uintptr_t 中存储 c 后面的整数参数。

MDB_OPT_UINT64

该选项接受 uint64_t 参数。该选项由以下参数说明:

char c, uint_t type, uint64_t *p

如果类型为 MDB_OPT_UINT64,而且在 argv 列表中检测到选项 c,则调试器在 p 引用的 uint64_t 中存储 c 后面的整数参数。

例如,以下源代码:

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);



        /* ... */

}
说明如何在 dcmd 中使用 mdb_getopts(),以接受布尔选项 "-v"(它将 opt_v 变量设置为 TRUE)和选项 "-s"(它接受存储在 opt_s 变量中的字符串参数)。如果在返回给调用方之前,mdb_getopts() 函数检测到选项字母无效或者缺少选项参数,则它还会自动发出警告消息。在 dcmd 完成时,调试器将自动对参数字符串和 argv 数组的存储进行垃圾收集。

mdb_strtoull()

u_longlong_t mdb_strtoull(const char *s);

将指定的字符串 s 转换为 unsigned long long 表示形式。此函数适用于处理和转换不适合使用 mdb_getopts() 进行处理和转换的字符串参数。如果无法将字符串参数转换为有效的整数表示形式,则该函数失败,同时列显相应的错误消息并异常中止 dcmd。因此,不需要错误检查代码。字符串可以使用任何有效基数说明符(0i、0I、0o、0O、0t、0T、0x 或 0X)作为前缀;否则,使用缺省基数解释它。如果 s 中的任何字符不适合于基数,或者如果发生整数溢出,则该函数将失败并异常中止 dcmd。

mdb_alloc()mdb_zalloc()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() 分配 size 字节的调试器内存,并返回指向已分配内存的指针。已分配的内存至少是双字对齐的,以便它可以保存任何 C 数据结构。 不能采用更高的对齐方式。flags 参数应该是以下一个或多个值的按位 OR

UM_NOSLEEP

如果完成请求所需的足够内存不是立即可用,则返回 NULL 以指示失败。调用方必须检查 NULL 并相应处理此情况。

UM_SLEEP

如果完成请求所需的足够内存不是立即可用,则在可以完成请求之前一直休眠。因此,UM_SLEEP 分配一定会成功。调用方无需检查 NULL 返回值。

UM_GC

在此调试器命令结束时,自动对分配进行垃圾收集。调用方随后不应在此块上调用 mdb_free(),因为调试器将自动执行取消分配操作。 从 dcmd 内进行的所有内存分配都必须使用 UM_GC,以便在用户中断 dcmd 的情况下,调试器可以对内存进行垃圾收集。

mdb_zalloc()mdb_alloc() 类似,但是将已分配内存返回给调用方之前用零填充该内存。对于 mdb_alloc() 所返回内存的初始内容没有任何保证。mdb_free() 用于释放以前分配的内存(除非为它分配了 UM_GC)。 缓冲区地址和大小必须与原始分配完全匹配。通过 mdb_free() 仅释放部分分配是不合法的。多次释放分配也是不合法的。零字节分配始终返回 NULL;释放大小为零的 NULL 指针总是会成功。

mdb_printf()

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

使用指定的格式字符串和参数列显带格式的输出。模块编写者应该将 mdb_printf() 用于所有输出,但警告和错误消息除外。在适当的情况下,此函数会自动触发内置的输出页面调度程序。mdb_printf() 函数与 printf(3C) 类似,但有一些例外:不支持宽字符串的 %C%S%ws 说明符,不支持 %f 浮点格式,两种可能的双精度格式的 %e%E%g%G 说明符仅生成单一样式的输出,不支持 %.n 格式的精度说明。支持的说明符列表如下:

标志说明符

%#

如果在格式字符串中找到 # 符号,则这会选择给定格式的替换形式。 并不是所有的格式都具有替换形式;替换形式随格式的不同而不同。有关替换格式的详细信息,请参阅下面的格式说明。

%+

列显带符号值时,始终显示符号(即以 '+' 或 '-' 为前缀)。 如果没有 %+,则正值不带有符号前缀,而在负值前面加上 '-' 前缀。

%-

在指定的字段宽度内左对齐输出。如果输出的宽度小于指定的字段宽度,则在右侧用空白填充输出。如果没有 %-,则缺省情况下将值右对齐。

%0

如果输出是右对齐的,而且输出的宽度小于指定的字段宽度,则用零填充输出字段。如果没有 %0,则在右对齐的值前面加上空白,以填充字段。

字段宽度说明符

%n

将字段宽度设置为指定的十进制值。

%?

将字段宽度设置为十六进制指针值的最大宽度。在 ILP32 环境中为 8,在 LP64 环境中为 16。

%*

将字段宽度设置为在参数列表的当前位置指定的值。假定此值为 int。请注意,在 64 位编译环境中,可能需要将 long 值强制转换为 int

整数说明符

%h

要列显的整数值为 short

%l

要列显的整数值为 long

%ll

要列显的整数值为 long long

终端属性说明符

如果调试器的标准输出是终端,而且可以通过 terminfo 数据库获取终端属性,则可以使用以下终端转义结构:

%<n>

启用对应于 n 的终端属性。对于 %<> 的每个实例,只能启用一个属性。

%</n>

禁用对应于 n 的终端属性。请注意,在反白显示、灰显文本和粗体文本的情况下,禁用这些属性的终端代码可能是相同的。因此,相互独立地禁用这些属性也许是不可能的。

如果没有可用的终端信息,则 mdb_printf() 将忽略每个终端属性结构。有关终端属性的更多信息,请参见 terminfo(4)。可用的 terminfo 属性如下:

a

替换字符集

b

粗体文本

d

灰显文本

r

反白显示

s

最突出的功能

u

加下划线

格式说明符

%%

列显 '%' 符号。

%a

以符号形式列显地址。与 %a 关联的值的最小大小是 uintptr_t;不需要指定 %la。如果打开地址到符号的转换,则调试器会尝试将地址转换为符号名称后跟当前输出中的偏移,并列显此字符串;否则,在缺省输出基数中列显值。 如果使用 %#a,则替换格式会将 ':' 后缀添加到输出。

%A

此格式与 %a 相同,只不过无法将地址转换为符号名称以及偏移时,不列显任何内容。如果使用 %#A,则在地址转换失败时替换格式列显 '?'。

%b

以符号形式解码并列显位字段。此说明符需要两个连续参数:位字段值(对于 %bint,对于 %lblong,等等)和一个指向 mdb_bitmask_t 结构的数组的指针:

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;

数组应该以其 bm_name 字段设置为 NULL 的结构结尾。使用 %b 时,调试器读取值参数,然后循环检查每个 mdb_bitmask 结构以查看是否为以下情况:

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

如果此表达式为真,则列显 bm_name 字符串。列显的每个字符串由逗号分隔。以下示例说明如何使用 %b 解码 kthread_t 中的 t_flag 字段:

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);



		...

}

如果 t_flag 设置为 0x000a,则该函数将列显:

t_flag = <T_WAKEABLE,T_TALLOCSTK>

如果指定 %#b,则会将与位掩码数组中的元素不匹配的所有位的联合列显为已解码名称后面的十六进制值。

%c

将指定的整数列显为 ASCII 字符。

%d

将指定的整数列显为带符号十进制值。与 %i 相同。如果指定 %#d,则替换格式将为值添加前缀 '0t'。

%e

以浮点格式 [+/-]d.ddddddde[+/-]dd 列显指定的双精度数,其中在基数字符前有一位,精度为七位,在指数后至少有两位。

%E

使用与 %e 相同的规则列显指定的双精度数,但指数字符将是 'E' 而不是 'e'。

%g

以与 %e 相同的浮点格式列显指定的双精度数,但精度为十六位。如果指定 %llg,则参数应为 long double 类型(四精度浮点值)。

%G

使用与 %g 相同的规则列显指定的双精度数,但指数字符将是 'E' 而不是 'e'。

%i

将指定的整数列显为带符号十进制值。与 %d 相同。如果指定 %#i,则替换格式将为值添加前缀 '0t'。

%I

将指定的 32 位无符号整数列显为点分十进制格式的 Internet IPv4 地址(例如,十六进制值 0xffffffff 将列显为 255.255.255.255)。

%m

列显由空格组成的边距。如果未指定字段,则使用缺省的输出边距宽度;否则,字段宽度确定列显的空格字符数。

%o

将指定的整数列显为无符号八进制值。如果使用 %#o,则替换格式将为输出添加前缀 '0'。

%p

将指定的指针 (void *) 列显为十六进制值。

%q

将指定的整数列显为带符号八进制值。如果使用 %#o,则替换格式将为输出添加前缀 '0'。

%r

在当前输出基数中将指定的整数列显为无符号值。用户可以使用 $d dcmd 更改输出基数。如果指定 %#r,则替换格式将为值添加相应的基数前缀: '0i'(用于二进制)、'0o'(用于八进制)、'0t'(用于十进制)或 '0x'(用于十六进制)。

%R

在当前输出基数中将指定的整数列显为带符号值。如果指定 %#R,则替换格式将为值添加相应的基数前缀。

%s

列显指定的字符串 (char *)。如果字符串指针为 NULL,则列显字符串 '<NULL>'。

%t

前移一个或多个制表停止位置。如果未指定宽度,则输出前移到下一个制表停止位置;否则,字段宽度确定前移多少个制表停止位置。

%T

将输出列前移到字段宽度的下一个倍数。如果未指定字段宽度,则不执行操作。如果当前输出列不是字段宽度的倍数,则将添加空格以前移输出列。

%u

将指定的整数列显为无符号十进制值。如果指定 %#u,则替换格式将为值添加前缀 '0t'。

%x

将指定的整数列显为十六进制值。字符 a-f 用作代表值 10-15 的符号。如果指定 %#x,则替换格式将为值添加前缀 '0x'。

%X

将指定的整数列显为十六进制值。字符 A-F 用作值 10-15 的位。如果指定 %#X,则替换格式将为值添加前缀 '0X'。

%Y

将指定的 time_t 列显为字符串 'year month day HH:MM:SS'。

mdb_snprintf()

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

基于指定的格式字符串和参数构造带格式字符串,并将生成的字符串存储在指定的 buf 中。mdb_snprintf() 函数接受与 mdb_printf() 函数相同的格式说明符和参数。len 参数指定 buf 的大小(以字节为单位)。 在 buf 中放置的带格式字节不超过 len - 1 个;mdb_snprintf() 始终使 buf 以空字节结尾。该函数返回完整的带格式字符串所需的字节数,不包括结尾的空字节。如果 buf 参数为 NULL,而且 len 设置为零,则该函数不会将任何字符存储到 buf 中,但返回完整的带格式字符串所需的字节数;此技术可用于确定适当的缓冲区大小以进行动态内存分配。

mdb_warn()

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

将错误或警告消息列显到标准错误。mdb_warn() 函数接受格式字符串和可能包含为 mdb_printf() 记录的任一说明符的变量参数列表。但是,mdb_warn() 的输出被发送到标准错误,未进行缓冲,且未通过输出页面调度程序发送或作为 dcmd 管道的一部分处理。 所有错误消息都自动使用字符串 "mdb:" 作为前缀。

此外,如果 format 参数不包含换行符 (\n),则格式字符串将隐式带有后缀字符串 ":%s\n",其中 %s 被替换为对应于模块 API 函数记录的最后一个错误的错误消息字符串。例如,以下源代码:

if (mdb_lookup_by_name("no_such_symbol", &sym) == -1)

       mdb_warn("lookup_by_name failed");

生成以下输出:

mdb: lookup_by_name failed: unknown symbol name

mdb_flush()

void mdb_flush(void);

刷新所有当前缓冲的输出。通常,mdb 的标准输出是按行缓冲的;只有遇到新行或者位于当前 dcmd 的结尾时,才会将使用 mdb_printf() 生成的输出刷新到终端(或其他标准输出目标)。但是,在某些情况下,可能希望在列显新行之前显式刷新标准输出;可以使用 mdb_flush() 实现此目的。

mdb_nhconvert()

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

将存储在由 src 指定的地址的 nbytes 字节序列从网络字节顺序转换为主机字节顺序,并将结果存储在由 dst 指定的地址。 srcdst 参数可能是相同的,这种情况下将就地转换对象。 可以使用此函数从主机顺序转换为网络顺序,或从网络顺序转换为主机顺序,因为在任一情况下转换都是相同的。

mdb_dumpptr()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);

这些函数可以用于生成列显到标准输出的带格式十六进制和 ASCII 数据转储。 每个函数都接受 addr 参数(指定起始位置)、nbytes 参数(指定要显示的字节数)、如下所述的一组标志、func 回调函数(用于读取要显示的数据)和作为其最后一个参数传递到回调 func 的每个调用的数据参数。这些函数在各方面都是相同的,只不过 mdb_dumpptr 使用 uintptr_t 作为其地址参数,而 mdb_dump64 使用 uint64_t。 例如,将 mdb_dump64mdb_pread 组合使用时,此不同是很有用的。 内置的 ::dump dcmd 使用这些函数执行其数据显示。

flags 参数应该是以下一个或多个值的按位 OR:

MDB_DUMP_RELATIVE

相对于起始地址而不是每行的显式地址的数字行。

MDB_DUMP_ALIGN

在段落边界上对齐输出。

MDB_DUMP_PEDANT

显示全宽地址,而不是截断地址以适合 80 列输出。

MDB_DUMP_ASCII

在十六进制数据的旁边显示 ASCII 值。

MDB_DUMP_HEADER

显示有关数据的标题行。

MDB_DUMP_TRIM

仅读取并显示指定地址的内容,而不是读取和列显整行。

MDB_DUMP_SQUISH

通过在与前面行重复的行上放置 "*" 来取消重复行。

MDB_DUMP_NEWDOT

将点的值更新为超过函数读取的最后一个地址的地址。

MDB_DUMP_ENDIAN

按字节存储顺序调整。 此选项假定字大小等于由 MDB_DUMP_GROUP() 指定的当前组大小。此选项将始终关闭对齐方式、标题和 ASCII 显示,以避免输出冲突。 如果使用 MDB_DUMP_ENDIAN 设置了 MDB_DUMP_TRIM,则转储的字节数将向下舍入到最接近的字大小字节。

MDB_DUMP_WIDTH(width)

增加所显示的每行 16 字节段落数。 width 的缺省值为 1,最大值为 16。

MDB_DUMP_GROUP(group)

将字节组大小设置为 group。 缺省 group 大小为 4 字节。 group 大小必须是 2 的幂,且可以整除行宽。

mdb_one_bit()

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

mdb_one_bit() 函数可以用于列显在其中打开或关闭相关单个位的位字段的图形化说明。对于创建与 snoop(1M) -v 的输出类似的位字段的详细显示,此函数是很有用的。 例如,以下源代码:

#define FLAG_BUSY       0x1



uint_t flags;



/* ... */



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

生成以下输出:

.... ...1 = BUSY

位字段中的每个位都列显为一个句点 (.),各个 4 位序列由空格分隔。相关位列显为 1 或 0,具体取决于 on 参数的设置。位字段的总 width(以位计)由 width 参数指定,相关位的位位置由 bit 参数指定。位从零开始编号。该函数将返回一个指针,该指针指向某个包含带格式位表示的大小适当、以空字符结尾的字符串。当前 dcmd 完成时,将自动对该字符串进行垃圾收集。

mdb_inval_bits()

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

可以将 mdb_inval_bits() 函数和 mdb_one_bit() 一起使用列显位字段的图形化说明。通过在适当的位位置上显示 'x',此函数将位序列标记为无效或保留。位字段中的每个位都表示为一个句点 (.),但由 start 和 stop 参数指定的位位置范围中的那些位除外。位从零开始编号。例如,以下源代码:

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

生成以下输出:

x... .... = reserved

该函数将返回一个指针,该指针指向某个包含带格式位表示的大小适当、以空字符结尾的字符串。当前 dcmd 完成时,将自动对该字符串进行垃圾收集。

mdb_inc_indent()mdb_dec_indent()

ulong_t mdb_inc_indent(ulong_t n);

ulong_t mdb_dec_indent(ulong_t n);

在列显输出行之前,这些函数递增和递减 MDB 将用空格自动缩进的列数。增量大小由列数 n 指定。每个函数都返回缩进以前的绝对值。尝试将缩进递减到低于零不起作用。在调用任一函数后,将相应地缩进对 mdb_printf() 的后续调用。如果 dcmd 完成或者它被用户强行终止,则调试器会将缩进自动恢复为其缺省设置。

mdb_eval()

int mdb_eval(const char *s);

评估并执行指定的命令字符串 s,就好像它由调试器从标准输入读取一样。如果成功,此函数返回 0;如果出错,此函数返回 -1。如果命令字符串包含语法错误,或者如果 mdb_eval() 执行的命令字符串被用户强行中止(使用页面调度程序或发出中断),则 mdb_eval() 失败。

mdb_set_dot()mdb_get_dot()

void mdb_set_dot(uintmax_t dot);

uintmax_t mdb_get_dot(void);

设置或获取点("." 变量)的当前值。模块开发者可能希望重新定位点,以便(例如)它引用 dcmd 读取的最后一个地址后面的地址。

mdb_get_pipe()

void mdb_get_pipe(mdb_pipe_t *p);

检索当前 dcmd 的管道输入缓冲区的内容。mdb_get_pipe() 函数供要占用完整的管道输入集但只执行一次的 dcmd 使用,以避免调试器针对每个管道输入元素重复调用 dcmd。在调用 mdb_get_pipe() 后,调试器不会再次在当前命令中调用 dcmd。例如,这可以用于构造对一组输入值进行排序的 dcmd。

管道内容放置在 dcmd 终止时对其进行垃圾收集的数组中,而数组指针存储在 p->pipe_data 中。 数组的长度放置在 p->pipe_len 中。如果未在管道的右侧执行 dcmd(即,未在其 flags 参数中设置 DCMD_PIPE 标志),则将 p->pipe_data 设置为 NULL,并将 p->pipe_len 设置为零。

mdb_set_pipe()

void mdb_set_pipe(const mdb_pipe_t *p);

将管道输出缓冲区设置为管道结构 p 说明的内容。管道值放置在数组 p->pipe_data 中,而数组的长度存储在 p->pipe_len 中。 调试器建立自己的此信息副本,因此调用方必须牢记在必要时释放 p->pipe_data。如果管道输出缓冲区以前不为空,则其内容将被替换为新数组。如果未在管道的左侧执行 dcmd(即,未在其 flags 参数中设置 DCMD_PIPE_OUT 标志),则此函数不起作用。

mdb_get_xdata()

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

将按名称指定的目标外部数据缓冲区的内容读入 buf 指定的缓冲区。buf 的大小由 nbytes 参数指定;复制到调用方缓冲区的字节数不会超过 nbytes。成功时将返回读取的总字节数;出错时将返回 -1。如果调用方要确定特定命名缓冲区的大小,则应该将 buf 指定为 NULL,并应该将 nbytes 指定为零。在这种情况下,mdb_get_xdata() 将返回缓冲区的总大小(以字节为单位),但不会读取任何数据。模块编写者可以通过外部数据缓冲区对无法通过模块 API 访问的目标数据进行访问。可以使用 ::xdata 内置 dcmd 查看当前目标导出的命名缓冲区集。

其他函数

此外,模块编写者还可以使用以下 string(3C)bstring(3C) 函数。它们保证具有与对应 Solaris 手册页中说明的函数相同的语义。

strcat()           strcpy()                strncpy()

strchr()           strrchr()               strcmp()

strncmp()          strcasecmp()            strncasecmp()

strlen()           bcmp()                  bcopy()

bzero()            bsearch()               qsort()