https://blogs.oracle.com/swan/entry/dtrace%E7%AE%80%E4%BB%8B

By samwan on 三月 20, 2007

记得几年前看过一部美国大片叫《全民公敌(Enemy of the State)》,在里面,谋杀国会议员的主谋强沃特和他的属下,为了取回记录着其犯罪事实的磁碟片,用高科技的卫星监视,使主人公史密斯的行踪处于严密的监控中。当时就对美国高科技跟踪系统惊叹不已。当然作为一个普通公民,是不希望自己受到监视的。但是对于计算机系统,如果能够对系统的运行情况进行监视并了如指掌,进而发现其中的臭虫(bug),那将是一件令IT管理者和开发者兴奋的事。今天我要介绍的SolarisTM Dtrace就是这样一个好帮手!

我的第一篇Blog就提到了Dtrace,但是没有作更多的说明。今天我将对Dtrace作比较详细的介绍,一是作为自己学习Dtrace的一点心得,二是希望对还没有使用Dtrace的朋友们提供一点入门知识,更详细的信息请参阅第一篇Blog中提到的资源。为了与中文版的《Solaris动态跟踪指南》保持一致,下面的术语都采用书中的翻译。
      DTRACE(全称Dynamic Tracing)是SolarisTM 10中引入的一种可以对核心(kernel)和应用程序(user application)进行动态跟踪并且对系统运行不构成任何危险的技术。下面是理解Dtrace的几个要点:

1. Dtrace的实现是紧密地结合到核心里的(intimately integrated),即Dtrace的源代码是分布到了Kernel的各个部分中。除了Dtrace的执行程序dtrace.c和头文件<sys/dtrace.h>,<sys/dtrace_impl.h>外,其它实现dtrace的代码遍布到Solaris Source tree的各个文件。具体请参见 Bryan Cantrill的Blog - "The Observation Deck"

2. Dtrace架构中一个很重要的组件是"探测器(Probe)",简单讲,探测器就是核心源代码中某一个特点的”“。在普通的Solaris 10内核中,这样的”点“有4万多个,而且还可以随着模块的加载而增加。探测器在没有被”启用(enable)“时,对核心是没有任何影响的,这时的核心与没有dtrace功能的核心如Solaris 8/9是没有任何区别的。当探测器被启用后,Solaris会动态地往核心中为启用的探测器加入相应的指令来实现探测器被"触发(fire)"时的“操作(action)"。

3. Dtrace架构可以简单的理解为”Dtrace提供器(Provider)和Dtrace使用者(Consumer)”模式。如下图所示:

”提供器“提供了”探测器“,而”使用者“通过libdtrace(3LIB)库和相应的设备文件或者其它方式来使用”提供器“提供的”探测器"。如上图所示,除了我们下面将会介绍的/usr/sbin/dtrace命令外,Solaris 10系统中还有很多收集统计信息的工具比如intrstat(1M),plockstat(1M),lockstat(1M)等都是Dtrace使用者。使用plockstat -V -p <pid>,你就可以看到plockstat使用的dtrace命令。
    4. Dtrace本身是安全的,即不会对内核的运行造成影响。Dtrace可以读取内核变量,却不能修改内核变量。但是Dtrace提供了”破坏性(destructive)"的操作比如panic(),如果你使用了这些动作,是会中断系统运行的。

在学习Dtrace的过程中,要切记上面的几点。

下面就重点介绍一下Dtrace中日常使用最频繁的一个Dtrace使用者/usr/sbin/dtrace命令。dtrace(1M)可以以命令行形式调用,也可以通过D-script调用。D-script是用Dtrace提供的D语言来编写的脚本程序。D语言类似于C和awk,但是没有程序控制如for,if等机制,也许是为了更好的控制系统的稳定性。

命令行调用的例子:  dtrace -n 'syscall::open\*:entry{trace(execname)}'

D-script例子:

#!/usr/sbin/dtrace -s
syscall::open:entry,
syscall::open64:entry
{
    trace(execname);
}                                    

不管是命令行方式还是脚本方式,都要指定至少一个探测器。每个探测器都是一个“四元组(4-tuple)",但是有的部分可以省略。探测器的具体格式如下:

Provider:Module:Function:Name

各部分的含义如下:

Provider即提供器,发布此探测器的Dtrace提供器的名称。比如:syscall是所有系统调用的提供器,sysinfo是系统统计信息的提供器,proc是进程信息的提供器。不同系统不同版本的Solaris的提供器的数量不同。使用下面的命令可以查看系统中有多少个提供器.

#dtrace -l|grep -v "PROVIDER"|awk '{print $2}'|sort -u

Module即模块,是此探测器对应于特定的程序位置时,其所在模块的名称。对于应用程序,模块名可以是动态链接库的名字,比如:libc,或者主程序a.out。有的探测器没有模块名。

Function即函数,探测器所在函数的名称

Name即名字,最后一个组成部分。

探测器的四元组名字如果某个部分为空,则表示匹配该字段的所有可能性,星号(\*)也是通配符,表示匹配任意字符串。现在我们再来看上面的两个例子。第一个命令行例子表示启用syscall提供器中所有模块里面名字以open开头的函数的entry探测器;而第二个脚本例子表示匹配syscall提供器中所有模块里面名字是open或者open64函数的entry探测器,其中的逗号表示或者的关系。命令行方式调用时,如果不使用-l开关,则指定的探测器将被启用,对于脚本方式,-s后面即D-script程序的正文部分。

一个D程序的结构如下:

0      #!/usr/sbin/dtrace -s
1      pragma D option quiet
2      probe_description_1 
3      / predicate_1 /
4     {
5           action_1;
6           action_2;
7             ...
8            action_n;
9      }
10      probe_description_2
11      / predicate_2 /
12     {
13           action_1;
14           action_2;
15             ...
16            action_n;
17      }
... 
18      probe_description_n
19      / predicate_n /
20     {
21           action_1;
22           action_2;
23             ...
24            action_n;
25      }

上面的伪代码(pseudo-code)描述了一个D程序的大致结构,其中除了探测器描述部分,其它的部分如谓词、操作都不是必须的。第0行指明D程序的解释器(interpreter),就是/usr/sbin/dtrace;第1行使用pragma关键字指定特定的D程序编译指令;从第2行起就是对相应的探测器的启用,并定义在指定的探测器被触发时应该执行的操作,操作以分号结尾。其中,在探测器描述和操作之间用 / / 符号隔开的部分称为"谓词(Predicate)"。前面已经提到,在D语言中,没有if语句和循环,只有通过谓词来进行判断,谓词是一系列的逻辑运算,如果计算结果是false(0),则忽略探测器的触发,当然更不会执行该探测器定义的任何操作;只有当谓词计算为true(非0)时,相应的操作才会被执行。D程序的执行是从上至下顺序执行的,花括号{}包围的部分是对应探测器被触发且谓词为真时的执行子句块,对于同一个探测器描述,可以指定多个执行子句块。

当你编辑完成一个D程序,并且使用dtrace -s或者通过直接添加执行权限来执行时,Dtrace首先会将你的脚本程序编译成一个安全的中间格式(有点类似于Java程序的运行机制),然后才会被加载到内核中执行。Dtrace的执行环境还会检查并处理运行时错误(run-time errors)比如被零除(dividing by zero),访问无效地址等。因此Dtrace是相当安全的。

当Dtrace程序被加载到内核执行时,相应的探测点被启用,如果有涉及探测点的事件发生,我们就把它称之为“触发”,如果此时谓词计算为true,则相应的操作就会被执行。为便于大家理解“启用”和“触发”两个概念,我们举一个日常生活中的实际例子。

现在全国各个城市为了更好地规范交通秩序,都安装了很多“电子警察”(就是“探测器”),安装完成就打开(即“启用”),如果有车闯红灯,就会激活安装在地上的感应线(”触发“),那么”电子警察“就会拍照,很快罚单就会送到你家里(这就是”操作“)。

通过上面这个例子,大家应该有个更加形象的认识了吧。

作为今天的结束,下面是一个监视谁(用户ID)使用什么命令访问一个文件(文件以参数形式传递)的例子。

who_access_thisfile.d


#!/usr/sbin/dtrace -qs
syscall::creat\*:return,
syscall::open\*:return
/arg0 != -1 && fds[arg0].fi_pathname == $1 /
{
        printf("uid#%d %s %s\\n",uid,execname,$1);
}


chmod +x who_access_thisfile.d,然后执行./who_access_thisfile.d /etc/passwd,在另一个终端上试试cat /etc/passwd, vi /etc/passwd,看看你都看到了什么信息,你原来能做到吗?

更多的信息,将在下一次中介绍。

最新文章

  1. ajax和json对象
  2. ECMAScript 6 入门
  3. CF 161B Discounts(贪心)
  4. hdu1875 畅通工程再续 最小生成树并查集解决---kruskal
  5. Eclipse 中的重构功能
  6. C 不改变顺序,原址剔除数组中的0元素
  7. PHP-Fcgi下PHP的执行时间设置方法
  8. linux -时间
  9. Android Activity的切换动画
  10. nginx配置文件的说明
  11. 增强SEO的div+css命名规则
  12. linux终端下文件不同颜色的含义
  13. 第一百三十一节,JavaScript,封装库--CSS
  14. 设计模式(二) 策略模式Strategy
  15. HTML中在a标签中添加onclick事件
  16. [NOIp 2012]同余方程
  17. Apache Hadoop 2.9.2 的归档案例剖析
  18. CSS效果:跑马灯按钮
  19. codeforces 1065F Up and Down the Tree
  20. oracle数据库导出与导入

热门文章

  1. Spark学习之在集群上运行Spark(6)
  2. Entity Framework 6.x介绍
  3. cuda输出
  4. 为什么java String是固定的 为什么字符串是不可变的
  5. 【VScode】使用VScode 来写markdown时序图
  6. Hadoop架构模型
  7. sqllite相关总结
  8. 关于C/C++的一些思考(1)
  9. js 技巧 (一)
  10. shell脚本语言基本命令