Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1299755
  • 博文数量: 554
  • 博客积分: 10425
  • 博客等级: 上将
  • 技术积分: 7555
  • 用 户 组: 普通用户
  • 注册时间: 2006-11-09 09:49
文章分类

全部博文(554)

文章存档

2012年(1)

2011年(1)

2009年(8)

2008年(544)

分类:

2008-04-11 15:51:52


printf()
第12 章• 输出格式化167
转换格式
每个转换字符序列都将导致提取零个或多个参数。如果没有为格式字符串提供足够的参
数,或者格式字符串已用完但仍有参数剩余,则D编译器将发出相应的错误消息。如果指
定了未定义的转换格式,则D编译器将发出相应的错误消息。转换字符序列为:
a 指针或uintptr_t 参数将列显为如下形式的内核符号名:module‘symbol-name 加上
十六进制字节偏移(可选)。如果该值不在已知内核符号定义的范围内,则该值将
列显为十六进制整数。
c char、short 或int 参数将列显为ASCII 字符。
C 如果字符为可列显的ASCII 字符,则char、short 或int 参数将列显为ASCII 字
符。如果该字符不是可列显字符,则将使用表2–5 中说明的对应转义序列将其列
显。
d char、short、int、long 或long long 参数将列显为十进制(以10 为基数)整数。
如果参数类型为signed,则将列显为带符号的值。如果参数类型为unsigned,则
将列显为无符号值。此转换与i 具有相同的含义。
e, E float、double 或long double 参数将转换为[-]d.ddde±dd 样式,其中基数字符前
有一位,基数字符后的位数等于精度。如果参数不为零,则基数字符也不为零。如
果未指定精度,则缺省精度值为6。如果精度为0 并且未指定# 标志,则不会显示
任何基数字符。E 转换格式将生成一个数字,该数字将使用E 而不是e 来引入指
数。指数总是至少包含两位数。该值将向上舍入为相应的位数。
f float、double 或long double 参数将转换为[-]ddd.ddd 样式,其中基数字符之后
的位数等于指定的精度。如果未指定精度,则缺省精度值为6。如果精度为0 并且
未指定# 标志,则不会显示任何基数字符。如果显示基数字符,则它之前至少会显
示一位数字。该值将向上舍入为相应的位数。
g, G float、double 或long double 参数将列显为f 或e 样式(如果使用G 转换字符则为
E 样式),并且使用指定有效数字位数的精度。如果显式精度为0,则会将其视为
1。使用的样式取决于转换的值:仅当转换生成的指数小于-4 或大于等于精度时,
才会使用e(或E)样式。结果的小数部分中的结尾零将删除。仅当基数字符后跟
数字时,才会显示基数字符。如果指定了# 标志,则不会从结果中删除结尾零。
i char、short、int、long 或long long 参数将列显为十进制(以10 为基数)整数。
如果参数类型为signed,则将列显为带符号的值。如果参数类型为unsigned,则
将列显为无符号值。此转换与d 具有相同的含义。
o char、short、int、long 或long long 参数将列显为无符号的八进制(以8 为基
数)整数。signed 类型或unsigned 类型的参数可用于此转换。如果指定了# 标
志,则在需要将结果的第一位强制为零时,结果的精度将增加。
p 指针或uintptr_t 参数将列显为十六进制(以16 为基数)整数。D接受任何类型的
指针参数。如果指定了# 标志,则将在非零结果的开头加上0x。
s 参数必须为char 数组或string。将一直读取数组或string 中的字节,直到达到终
止空字符或数据的结尾,并将其解释和列显为ASCII 字符。如果未指定精度,则会
printf()
168 Solaris 动态跟踪指南• 2006 年7 月
将其视为无穷的,所以将列显所有字符,直到达到第一个空字符。如果指定了精
度,则仅会列显在对应屏幕列数中显示的那一部分字符数组。如果要格式化char *
类型的参数,则应将该参数强制转换为string,或者使用Dstringof 运算符为前
缀,以指示DTrace 应跟踪的字符串字节数并设置这些字节的格式。
S 参数必须为char 数组或string。参数将按类似%s 转换的方式进行处理,但不可列
显的所有ASCII 字符都将替换为表2–5 中说明的对应转义序列。
u char、short、int、long 或long long 参数将列显为无符号十进制(以10 为基数)
整数。signed 类型或unsigned 类型的参数可用于此转换,并且结果都将格式化为
unsigned。
wc int 参数将转换为宽字符(wchar_t) 并且将列显生成的宽字符。
ws 参数必须为wchar_t 数组。会一直读取数组中的字节,直到达到终止空字符或数据
结尾,并将其解释和列显为宽字符。如果未指定精度,则会将其视为是无穷的,所
以将列显所有宽字符,直到达到第一个空字符。如果指定了精度,则仅会列显在对
应屏幕列数中显示的那一部分宽字符数组。
x, X char、short、int、long 或long long 参数将列显为无符号十六进制(以16 为基
数)整数。signed 类型或unsigned 类型的参数可用于与此转换。如果使用x 形式
的转换,则将使用字母数字abcdef。如果使用X 形式的转换,则将使用字母数字
ABCDEF。如果指定了# 标志,则将在非零结果的开头加上0x(对于%x)或0X(对
于%X)。
Y uint64_t 参数将解释为自1970 年1 月1 日00:00 世界标准时间以来的纳秒数,并且
按以下cftime(3C) 形式列显:“%Y %a %b %e %T %Z”。自1970 年1 月1 日00:00 世
界标准时间以来的当前纳秒数将通过walltimestamp 变量提供。
% 列显文字% 字符。不会转换任何参数。完整的转换规范必须为%%。
printa()
printa() 函数用于格式化D程序中聚合的结果。可通过下列两种形式之一来调用该函数:
printa(@aggregation-name);
printa(format-string, @aggregation-name);
如果使用函数的第一种形式,则dtrace(1M) 命令将提取聚合数据的一致快照并生成输出,
该输出与用于聚合的缺省输出格式等效,如第9 章中所述。
如果使用函数的第二种形式,则dtrace(1M) 命令将提取聚合数据的一致快照,并根据
format string 中指定的转换以及下列规则来生成输出:
 格式转换必须与用于创建聚合的元组签名相匹配。每个元组元素仅能显示一次。例如,
如果使用下列D语句来聚合计数:
printa()
第12 章• 输出格式化169
@a["hello", 123] = count();
@a["goodbye", 456] = count();
然后将D 语句printa(format-string, @a) 添加到探测器子句,则dtrace 将提取聚合数据
的快照并生成输出,就像输入了下列语句:
printf(format-string, "hello", 123);
printf(format-string, "goodbye", 456);
及类似语句一样(对于聚合中定义的每个元组)。
 与printf() 不同,用于printa() 的格式字符串无需包括元组的所有元素。即,元组的
长度可以为3,并且仅进行一次格式转换。因此,可以通过更改聚合声明将要在
printa() 输出中省略的任何元组键移到元组的结尾来将其省略,然后在printa() 格式字
符串中省略其对应的转换说明符。
 可通过使用附加的@ 格式标志字符(仅在用于printa() 时有效)将聚合结果包括在输出
中。@ 标志可与任何相应的格式转换说明符组合,并且可在一个格式字符串中多次显
示,以便元组结果可在输出中的任何位置多次显示。可用于聚合函数的一组转换说明符
隐含在聚合函数的结果类型中。聚合结果类型包括:
avg() uint64_t
count() uint64_t
lquantize() int64_t
max() uint64_t
min() uint64_t
quantize() int64_t
sum() uint64_t
例如,要格式化avg() 的结果,可应用%d、%i、%o、%u 或%x 格式转换。quantize() 和
lquantize() 函数会将其结果格式化为ASCII 表而不是单个值。
以下D程序显示printa() 的完整示例,通过使用profile 提供器来对caller 的值进行采
样,然后将结果格式化为简单的表:
profile:::profile-997
{
@a[caller] = count();
printa()
170 Solaris 动态跟踪指南• 2006 年7 月
}
END
{
printa(" %a\n", @a);
}
如果使用dtrace 来执行此程序,请等待几秒钟,然后按Ctrl-C 组合键,将会看到与以下示
例类似的输出:
# dtrace -s printa.d
^C
CPU ID FUNCTION:NAME
1 2 :END 1 0x1
1 ohci‘ohci_handle_root_hub_status_change+0x148
1 specfs‘spec_write+0xe0
1 0xff14f950
1 genunix‘cyclic_softint+0x588
1 0xfef2280c
1 genunix‘getf+0xdc
1 ufs‘ufs_icheck+0x50
1 genunix‘infpollinfo+0x80
1 genunix‘kmem_log_enter+0x1e8
...
printa()
第12 章• 输出格式化171
trace() 缺省格式
如果使用trace() 函数而不是printf() 函数来捕获数据,则dtrace 命令将使用缺省输出格
式来设置结果的格式。如果数据大小为1、2、4 个字节或8 个字节,则结果格式将设置为十
进制整数值。如果数据为任何其他大小并且是可列显字符序列(如果解释为字节序列),
则它将列显为ASCII 字符串。如果数据为任何其他大小并且不是可列显字符序列,则它将
列显为一系列十六进制整数格式的字节值。
trace() 缺省格式
172 Solaris 动态跟踪指南• 2006 年7 月
推理跟踪
本章介绍用于推理跟踪的DTrace 工具,它可用于试探性地跟踪数据,并在以后决定是将这
些数据提交到跟踪缓冲区还是放弃这些数据。在DTrace 中,过滤出非关注事件的主要机制
是谓词机制(已在第4 章中介绍)。如果在触发探测器时,要知道该探测事件是否是所关
注的事件,此时谓词很有用。例如,如果您只关注与某个进程或某个文件描述符关联的活
动,则在触发探测器时,您将知道该活动是否与所关注的进程或文件描述符关联。但是,
在其他情况下,可能需要在触发探测器之后的某个时间,才可知道指定的探测事件是否是
所关注的事件。
例如,如果系统调用偶尔失败,并生成常见的错误代码(例如,EIO 或EINVAL),则可能需
要检查导致该错误的代码路径。要捕获代码路径,可以启用所有探测器-但只有隔离失败
的调用,才可以构造有意义的谓词。如果故障具有偶发性或不确定性,则必须跟踪可能需
要关注的所有事件,然后对数据进行后期处理以过滤出与失败的代码路径无关的数据。在
此情况下,即使需要关注的事件数量可能相当少,但必须跟踪的事件数量还是会非常大,
这将使得后期处理很困难。
在这些情况下,您可以使用推理跟踪工具在一个或多个探测位置试探性地跟踪数据,然后
在另一个探测位置决定是否将这些数据提交到主体缓冲区。因此,跟踪数据将只包含所关
注的输出,无需进行后期处理,从而DTrace 开销也降至最低。
推理接口
下表说明了DTrace 推理函数:
表13–1DTrace 推理函数
函数名参数说明
speculation 无返回新推理缓冲区的标识符
speculate ID 表示子句的其余部分应跟踪到由ID 指定的推理缓冲区
13 第1 3 章
173
表13–1DTrace 推理函数(续)
函数名参数说明
commit ID 提交与ID 关联的推理缓冲区
discard ID 放弃与ID 关联的推理缓冲区
创建推理
speculation() 函数分配推理缓冲区并返回推理标识符。在对speculate() 函数的后续调用
中应使用推理标识符。推理缓冲区是一种有限的资源:如果在调用speculation() 时无推
理缓冲区可用,则返回的ID 为零,并将递增相应的DTrace 错误计数器。值为零的ID 始终
无效,但可以传递到speculate()、commit() 或discard()。如果调用speculation() 失
败,则将生成与以下示例类似的dtrace 消息:
dtrace: 2 failed speculations (no speculative buffer space available)
推理缓冲区的数量缺省值为1,但是也可以调高。有关更多信息,请参见第184 页中的“推
理选项和调整”。
使用推理
要使用推理,必须在执行任何数据记录操作之前,将从speculation() 返回的标识符传递到
子句中的speculate() 函数。包含speculate() 的子句中所有后续数据记录操作将被推理跟
踪。如果在D探测子句中对speculate() 的调用紧跟在数据记录操作之后,D编译器将会
生成编译时错误。因此,子句可以包含推理跟踪请求或非推理跟踪请求,但不能同时包含
二者。
不能对聚集操作、破坏性操作和exit 操作进行推理跟踪。在包含speculate() 的子句中采
用这其中某个操作的任何尝试,都将导致编译时错误。一个speculate() 不能跟在另一个
speculate() 之后:每条子句中只允许使用一个推理。只包含一个speculate() 的子句将推
理性地跟踪缺省操作,该缺省操作被定义为仅跟踪已经启用的探测器ID。有关缺省操作的
说明,请参见第10 章。
通常,将speculation() 的结果赋给线程局部变量,然后使用该变量作为其他探测器的后续
谓词以及speculate() 的参数。例如:
syscall::open:entry
{
self->spec = speculation();
}
创建推理
174 Solaris 动态跟踪指南• 2006 年7 月
syscall:::
/self->spec/
{
speculate(self->spec);
printf("this is speculative");
}
提交推理
请使用commit() 函数提交推理。提交推理缓冲区时,其中的数据将被复制到主体缓冲区。
如果指定的推理缓冲区中包含的数据多于主体缓冲区中的可用空间,则不会复制该数据,
并将递增缓冲区的删除计数。如果已推理跟踪多个CPU 中的缓冲区,则会立即复制要提交
的CPU 中的推理数据,而其他CPU 中的推理数据将在commit() 之后的某个时间复制。因
此,从commit() 开始在一个CPU 中运行到数据从推理缓冲区复制到所有CPU 中的主体缓
冲区之间可能会经历一段时间。此时间肯定不长于清除速率指定的时间。有关更多详细信
息,请参见第184 页中的“推理选项和调整”。
在将每个CPU 的推理缓冲区完全复制到相应的每个CPU 的主体缓冲区之前,后续
speculation() 调用将不能使用要提交的推理缓冲区。类似地,提交缓冲区对speculate()
的后续调用将默认被放弃,对commit() 或discard() 的后续调用将默认失败。最后,包含
commit() 的子句不能包含数据记录操作,但是子句可能包含多个commit() 调用来提交不相
交的缓冲区。
放弃推理
可使用discard() 函数放弃推理。放弃推理缓冲区时,其中的内容将被丢弃。如果推理仅
在调用discard() 的CPU 中处于活动状态,则后续对speculation() 的调用将可以立即使用
该缓冲区。如果推理在多个CPU 中处于活动状态,则在调用discard() 之后的某个时间,
后续speculation() 将可以使用放弃的缓冲区。在一个CPU 中执行discard() 之后到后续推
理操作可以使用缓冲区之间的时间肯定不长于清除速率指定的时间。如果在调用
speculation() 时,因为所有推理缓冲区当前正在被放弃或提交,而没有可用的缓冲区,将
会生成与以下示例类似的dtrace 消息:
dtrace: 905 failed speculations (available buffer(s) still busy)
可以通过调整推理缓冲区的数量或清除速率,来降低所有缓冲区都不可用的可能性。有关
详细信息,请参见第184 页中的“推理选项和调整”。
 
 
以上文章转自于 : http://developers.sun.com.cn/
阅读(469) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~