IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  AIX and UNIX  >

Probevue - AIX 6 新的程序员工具

检查和调试动态应用程序(live applications)

developerWorks
文档选项

未显示需要 JavaScript 的文档选项


级别: 中级

Nigel Griffiths, pSeries Technical Support, IBM

2007 年 11 月 26 日

本文向您介绍了 IBM 最新的操作系统-AIX 6 中提供的一个新的程序调试工具,它能帮助您能够更好地对您的系统进行运行前或运行时的调试,同时它的一个特点就是它是针对于动态应用程序(live applications)发布的。通过这个工具可以更好地对您的程序进行调试去以更好的运行它。

AIX 从 1990 年就开始提供 Trace 功能

  • 在 AIX 内核中预先构建了挂钩(代码),并且这些挂钩大部分用于捕获内核函数的状态和参数
  • 专为生产使用而设计,但需要注意,其收集信息量过多,可能会降低性能

AIX 6 ProbeVue——包含在当前 AIX 6 Open Beta 版本中,但有功能限制

  • 需要时也可动态添加用户代码
  • 零代码修改
  • 一旦启用,将触发与 C 语言类似的 Vue
  • 专为生产使用而设计,而且几乎没有性能影响
  • 不会转储所有信息
  • 使用脚本确定真正需要的内容或查找错误条件
以上说明基于当前 Open Beta 版本之后的 AIX6 中的 probevue。 如果使用以下示例 probevue 脚本出现较大问题,则可能有必要期待 AIX 6 完整版本的推出。

ProbeVue——五种启动方法

可以采用交互方式运行 probevue(对我而言,这工作似乎有点艰难,因为必须键入和重复键入细节!),也可以通过脚本运行 probevue。至少可以采用五种方法,可能开始会让人有些迷惑,那么接下来我们就对各个方法进行说明,以便您决定尝试哪种方法:

  • 1) 和 2) 使用脚本启动 probevue,并将脚本指定为输入参数,或将脚本重定向到命令中,如下所示(这是两种方法):
        probevue myscript.e
        probevue <myscript.e
        

  • 3) 自动启动脚本,并在第一行使用特殊的代码告知 shell 启动 probevue 来处理文件内容
        脚本(在本例中称为 myscript)包括第一行: #!/usr/bin/probevue
        将文件处理为可执行文件:chmod +x myscript
        然后运行脚本:./myscript.e
        

  • 4) 有些脚本需要参数(如 shell 脚本 $1、$2 等)。具体来说,很多需要进程 ID (PID)
        查找进程 ID:# ps -ef | grep find
        使用 PID 参数运行: probevue   myscript   43561
    	

  • 5) 直接通过用户程序使用 probevue
        probevue -X progname -A prog-arguments myscript
    	

Vue 脚本——基础知识

probevuew 脚本的结构如下(<-- 是注释,即不属于脚本的一部分)

<- optional declaring function prot-type so probevue understands your functionarguments
@@BEGIN
{
...                 <- optional start up code, good for initialisation of variables
}

@@<probe-spec>        <- Probe point specification Tuple (see below)
when <predicate>        <- Optional predicate
{
    statements here;	<- Probe actions in C like code
...
}

@@END
{
...                              <- Optional ending code.  Can print out final 
                                 results. Gets runs if you hit Control+C
}

探点(Probe point )规范“Tuple”

首先请注意,Tuple 是有序列表的雅名——我在知道这一点后帮助非常大!
目前有三种类型的 Tuple:

  • 1) 针对用户编写的代码的入口或出口的用户函数跟踪探针(或 uft)
    • 语法: @@uft:PID:*:FunctionName:[entry/exit]
    • 示例:针对函数 foo()(主要可执行文件或库)的入口的探针,PID = 34568
      • @@uft:34568:*:foo:entry
      • 注意“*”是用于下一版本的占位符
  • 2) 系统调用入口/出口探针(或 syscall)
    • 语法: @@syscall:PID:SystemCallName:[entry/exit]
    • 示例:读取系统调用所有进程的出口的探针
      • @@syscall:*:read:exit
  • 3) 间隔探针(以指定的时间间隔触发)
    • 语法: @@interval:*:clock:milliseconds
    • 示例:每 500 毫秒(墙上时钟时间)触发一次的探针
      • @@interval:*:clock:500
在将来的版本中将包含更多的 Tuple。

第一个示例 probevue 脚本

以下是一个非常简单的脚本,此脚本实际上工作并执行一些有意义的工作。

#!/usr/bin/probevue                <-- autorun
@@BEGIN
{
int count, total;                       <-- declare the variables (later probevue 
                                                   versions will not need this)
}

@@syscall:*:read:entry            <-- System call: any process ID, read() function, 
                                                    at start of the function
{
   count++;                             <-- increment the counters
   total++;
}

@@interval:*:clock:1000          <-- once per second (1000 ticks per second)
{
   printf("Number of reads = %d\n", count);
   count=0;
}

@@END                                   <-- After you type Control-C
{
    printf("\nTotal reads = %d\n",total);
}


以下是一些示例输出:
# ./count.e
Number of reads = 0
Number of reads = 1
Number of reads = 0
Number of reads = 0
Number of reads = 2
Number of reads = 0
Number of reads = 5
Number of reads = 269
Number of reads = 0
Number of reads = 0
^C
Total reads = 277

第二个示例包含从函数返回的代码

#!/usr/bin/probevue
@@BEGIN
{
int read(int fd, void *buf, int n);       <-- Declare function so ProbeVue understands it
int bad;
int good;
}
@@syscall:*:read:exit
when(__rv == -1)                         <-- __rv means the return value
{
   bad++;
}
@@syscall:*:read:exit
when(__rv != -1)
{
   good++;
}
@@interval:*:clock:1000                  <-- Once a second output counter
{
   printf("Reads good=%d bad=%d\n",good,bad);
   good=0;
}

以下是上面脚本的一些示例输出,我专门运行了一个程序来生成一些磁盘 I/O。注意:当程序尝试从无效的文件描述符(即未使用 open() 函数初始化文件描述符)进行读取时,将生成不良 I/O:

# ./goodandbad.e
Reads good=0 bad=0
Reads good=55 bad=0
Reads good=0 bad=0
Reads good=0 bad=0
Reads good=57 bad=1
Reads good=0 bad=1
Reads good=0 bad=1
Reads good=55 bad=1
Reads good=0 bad=1
Reads good=1 bad=1
Reads good=55 bad=1
Reads good=40 bad=1
Reads good=1 bad=2
Reads good=55 bad=3
Reads good=1 bad=3
Reads good=0 bad=3
Reads good=56 bad=4

第三个示例监视用户程序

我使用一个小程序生成了很多 CUP 工作。此程序调用了 ncpu,其中包含函数“engine()”,此函数耗尽了 CPU 上的 CPU 时钟周期。以下脚本用于研究此函数的使用情况。注意:此程序已经编译并在运行,并且它包括符号表,probevue 使用此符号表来查找函数入口和出口点(即未剥离)。probevue 脚本包括用于输出所能找到的程序和环境的详细信息的 probevue 函数:

#!/usr/bin/probevue

double engine(int p1, int p2);

@@uft:$1:*:engine:entry
{
     printf("PID=%d TID=%d PPID=%d PGID=%d UID=%d GID=%d InKernel=%d\n", __pid, 
     __tid, __ppid, __pgid, __uid, __euid, __kernelmode);
      printf("ProgName=%s errno=%d\n", __pname, __errno);
       printf("---\n");
      stktrace(GET_USER_TRACE,-1);
      printf("+++\n");
      stktrace(PRINT_SYMBOLS|GET_USER_TRACE,-1);
}

以下是用于运行此脚本的命令及其输出:
# ps -ef | grep ncpu
    root 299106 307250  27 14:02:00  pts/0 15:16 ./ncpu -p1 -z 90
    root 315596 307250   0 16:48:47  pts/0  0:00 grep ncpu
#  ./engine    299106
PID=299106 TID=630919 PPID=307250 PGID=299106 UID=0 GID=0 InKernel=0 
ProgName=ncpu errno=0
---
0x100001dc
0x10000964
0x100010e8
0xd02fd66c
0x20000c80
0x10001160
+++
.__start+0x8c
.main+0x60c
.work+0x1b4
.random+0x2c

第四个示例检查用户程序函数的参数

接下来让我们研究一下名为 engine() 的函数。该函数接受两个参数,第一个是随机生成的 1 到 1,000,000 之间的数字——但我们不确定具体是不是在此范围内。我们将随机数放入四个“桶”中。我们要确定各个桶中的随机数的数量是否大体相等——不是完全相等,但至少相近:

#!/usr/bin/probevue

double engine(int p1, int p2);
int b1, b2, b3, b4;

@@uft:$1:*:engine:entry
{
        tmp = __arg1;
        if( tmp < 250000 ) b1++;
        if( 250000 <= tmp && tmp < 500000 ) b2++;
        if( 500000 <= tmp && tmp < 750000 ) b3++;
        if( tmp  >750000 ) b4++;
}

@@END
{
printf("\nFour bucket results are: %d %d %d %d\n",b1,b2,b3,b4);
}

以下是上述脚本的对应命令和输出:
ps -ef | grep ncpu
    root 299106 307250  27 14:02:00  pts/0 15:16 ./ncpu -p1 -z 90
    root 315596 307250   0 16:48:47  pts/0  0:00 grep ncpu
#  # ./random 299106
[[[Wait 30 seconds here]]]
^C
Four bucket results are: 101524 101262 101454 120657

噢,最后一个桶里中的数量更多一些,但这不是 probevue 的问题!这是我代码中的一个错误。我后来通过一个更为详细的 probevue 脚本发现,有些值大于 1,000,000!Probevue 将实际值添加到“生产”工作负载,而没有中断应用程序,也不对程序进行重新编码/重新编译/重新启动。我想不出其他能够做到这一点的方法。

关于 probevue 的这个早期测试版的最后一点看法

我并没有讨论更为高级的功能,如:

  • 试验性捕获——缓存结果,并稍后在找到要研究的点后提交/丢弃数字
  • 从用户程序读取数据
  • 线程处理
  • 用于更改缺省行为的 probevctrl 命令
对于 AIX 6.1,这是初始版本,我还得知可能会提供扩展用户指南——将是当前手册页的改进。
第一个 AIX6 版本有以下方面值得我们注意:
  • 内核变量仅限于内存中驻留的变量
  • 用户 C 代码函数入口需要符号表
  • 没有 C 预处理器支持
  • WPAR 限制——使用 probevue 时,您的 WPAR 不具有移动性(不能重新加载)。停止 probevue 后,WPAR 就可再次移动了。另外, syscall tuple 必须具有 PID,可从 WPAR 查看 PID(不允许使用“*”)
  • 不支持线程局部字符串
计划在 2008 版中推出更多功能。



参考资料

学习

讨论


关于作者

Nigel Griffiths 是 IBM eServer pSeries Technical Support Advanced Technology Group 的成员。他是一位性能、规模调整、基准测试和 Oracle RDBMS 方面的专家。您可以通过 nag@uk.ibm.com 与 Nigel 联系。




对本文的评价










回页首


IBM 和 AIX 是 IBM Corporation 在美国和 / 或其他国家 / 地区的商标。 UNIX 是 The Open Group 在美国和其他国家 / 地区的注册商标。 Linux 是 Linus Torvalds 在美国和其他国家 / 地区的注册商标。 其他公司、产品或服务名称可能为各自所有者的商标或服务标志。 其他公司、产品或服务的名称可能是其他公司的商标或服务标志。

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款