WEB开发网
开发学院操作系统Linux/Unix AIX 6.1 的动态跟踪工具 ProbeVue 阅读

AIX 6.1 的动态跟踪工具 ProbeVue

 2008-11-10 08:26:14 来源:WEB开发网   
核心提示:VUE 语言简介 VUE 语言是用来编写 ProbeVue 动态跟踪程序的语言,VUE 脚本是指用 VUE 语言编写的程序,AIX 6.1 的动态跟踪工具 ProbeVue,VUE 脚本可以用来确定动态探针所在的探针点,指定动态探针触发条件和探针的行为(例如捕获哪些跟踪数据), ProbeVue 为 AIX 系统程序跟

VUE 语言简介

VUE 语言是用来编写 ProbeVue 动态跟踪程序的语言。VUE 脚本是指用 VUE 语言编写的程序。VUE 脚本可以用来确定动态探针所在的探针点,指定动态探针触发条件和探针的行为(例如捕获哪些跟踪数据)。简单来说,VUE 脚本就是告诉 probevue 那里跟踪,什么时候跟踪,以及跟踪什么数据。

一个典型的 VUE 程序包含几个子句,每个子句包含一个或多个调查规范以及相应的具有可选谓词的用户操作。VUE 脚本提供了声明、操作和输出数据的工具以及某些对执行的控制。VUE 脚本的子句有三种元素组成:

探针点声明 - 该声明标示了一系列的可以启用的探针点。它包含若干个探针点元组。

动作块 - 动作块包含探针动作代码,这些代码在探针触发时执行。

可选的前缀 - 包含了探针被触发的条件。只有在条件被满足时动作块中的探针动作代码才会被执行。

VUE 语言的特性可以总结为以下几点:

支持 C-89 数据类型和 VUE 特殊的数据类型(例如 String, List 等类型)

支持不同的数据作用域,包括线程内部数据,全局数据,操作系统内核数据等等

支持读取被跟踪函数的参数和返回值

支持内置变量来返回信息。例如 __pid 返回跟踪进程的进程号,__tid 返回跟踪线程的线程号,__pname 返回执行文件名

支持众多辅助函数,例如 stkstrace(),timestamp(),get_userstring(),printf() 等等

支持 while 表达式,用于有条件的启动探针点

支持 Shell 表达式,可以访问 Shell 环境变量和参数值

VUE 程序结构

一个典型的 VUE 程序包含几个子句,每个子句包含一个或多个调查规范以及相应的具有可选谓词的用户操作。VUE 脚本提供了声明、操作和输出数据的工具以及某些对执行的控制。VUE 脚本的子句有三种元素组成:

探针点声明:探针点声明标示了一系列的可以启用的探针点。它包含若干个探针点元组。

动作块:动作块包含探针动作代码,这些代码在探针触发时执行。

可选的前缀:前缀包含了探针被触发的条件。只有在条件被满足时动作块中的探针动作代码才会被执行。

VUE 语言支持的变量类型包括自动变量,线程局部变量,内核全局变量,入口 / 出口变量,内置变量。

VUE 的自动变量与 C 语言中的自动变量相似。它仅在子句的操作块内起作用,每次调用操作块时重新创建该变量。

线程局部变量被跟踪线程在首次执行使用线程局部变量的操作块时被初始化。线程局部变量一旦创建,只要 ProbeVue 跟踪程序是活动的并且被跟踪线程没有退出,它就一直存在。线程局部变量的值特定于线程,并且在同一程序的任何子句的执行过程中保持不变。

全局变量可在一个或多个 ProbeVue 跟踪程序中使用全局变量。对于所有执行 ProbeVue 程序的线程,全局变量的值相同。全局变量初始值为 0 或者 NULL。

内核全局变量可以被直接引用,并且可以在任何用户操作块中访问它的当前值。指针内核变量的引用可以被取消。如果内核变量为结构、并集或者结构或并集指针,那么可在跟踪脚本中引用其成员名。只有通过内核导出的变量才可以被访问。

入口 / 出口变量可以被与探针点(该点位于内核或用户例程的人口或出口位置点)关联的子句可以访问,并作为参数传递到被跟踪例程。例如,read 系统调用获取三个参数:文件描述符标识、用户缓冲区指针和要读取的数据大小值。如果探针规范是 @@syscall:*:read:entry(它是 read 系统调用人口点),那么可访问这三个参数的值。

VUE 脚本在运行时可使用内置变量,并且不需要声明。VUE 脚本支持的内置变量见下表:

表 1. VUE 内置变量

内置变量名称 描述
__tid 目标线程的线程标识
__pid 目标线程的进程标识
__ppid 目标线程的父进程标识
__pgid 目标线程的进程组标识
__pname 目标线程的进程名称
__uid 、__euid 目标线程的真实和有效用户标识
__errno 目标线程的当前错误号的值
__kernelmode 内核方式(值为 1)或用户方式(值为 0)
__execname 当前可执行名称
__r3 、…、__r10 函数参数的 GP 寄存器值或返回值

内置函数也是 VUE 脚本的重要功能。编写 VUE 脚本时最常用的内置函数包括:

stktrace - 生成和打印运行时堆栈跟踪。

get _ probe - 返回当前调查位置。

timestamp - 根据添加戳记之后耗用的时间返回当前时间戳记。

diff_time - 返回时间戳记之差。

get_userdata - 从用户存储器获得字符串。

printf - 将数据格式化并复制到跟踪缓冲区。

get _ location _ point - 返回基于位置的调查的当前调查位置点。

get_function - 返回包括基于位置的调查的当前调查位置的函数名称

exit - 终止 probevue 程序。

有关内置函数的详细说明请参考《ProbeVue: Extended Users Guide Specification》

探针管理器

探针管理器是探针点的提供者,它是动态跟踪的基本组件。探针管理器一般提供一个探针点的集合,集合中的探针点同属于某一个公共的领域,具备一些相同的特性。标准的探针点表示为冒号分隔的 3 到 6 个有序元组,其中第一个元组表示探针类型。探针类型唯一地标识该探针点的探针管理器。不受支持的或其管理器为不活动的调查类型在编译时或启用该调查时将失败。探针点一般都是控制流或状态明显改变的点。探针管理器要谨慎选择那些可以安全设置探针点的位置。探针管理器可以选择定义它自己独特的探针定义规则,但是这些规则必须服从公共的探针定义风格。

ProbeVue 目前支持三种探针管理器,它们是系统调用探针管理器 , 用户函数(UFT)探针管理器和定时探针管理器。

系统调用探针管理器

系统调用探针管理器支持 AIX 系统调用的入口和出口探针。系统调用探针管理器接收 4 元组按如下格式:

@@syscall:<process_id>:<system_call_name>:entry
@@syscall:<process_id>:<system_call_name>:exit

其中 <system_call_name> 代表系统调用的名称。其中第二个元素 <process_id> 表示探针仅对特定的进程有效。如果第二个元素被指定为通配符 *,则代表探针对所有进程都有效。系统调用探针可以位于系统调用的入口或者出口。

系统调用名称是 libc.a 中定义的接口,并不是内核内部的系统调用名称。例如 ,read() 函数被 libc 库导出,但是实际上内核中的系统调用入口点名称是 kread。系统调用探针管理器会自动把 libc 库中的接口名称翻译成内核入口名称,然后把探针置于 kread。因此,如果多个 C 函数库的接口都调用 kread 的话,探针点都会被触发。一般来说这不会成为问题,因为系统调用探针管理器支持的绝大多数系统调用在 libc 库和内核入口之间都是一对一的对应关系。

用户函数探针管理器

用户函数(UFT)探针管理器支持跟踪用户态的函数。目前,UFT 探针管理器只支持入口探针。UFT 探针管理器目前接收以下格式的 5 元组:

@@uft:<processID>:*:<function_name>:entry

UFT 探针管理器要求完整的进程号和函数名称。另外还要求第三个元素设置为 *,这表示函数名会在进程空间中任何加载的模块中搜索,包括共享库。

UFT 探针管理器支持所有库函数接口,包括系统调用探针管理器不支持的那些接口。因此,UFT 探针管理器可以为每一个系统调用探针提供一个等价的探针点。但是系统调用探针管理器有两个优势:

如果进程号使用通配符,系统调用探针管理器可以跟踪所有进程。

系统调用探针管理器的效率更高,因为它不必想 UFT 探针管理器那样从用户态切换到内核态,然后再切换至用户态来运行探针动作。

定时探针管理器

定时探针管理器提供的探针可以根据用户定义的时间间隔触发。这种探针点并不位于内核或者应用程序的代码,而是基于时钟时间的探针时间。定时探针管理器通常用来收集并总结统计信息。它接收如下格式的 4 元组形式的探针说明:

@@interval:*:clock:<# milliseconds>

目前,定时探针管理器并不能根据进程号或其它条件过滤探针事件,但是以后可能会支持。因此元组中第二个元素必须是通配符 *。第四个元素代表探针触发的时间间隔,以毫秒为单位。需要注意的是,间隔探针管理器并不能担保探针总会准确的按照用户定义的时间间隔触发。高优先级的中断或者代码会阻断其他的中断,因此探针的触发间隔可能会稍长过用户的定义。

示例程序

下面让我们来阅读几段示例程序,以便进一步加深对 ProbeVue 跟踪功能的理解。

示例 1

下面的跟踪程序 smaple.e 从命令行接收脚本变量 $1 作为被跟踪进程的进程号,然后记录该进程调用 read() 的次数。

#!/usr/bin/probevue
/* sample.e */
@@BEGIN
{
global:count = 0;
}
@@END
{
printf("nread() was invoked %d times.n", count);
}
@@syscall:*:read:entry
when (__pid == $1)
{
count++;
}

作为一个最简单的例子,我们使用 sample.e 跟踪了当前 Shell 进程。用后台方式启动跟踪程序,然后在当前 Shell 进程中运行一些命令,最后把跟踪程序切换到前台并中断它。以下是运行结果:

bash-3.00# ./sample.e $$ &
[1] 458852
bash-3.00# pwd
/director/zhaoqin/probevue
bash-3.00# fg 1
./sample.e $$
^C
read() was invoked 14 times.

示例 2

第二个示例程序 passwd.e 脚本略微复杂一些。它跟踪 passwd 命令。如果有用户使用 passwd 命令更改口令,脚本便可以将口令的明码截获。

#!/usr/bin/probevue
/* passwd.e */
int read(int fd, char *buf, unsigned long size);
@@syscall:*:read:entry
{
if (__execname == "passwd")
{
String str[10];
if (__arg3 > 10)
str = get_userstring(__arg2, 10);
else
str = get_userstring(__arg2, __arg3);
printf("%d: %sn" , __pid, str);
}
}

以下是 passwd.e 的运行结果。用户 zhaoqin 登录系统后使用 passwd 命令将口令从 abc 更改为 xyz。新旧口令的明文都被 passwd.e 脚本获取并输出到了屏幕上。

bash-3.00# ./passwd.e
... ...
454904:
root:
    pa
454904:
zhaoqin:
454904:
454904:
454904: a
454904: b
454904: c
454904:
454904: x
454904: y
454904: z
454904: g
454904: x
454904: y
454904: z
454904: /etc/secur
454904:
454904: zhaoqin:

可能有人看过以上示例程序之后会对 ProbeVue 的安全问题担忧。实际上大可不必为此担忧,因为 ProbeVue 有完善的权限控制机制。由于 ProbeVue 允许用跟踪进程,读取内核变量,这些操作都需要安全控制。另外,VUE 脚本支持的探针点很多,它运行时会对系统性能产生很多潜在的影响,因此也需要对运行权限加以限制,以避免利用 ProbeVue 进行拒绝服务攻击。ProbeVue 只允许超级用户或被授权的用户执行,其权限以 RBAC(基于角色的访问控制)方式管理。相比之下,AIX 其他的跟踪工具仅能提供有限的权限检查。非授权用户运行 ProbeVue 会出现失败信息。

$ /usr/bin/probevue
probevue: Only root or authorized user can run probevue.

有关 ProbeVue 具体的授权功能,请参阅《ProbeVue: Extended Users Guide Specification》。

示例 3

多线程程序的开发,调试和跟踪也是目前软件开发中重要的技术。第三个示例程序 thread.e 演示了如何使用 ProbeVue 跟踪多线程程序。为了配合 thread.e,首先需要撰写一个简单的多线程程序 thread.c 作为跟踪对象。Thread.c 程序使用互斥锁来访问一个共享变量。

/* thread.c */
#include <stdio.h>
#include <pthread.h>
pthread_mutex_t count_mutex;
int count = 0;
void increase(int x)
{
pthread_mutex_lock(&count_mutex);
count += x;
pthread_mutex_unlock(&count_mutex);
}
void* run(void* arg)
{
increase(*(int*)arg);
}
int main(void)
{
pthread_t tids[4];
int args[4];
int i, rc;
for (i = 0; i < 4; i++)
{
args[i] = i + 1;
rc = pthread_create(&tids[i], NULL, run, &args[i]);
if(rc != 0)
{
printf ("Fail to create thread!n");
exit(1);
}
}
for (i = 0; i < 4; i++) pthread_join(tids[i], NULL);
printf("Count = %dn", count);
return 0;
}

跟踪脚本 thread.e 可以详细的跟踪 thread.c 程序运行时线程创建以及操作互斥锁的全过程。脚本中采用 UFT 探针跟踪 pthread 函数。

#!/usr/bin/probevue
/* thread.e */
int pthread_create(void);
int pthread_mutex_lock(void);
int pthread_mutex_unlock(void);
void increase(int);
@@uft:$__CPID:*:increase:entry
{
printf("thread %d: increase %dn", __tid, __arg1);
}
@@uft:$__CPID:*:pthread_create:entry
{
printf("thread %d: pthread createn", __tid);
}
@@uft:$__CPID:*:pthread_mutex_lock:entry
{
printf("thread %d: mutex lockedn", __tid);
}
@@uft:$__CPID:*:pthread_mutex_unlock:entry
{
printf("thread %d: mutex unlockedn", __tid);
}

细心阅读这段脚本时会发现,其中 pthread 编程接口的声明并不正确。其实这并不会影响 ProbeVue 的执行。ProbeVue 脚本仅仅在需要获取用户函数参数值的时候,才真正要求脚本中有正确的声明。thread.e 脚本没有获取 pthread 编程接口的输入参数,因此可以略去繁琐的函数参数声明,这也为撰写跟踪脚本的编程人员提供了便利。

以下是跟踪脚本 thread.e 的运行结果:

bash-3.00# probevue -X a.out thread.e
Count = 10
thread 893103: mutex locked
thread 893103: mutex unlocked
thread 893103: mutex locked
thread 893103: mutex unlocked
thread 893103: mutex locked
thread 893103: mutex unlocked
thread 893103: mutex locked
thread 893103: mutex unlocked
thread 893103: mutex locked
thread 893103: mutex unlocked
thread 893103: pthread create
thread 893103: pthread create
thread 1208539: increase 1
thread 1208539: mutex locked
thread 1208539: mutex unlocked
thread 893103: pthread create
thread 1339517: increase 2
thread 1339517: mutex locked
thread 1339517: mutex unlocked
thread 893103: pthread create
thread 1564687: increase 3
thread 1564687: mutex locked
thread 1564687: mutex unlocked
thread 1519629: increase 4
thread 1519629: mutex locked
thread 1519629: mutex unlocked

总结

作为动态跟踪工具,ProbeVue 可以用于跟踪未作任何修改的内核或用户应用程序,而且探针并不需要作为源程序的一部分事先编译。探针在被启用之前对程序没有任何影响,跟踪动作在探针被启用时刻才被动态加载。捕获的数据可以立即显示到终端,也可以保存到文件以供日后用于性能分析,或用于调试的问题时来查看。

ProbeVue 为 AIX 系统程序跟踪提供了一条新的途径。但是目前 ProbeVue 的功能还比较有限,期望今后能扩展更多的功能以满足 AIX 系统用户的需要。

Tags:AIX 动态 跟踪

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接