本文向用户介绍了编写有效的跟踪格式条目所需的知识。我们将查看跟踪格式条目的组成元素,分别举例说明每一个元素和适用它们的场景。本文结束部分将通过一个示例让您了解如何将这些元素组合在一起。

Prateek Goel, 系统软件工程师, IBM

/developerworks/i/p-pgoel.jpgPrateek Goel 是一位系统软件工程师,过去两年在 UPT AIX Release 团队工作。在此期间,他参与了网络文件传输的数据完整性测试,他目前是 ProbeVue 方面的主题专家(SME)。



2013 年 12 月 30 日

简介

跟踪是最常用的调试技术之一。它向用户提供了出现问题之前发生的行为的信息。为了做到这一点,开发人员需要在代码中放置一些跟踪钩子(trace hook)。每次运行包含跟踪钩子的代码路径时,这些跟踪钩子都会记录必要的数据。这些跟踪钩子记录的数据均采用二进制格式,然后通过用户定义的规范进行格式化和呈现,以便进行分析。因为跟踪点和跟踪数据是固定的,这种类型的跟踪被称为静态跟踪。静态跟踪可分为两类:系统跟踪和组件跟踪。对于系统跟踪,我们使用一个通用的全局内核缓冲器来保存跟踪记录(traces)。然而,在组件跟踪中,我们使用组件缓冲器来保存来自特定组件的跟踪记录。组件跟踪可以提供细粒度的灵活性,可根据需要控制个别组件的跟踪行为。跟踪报告生成工具可以使用跟踪格式文件指定的条目,将来自跟踪点的原始数据转换为可读的格式。

本文将重点介绍跟踪格式条目,包含语法、各种构造(constructs)等内容。本文结尾部分将通过一个示例展示一些常用的构造。

钩子标识符

为了惟一地标识每个跟踪点和数据,每个跟踪点都有一个相关联的标识符。通过使用这些标识符,跟踪报告生成工具将在跟踪格式文件中查找跟踪格式条目。为了管理这些数量庞大的标识符,引入了子钩子标识符(subhook identifier) 的概念。每个组件可以分配一个父钩子标识符,这个标识符在所有组件中是惟一的。它的后面可以附加一些子钩子标识符,这些标识符在组件内部有效,可以在组件层面随意使用。

用于跟踪的应用编程接口(API)

开发人员可以使用多种 API 为其代码编写跟踪数据。非泛型跟踪 API 需要编写固定长度(0 到 5 个数据字)的数据,其中每个数据字在 32 或 64 位环境下为 8 或 16 字节。泛型跟踪 API 使用户能够跟踪可变长度的数据。请参阅 参考资料 部分,详细了解系统跟踪 API。

跟踪条目的结构

跟踪条目的结构包括以下几个部分:

  • 起始部分:Hookword(包括钩子 ID、标志和子钩子 ID)
  • 结尾部分:线程 ID,时间戳(64 位情况下还包括可选的处理器 ID)
  • 中间部分:0 到 5 个数据字,被符号化为 D1、D2、D3、D4 和 D5

非泛型跟踪结构的示例:

Hookword | [ D1|D2|D3|D4|D5 ] | Thread ID|Timestamp

泛型跟踪结构的示例:

Hookword| D1 | Variable_length_data | Thread ID | Timestamp

样例 C 程序:

#include <sys/types.h>
#include <sys/trchkid.h>
#include <stdio.h>

#define HKWD_CUSTOM 0x01000000
main()
{
int *i;
i=malloc(sizeof(int)*10);
/* logs a trace entry with 3 datawords passed as argument after HKWD_CUSTOM */
TRCHKL3(HKWD_CUSTOM,sizeof(int),i,10);
}

编译上述程序:

/usr/vac/bin/cc -q64 -g myc.c -l rts

运行上述程序:

trace -aj 010 ; ./a.out ; trcstop

命令输出:

ID	ELAPSED_SEC	DELTA_MSEC	APPL    SYSCALL KERNEL  INTERRUPT
010	0.005312869	5.312869	UNDEFINED TRACE ID idx 0x1fc28
					traceid 0100 hookword  8000002801000000 type 8000
					hookdata 0000 0000000000000004 
					00000001100008D0 000000000000000A

被跟踪的数据用十六进制格式输出,除了 hookword、类型和钩子数据之外,很难识别哪个十六进制字表示哪些内容。这种数据形式难以进行分析,因为您需要知道这些数据字对应的内容并记住每个跟踪条目的这些内容。为了解决这个问题,跟踪基础架构为用户提供了一个可以定义他们自己的跟踪格式条目的工具。例如,如果上述输出用以下格式出现,那么可以更加容易地利用它们。

ID	ELAPSED_SEC	DELTA_MSEC	APPL    SYSCALL KERNEL  INTERRUPT
010	0.005312869	5.312869	Single Unit:0004 Address:1100008D0 Count:000A

为了提供子钩子 ID,用户可以使用逻辑 “或” 操作符 ("|") 将子钩子 ID 附加到钩子 ID,如下面示例所示:

#define FIRST_SUBHOOK 0x01
TRCHKL3(HKWD_CUSTOM | FIRST_SUBHOOK,sizeof(int),i,10);

构建跟踪格式条目块

宏(macro)与 C 程序中的 #define statements 类似,在该语句中,将对这些宏定义一个任务并在需要时进行调用。跟踪格式基础架构提供了一些预定义的宏,可对跟踪条目执行一些基本的任务。本节将讨论其中一些重用的宏。

  • $DATAPOINTER:包含从跟踪条目起始部分到当前位置的位移。值的范围介于 0 到跟踪条目的长度之间。
  • $BASEPOINTER:指向跟踪条目的起始位置,通常为 0。
  • $HOOKENV:表示是否来自 32 位或 64 位环境。值为 32 或 64。
  • $D1-$D5:提供跟踪条目的数据字的值,范围为 1 到 5。
  • $GENERIC:表示是否使用泛型跟踪 API 记录跟踪条目。
表 1. $GENERIC 宏值的解释
含义
0非泛型跟踪 API
1泛型跟踪 API
  • $HD:提供子钩子 ID 信息。但是对于 32 位泛型跟踪,HD 指的是所跟踪的泛型数据的长度。
  • $HL:指钩子数据的长度,单位为字节。在泛型跟踪中,该长度指的是所跟踪的泛型数据的长度。
表 2. 非泛型跟踪条目
32 bit64 位
DATAPOINTER(初始值)26
第一个数据字(D1)起始位移为 48
表 3. 泛型跟踪条目
32 位64 位
DATAPOINTER(初始值)46
第一个数据字(D1)起始位移为 48
可变数据起始位移为 816

备注:宏对于访问定义明确的跟踪条目非常有用。为了操作作为跟踪条目一部分而记录的数据中的数据,我们使用了格式代码,这些内容将在下一小节中讨论。

格式代码

格式代码用于获取 DATAPOINTER 指示的数据字部分或上述任意宏返回的数据。下表列出了一些常用的格式代码。

表 4. 缩进格式
格式解释
L=APPL将左边界移动到第一列
L=SVC将左边界移动到第二列
L=KERN将左边界移动到第三列
L=INT将左边界移动到第四列

示例:

使用这些对齐方式后,用户可以很容易发现跟踪条目是来自应用程序、系统调用、内核服务例程还是中断。在下面的代码中,诸如 APPLSYSCALL 等标记都有一个列编号,表示了它们所属的列。

ID    ELAPSED_SEC    DELTA_MSEC   APPL(1st)    SYSCALL(2nd) KERNEL(3rd)  INTERRUPT(4rth)
表 5. 操作 DATAPOINTER 的代码
格式解释
Gm.n将 DATAPOINTER 宏设置为 “m” 字节,让它位于跟踪条目中的 “n” 位之后。
Om.n将 DATAPOINTER 宏推进 “m” 个字节,让它位于跟踪条目中的 “.n” 位之后。
Rm在跟踪条目中将 DATAPOINTER 宏递减 “m” 个字节。
Wm将 DATAPOINTER 放在单词 m 所在的位置。字长为 4 或 8 字节,具体取决于执行的是 32 位格式跟踪还是 64 位格式跟踪。

示例:

G8:相当于 {{ DATAPOINTER = 8 }}(表示访问跟踪条目中的第八个字节)。

下表包含一些常用的格式代码。要查看完整列表,请参见 参考资料 小节。

表 6. 导致数据输出的代码
格式解释
Am.n左侧调整 ASCII,其中 m = 字节长度,n = 所示字段的宽度
Bm.n二进制格式的输出,其中 m = 字节长度,n 是 m 字节后要显示的位数
Xm “m” 字节的十六进制格式输出,其中 “m” 是 0 到 16 之间的值
Dm带符号的十进制格式输出,“m” 是要显示的字节数
Um不带符号的十进制格式输出,“m” 是要显示的字节数
Om八进制格式输出,“m” 是要显示的字节数。
F4浮点格式输出(类似于 %0.4E,数据长度 = 4)
F8浮点格式输出(类似于 %0.8E,数据长度 = 8)
HB trcgen() 可变长度缓冲器中的字节数。
HT32 位钩子:
hooktype (0 - E)
trcgen = 0, trchk = 1, trchl = 2, trchkg = 6 trcgent = 8, trchkt = 9, trchlt = A, trchkgt = E
HT & 0x07 屏蔽时间戳位
64 位钩子
0x8000 - 钩子被标记了时间戳。
0x4000 - 这是一个泛型跟踪。

示例:

  • $HD%D1:获得 $HD 返回的 1 个字节的数据并用十进制格式显示。DATAPOINTER 保持不变,因为数据来自宏。
  • D1:获得 DATAPOINTER 指向的位置返回的 1 个字节的数据,采用十进制格式。DATAPOINTER 增加一个字节。

SWITCH 语句

语法:

Format_code ,
Matchvalue1 { descriptor },
Matchvalue2 { descriptor2 },
\* { default_descriptor }

格式代码后面加一个逗号就是一条 SWITCH 语句。SWITCH 语句的每个 CASE 条目的组成内容包括:

  • 一个 ‘匹配值’ 和一个类型(通常为数值),与格式代码对应
  • 一个简单的 ‘字符串’ 或一个新的 ‘描述符',使用大括号括起。描述符是一组格式代码、字符串、switch 语句和循环
  • 逗号分隔符

switch 语句由 CASE 条目终止,无需逗号分隔符。选中的 CASE 条目是匹配值等于格式代码扩展的第一个条目。特殊匹配值 ‘\*' 是一个通配符,可以匹配任何值。DATAPOINTER 是根据格式代码向前推进的。

备注:如果数据来自泛型跟踪 API 等,那么将根据子钩子 ID 之类的因素采取相应的操作。

LOOP 语句

语法:

 LOOP format_code { descriptor }

描述符被运行 N 遍,其中的 N 是格式代码的数字值。DATAPOINTER 是根据格式代码和描述符的行为向前推进的。循环用于输出二进制缓冲器数据,因此描述符通常为 X1 或 X0。 请注意,X0 与 X1 类似,但是没有在每个字节之间提供空格分隔符 ‘ ’。类似地情况还包括 A0 和 A1。如果结合使用格式代码和宏,那么 DATAPOINTER 中的修改只能通过描述符表示。参考宏小节中的示例,获得有关的详细信息。

备注:当可变数据的长度是非静态的,并且被写为跟踪条目中的数据的一部分,那么此时适合使用循环。

自定义宏

宏是临时变量(只在事件期间有效),工作原理与 shell 变量相似。

语法:

   {{ $xxx = EXPR }}
EXPR is a combination of format codes, macros, and constants.
   Allowed operators are + - / *
   For example:
{{ $dog = 7 + 6 }} {{ $cat = $dog * 2 }} $dog $cat
   will output:
000D 001A

备注:宏也适用于循环数并不总是从数据开始的循环。

G1.5 {{ $count = B0.5 }} G11 LOOP $count {X0}

此处,我们从第一个字节后的第 5 位获取计数,而真正的数据从第 11 字节开始。对于这种情况,使用宏将有助于编写跟踪格式条目。

模板子例程

如果宏名称包含 3 或 4 个十六进制数字,那么这是一个模板例程。模板的跟踪 ID 就是宏的名称,插入它是为了替代宏。数据指针指向发生模板替代的位置。在模板结束时,模板子例程对数据指针所作的任何更改都将持续有效。在模板子例程内使用的宏与调用模板中的宏是一致的。在被调用的模板中,宏的第一个定义与被调用版本中的第一个定义是同一个变量。名称没有关联。

备注:模板子例程的嵌套最多可以达到十级。宏和模板子例程之间有一处细微的差别。两者都是为了避免重新编写相同的格式。但是,模板子例程可以充当已定义的钩子标识符的跟踪条目格式程序,而宏没有这项功能。

BITFLAGS

语法:

BITFLAGS [format_code|register],
flag_value string {optional string if false},   

--- or  ----

'&' mask field_value string

该语句简化了对状态标志的扩展,因为它看上去非常类似于一组 #define 语句。'&' mask 用于解释位字段。对寄存器和 mask 值执行 '&' 操作的结果将与 field_value 进行比较。如果 field_value 与提供的某个选项匹配,则会输出相对应的字符串。flag_value 和 mask 的基值是 16,如果使用了格式代码,DATAPOINTER 将被向前推进。

现在,假设您需要输出某个文件的权限。为此,您需要查看它的权限。如果文件只有只读权限,那么输出将为 r--

在下面的样例代码中,如果 $omode%o4 的值是 00400,则会输出 r,否则输出 -。对 0020 将会采取类似的操作。

BITFLAGS $omode%o4, \
00400 "r" "-" \
00200 "w" "-" \

必须显示或不显示所有内容的情况:

BITFLAGS $omode%o4, \
& 012 040   "I"
& 012 020   "B"
& 012 080   "M"

使用 $omode%o4 值掩盖 012 后,如果值为 040,则输出 I。如果值为 020,则输出 B。如果值为 080,则输出 M

跟踪格式条目的语法

了解了跟踪格式条目的构件后,让我们将所有构件组装起来并尝试编写一个完整的跟踪格式条目。

HOOK_ID version L=APPL/SYS/KERN "name" \
Macro%Format code,\
Matchvalue1 {,\
	Format code,\
	Matchvalue1a { Action },\
	Matchvalue1b { Action }\
      },\
Matchvalue2 {,\
	Macro%Format code,\
	Matchvalue2a { Action },\
	Matchvalue2b { Action }\
       }
Action could be any combination of building blocks that we have looked at.

示例

下面的 C 程序使用 TRCHKL4TRCGEN 定义的宏记录了跟踪条目:

#include <sys/types.h>
#include <sys/trchkid.h>
#include <stdio.h>

#define HKWD_CUSTOM 0x01000000
main()
{
char s[]="malloc";
char s1[]="successful malloc";
int *i;
int j=5;
int k=20;
i=malloc(sizeof(int)*10);
/* 
  * please note the way string is 
  * passed as one of the datawords in non-generic trace API  
  */
TRCHKL4(HKWD_CUSTOM,*(unsigned long *)s,i,sizeof(int),10);
TRCGEN(0,HKWD_CUSTOM|0x20,strlen(s1),strlen(s1),(char *)s1);
i[5]=20;
TRCHKL3(HKWD_CUSTOM+0x1,i+j,j,k);
}

编译上述 C 程序:

cc -q64 -g myc.c -l rts

运行(只捕捉从钩子 ID 010 传入的条目):

trace -aj 010 ; ./a.out ; trcstop

“MyCustomHook” 的跟踪条目格式:

#cat mytrace
##########################
# Check if, the entry is GENERIC
# If, not then check for subhook ID using $HD%D1
#	 if, Subhookid is 0 datawords using Macros
# 	if, subhookid is 1 print simple datawords
# 	if, none matched print Undefined Hook and subhook id
#if, the entry is GENERIC print ASCII string using LOOP construct
#	where the length of the String is in Dataword1 
#           HL macro can also be used to access the length as shown below
###########################

010 1.0 L=APPL "MyCustomHook" \
$GENERIC,\
0 {\
    $HD%D1,\
        0 { "Activity="G8 A10 \ 
		"Address=0x"$D2 {{ $total_size = $D3 * $D4 }} \
		"Size="$total_size%D4 },\
        1 { "Address=0x"$D1 "Array Index="$D2%D4 "Value="$D3%D4 },\
        \* {"Undefined Hook with subhook id = "$HD%D1 }\
    },\
1 { "String Length=" $D1%D4 {{ $loopcnt = $HL }} G16 LOOP $loopcnt {A0} }

使用自定义的跟踪条目格式查看跟踪条目:

#trcrpt -t mytrace
ID    ELAPSED_SEC    DELTA_MSEC   APPL    SYSCALL KERNEL  INTERRUPT

010   0.003872577    3.872577   MyCustomHook Activity=malloc Address=0x110000970 Size=40
010   0.003874101    0.001524   MyCustomHook String Length=17 successful malloc
010   0.003874956    0.000855   MyCustomHook Address=0x110000984 Array Index=5 Value=20

参考资料

学习

  • 非泛型 API
  • 泛型 API
  • 有关跟踪格式的更多内容
  • AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
  • AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
  • AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
  • AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
  • IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。
  • 浏览 技术书店,获得有关这些和其他技术主题的图书。

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=AIX and UNIX
ArticleID=958782
ArticleTitle=如何编写跟踪格式条目
publish-date=12302013