您的位置:首页 > 其它

Solaris上使用DTrace进行动态跟踪

2012-04-26 16:28 148 查看
Solaris内置的Dynamic Tracing (DTrace) 功能提供一个动态的跟踪环境。

DTrace is a comprehensive dynamic tracing framework for the Solaris Operating System. DTrace provides a powerful infrastructure that permits administrators, developers, and service personnel to concisely answer arbitrary questions about the behavior of the
operating system and user programs.

与 truss 和相似的工具不同,可以使用 DTrace 检查正在运行的程序的内部情况,而不只是查看系统调用。DTrace 可以显示应用程序的函数名、它所依赖的任何库以及调用的内核函数。这把跟踪的方便性提高到与调试器差不多的水平。但是,与调试器不同,不能修改值,也不能暂停或以其他方式改变应用程序的执行过程。只能跟踪执行过程,而不能控制它。

DTrace 的另一个独特之处是可以编写跟踪脚本,称为“D”的脚本语言,使用D语言,很容易编写动态启动probe、收集信息及处理信息的脚本。D脚本使用户可以方便地同其他人员共享知识和故障检修方法。Solaris 10中包含大量有用的D脚本,在Sun公司的BigAdmin 站点:sun.com/bigadmin/content/dtrace/和OpenSolaris项目站点:opensolaris.org/os/community/ dtrace/上可以找到更多脚本。

DTrace 会在应用程序中添加检测机制,从而识别不同的执行点。这些执行点称为探测(probe),包括在内核、库和程序中定义的探测。内核、库和用户程序中的所有函数都可以指定为探测。另外,可以使用静态定义的探测识别感兴趣的特殊执行点。例如,在内核中可以使用探测识别向磁盘写数据的执行点。开发人员可以在程序中添加特定的探测,从而允许用户启用跟踪。这些探测称为 User-land Statically Defined Tracing(USDT)。

Probe就像遍布于Solaris系统中各个感兴趣位置的可编程传感器。

可以使用 dtrace 工具获得探测列表;-l 命令行选项列出系统中定义的所有探测:$ dtrace -l。例如在我用的solaris上,有超过5万个probe点。

-bash-3.00# dtrace -l | wc -l

51927

Probe

Probe的定义如下:

Description

/Predicate/

{

Action

}

Description使用四个字段:provider:module:function:name。

provider: 指定要使用的实现方法。例如,syscall provider用以监控系统调用,而io provider用以监督磁盘IO。

module: 模块。

function: 函数,可以使用*和?通配符。

name: 通常代表函数中的位置。

例如:

1) 监视系统调用

dtrace -n 'syscall:::entry' -o trace.log

*输入ctrl+c中止。

*-o file选项把trace结果输出到文件而不是stdout。

2) 监视系统调用open

dtrace -n 'syscall::open:entry' -o trace.log

*输入ctrl+c中止。

3) 监视函数输入参数

监视程序mytest.exe中函数add的输入参数

*-c指定command,可以使用-p pid来attach到一个进程.

-bash-3.00$ more mytest.cc
#include <iostream>

int add(int i1, int i2) {
return (i1 + i2);
}

int main(int argc, char** argv) {
printf("1+2=%d\n", add(1,2));
return 0;
}
-bash-3.00$ CC mytest.cc -o mytest.exe
-bash-3.00$
-bash-3.00# dtrace -n 'pid$target:mytest.exe:*add*:entry /execname=="mytest.exe"/ { printf("%d %d", arg0, arg1) }' -o trace.log -c './mytest.exe'
dtrace: description 'pid$target:mytest.exe:*add*:entry ' matched 1 probe
1+2=3
dtrace: pid 19991 has exited
-bash-3.00# more trace.log
CPU     ID                    FUNCTION:NAME
0  51942            __1cDadd6Fii_i_:entry 1 2


4)编写脚本

-bash-3.00# more mytest.trace.sh
#!/usr/sbin/dtrace -s

#pragma D option quiet

pid$target:mytest.exe:*add*:entry
{
printf("Add(%d,%d)",arg0,arg1);
}
-bash-3.00#
-bash-3.00# ./mytest.trace.sh -o trace.log -c ./mytest.exe
1+2=3
-bash-3.00# more trace.log
Add(1,2)
-bash-3.00#


5) 聚合函数

聚合(Aggregation)具有如下构造函数:

@table_name[index1[,index2...]] =aggregate_function()

例如:

@table[probefunc]=count() ;

@table[probemod,probefunc]=count();

probemod, probefunc是模块名,函数名的内置变量。

聚合函数count()追踪函数被调用的次数。

其他常用的聚合函数有min、max、average、sum等。

*-q选项只输出D脚本中的trace(),printf()函数等的输出,不会为每个probe输出一行跟踪结果。

-bash-3.00# dtrace -n 'pid$target::strcpy:entry { @table[probemod,probefunc]=count(); }' -q -c './mytest.exe'
1+2=3

libc.so.1           strcpy             1
LM1`ld.so.1         strcpy             4


6) 记录执行时间

*为了让结果更明显,修改mytest.cc在add()函数中加上sleep(1),重新编译mytest.exe;

*可以使用self变量来保存记录。向self变量添加的任何数据都被设置为线程局部数据,适用于多线程程序。

*如果是attach到一个进程进行监控,有可能entry probe在attach之前就已经执行,在return probe加上的predicate可以处理这种情况。

-bash-3.00$ more mytest.cc
#include <iostream>
#include <unistd.h>int add(int i1, int i2) {
sleep(1);
return (i1 + i2);
}
int main(int argc, char** argv) {
for (int i=0; i<6; ++i) {
printf("1+2=%d\n", add(1,2));
}
return 0;
}
-bash-3.00$ CC mytest.cc -o mytest.exe
-bash-3.00$

-bash-3.00# more mytest.trace.sh
#!/usr/sbin/dtrace -s

#pragma D option quiet

pid$target:mytest.exe:*add*:entry
{
self->start[probefunc]=timestamp;
}

pid$target::printf:entry
{
self->start[probefunc]=timestamp;
}

pid$target:::return
/self->start[probefunc]!=0/
{
@elapsed[probefunc]=sum((timestamp-self->start[probefunc])/1000000);
self->start[probefunc]=0;
}
-bash-3.00# ./mytest.trace.sh -q -c ./mytest.exe
1+2=3
1+2=3
1+2=3
1+2=3
1+2=3
1+2=3

printf                                                            1
__1cDadd6Fii_i_                                                6000
-bash-3.00#


7) C++程序

C++程序编译以后生成的符号名字和代码中是不一样的。不过可以使用nm命令找到对应关系。例如:

-bash-3.00$ more math.h
class Math {
public:
int add(int i1, int i2);
};
-bash-3.00$ more math.cc
#include "math.h"

int Math::add(int i1, int i2) {
return (i1 + i2);
}
-bash-3.00$ more mytest.cc
#include <iostream>
#include "math.h"

int main(int argc, char** argv) {
Math m;
printf("1+2=%d\n", m.add(1,2));
return 0;
}
-bash-3.00$ CC mytest.cc math.cc -o mytest.exe
mytest.cc:
math.cc:
-bash-3.00$ /usr/ccs/bin/nm -C mytest.exe | grep Math
[59]    |     69296|      40|FUNC |GLOB |0    |7      |int Math::add(int,int)
[__1cEMathDadd6Mii_i_]
-bash-3.00$

这样就找到了Math::add()对应的名字是__1cEMathDadd6Mii_i_,现在可以使用这个名字来监控,

-bash-3.00# dtrace -n 'pid$target::__1cEMathDadd6Mii_i_:entry { @table[probemod,probefunc]=count(); }' -q -c './mytest.exe'
1+2=3

mytest.exe      __1cEMathDadd6Mii_i_        1
-bash-3.00#


9)一些例子

dtrace -n 'proc:::exec-success {printf("cmd=%s,pid=%d,uid=%d\n",execname,pid,uid);}'

监视系统中成功启动的进程名字,pid,uid

dtrace -q -n 'proc:::signal-send /args[2]==SIGKILL/ {printf("%d sent to %s uid=%d\n",args[2],args[1]->pr_fname,uid);}'

监视接收到SIGKILL的进程。(pr_fname是接收进程的psinfo_t结构的成员)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: