By samwan on 三月 21, 2007

通过上一次的介绍,相信大家对DTRACE已经有了一个初步的认识。上一次结束时专门留了一个例子,可能大家第一次看有很多不明白的地方,没有关系,随着我们对DTRACE更多的介绍,很快就会”云开雾散“了。

D语言作为一种编程语言,自然就有其语法、关键字、数据结构、运算符、函数等,我将一一介绍。

D语言中标志符名称与C语言类似,由字母、数字和下划线组成,其中第一个字符必须是字母或者下划线。D语言预留了一些关键字供DTRACE本身使用,关键字不能用做D变量的名称。D关键字列表参阅《Solaris动态跟踪指南》,这里只列出一些常用的。

表1 - 常用DTRACE关键字

关键字  描述
inline  编译期间将指定的D变量替换为预定义的值或者表达式,inline可以申明类型
sizeof  计算对象的大小
self  表示将D变量存放在线程(thread)的私有空间里
this  表示D变量的有效范围在this所在的子句内

D语言中定义了整数类型和浮点类型,以及用于表示ASCII字符串的string类型。整数类型随机器字长的不同而不同。机器字长可以用命令isainfo -b来查看。

表2 - D整数类型

 类型名称 32位机器字长
64位机器字长
 char 1个字节   1个字节
 short 2个字节  2个字节
 int 4个字节  4个字节
 long 4个字节  8个字节
 longlong 8个字节  8个字节

一点小知识,C语言中有ILP32和LP64两种数据模型,ILP32指的就是int/long/pointer(指针)是32位,LP64指的是long/pointer是64位。

对于整数类型,又分为带符号(signed)和无符号(unsigned)两种,因为是否带符号决定了对其最高位(most-significant)的解释。无符号整型通常是在相应的整型前面添加unsigned或者u限定符。

表3 - D整数类型别名

 类型名称 说明 
 int8_t / uint8_t 1字节带符号整数 / 1字节无符号整数
 int16_t / uint16_t 2字节带符号整数 / 2字节无符号整数
 int32_t / uint32_t 4字节带符号整数 / 4字节无符号整数
 int64_t / uint64_t 8字节带符号整数 / 8字节无符号整数
 intptr_t / uintptr_t 大小等于指针的带符号整数 / 大小等于指针的无符号整数

D语言中也定义了转义序列如'\\n'表示回车。

D语言中定义了算术运算符、关系运算符、逻辑运算符、按位运算符、赋值运算符、递增和递减运算符、条件表达式(即 ? : 运算符),由于这些运算符及其优先级与C语言基本相同,就不在这里占用篇幅了。D语言也支持”强制类型转换“,即把一种类型转换为另一种兼容类型,比如将指针转换为整数。

除了上面的数据类型,D语言还提供有数组(array)关联数组(associative array),关联数组中有一种特殊类型叫做聚合(aggregation),在后面会看到。关联数组通过一个称为键(key)的名称来检索数据,用过Perl的朋友相信不会陌生。定义关联数组,只需作以下赋值操作即可:

name[key]=expression;

例如: people["sam.wan",30]=100

D语言中的变量是不需要预定义就可以直接使用的。但是在没有赋值之前,是不能出现在谓词中和赋值运算等号右侧。请看下面的3个例子:

例子1
# dtrace -n 'BEGIN{a=1;exit(0);}END{printf("a=%d\\n",a);}'
dtrace: description 'BEGIN' matched 2 probes
CPU     ID                    FUNCTION:NAME
  0      1                           :BEGIN
  0      2                             :END a=1

例子2
# dtrace -n 'BEGIN/a==0/{exit(0);}END{printf("a=%d\\n",a);}'
dtrace: invalid probe specifier BEGIN/a==0/{exit(0);}END{printf("a=%d\\n",a);}: in predicate: failed to resolve a: Unknown variable name

例子3
# dtrace -n 'BEGIN{a=a+1;exit(0);}END{printf("a=%d\\n",a);}'
dtrace: invalid probe specifier BEGIN{a=a+1;exit(0);}END{printf("a=%d\\n",a);}: in action list: a has not yet been declared or assigned

缺省情况下,D语言中定义的变量是全局范围的。在多线程环境中,全局变量是不安全的,因为可能多个线程都会进行访问,因此,D语言引入了线程局部变量标志符self。通过在一个变量前面添加self->修饰,可以将该变量存放在线程自己的局部空间中,这样不会受到其它线程的影响,这种方法对于现在越来越多的并发操作环境十分有利。线程局部变量与全局变量在不同的名称空间(name space)中,因此即使名字相同也不会冲突,比如self->aaa和aaa是两个不同的变量。

特别需要提醒注意的是,在使用完一个变量之后,要将该标量赋值为'0',这样DTRACE就会回收释放其所占用的内存空间。对用一个好的程序员来说,释放空间和分配空间同样重要。

D语言中还有一种特殊的变量叫“子句局部变量(Clause Local)”,通过在变量名前添加this->修饰符完成。子句局部变量的作用域只在其定义的子句内有效。D语言中的变量缺省情况下会被赋值为0,但是子句局部变量除外。

除用户定义的变量外,D语言本身提供了一些非常有用的内置变量,所有这些内置变量都是全局变量。

表4 - DTrace内置变量

 类型和名称 说明 
int64_t arg0,...,arg9  探测器的前10个输入参数(64位整数)。如果当前探测器参数个数少于10,则未定义的参数值不确定 
args[] 与arg0...arg9不同,args[]是有类型的,其类型对应与当前探测器的参数类型。
uintptr_t caller 进入当前探测器之前的当前线程的程序计数器(PC)位置
chipid_t chip 当前物理芯片的CPU芯片标志符
processorid_t cpu 当前CPU的编号
cpuinfo_t \*curcpu 当前CPU的信息(具体结构后面会讲到)
lwpsinfo_t \*curlwpsinfo 与当前线程关联的轻量进程(LightWeight Process,LWP)的信息(具体结构见后)
psinfo_t \*curpsinfo 与当前线程关联的进程的信息
kthread_t \*curthread 当前线程在内核中的数据结构(kthread_t)的地址,kthread_t的定义在<sys/thread.h>中。
string cwd 当前进程的工作路径(Current Working Directory)
uint_t epid 当前探测器的已启用的探测器ID号。
int errno 当前线程最后一次执行的系统调用的返回错误值
string execname 当前进程的名称
gid_t gid 组ID
uint_t id 当前探测器的唯一ID号,dtrace -l的第一列
uint_t ipl 触发探测器时当前CPU的中断优先级(Interrupt Priority Level,IPL)。
lgrp_id_t lgrp 当前CPU所属的延迟组(Latency Group)的ID
pid_t pid 当前进程号
pid_t ppid 当前进程的父进程
string probefunc 当前探测器的函数名
string probemod 当前探测器的模块名
string probename 当前探测器的名字
string probeprov 当前探测器的提供器名
psetid_t pset 当前CPU所属的处理器集(Processor Set)的ID
string root 当前进程的根目录名
uint_t stackdepth 当前线程的栈帧(Stack Frame)的深度。即其调用的函数的层次数。
id_t tid 当前线程的线程ID
uint64_t timestamp 以纳秒(ns)为单位的时间计数器。此计数器从过去的任意点递增,仅用于相对计算中。
uid_t uid 当前进程的实际用户ID
uint64_t uregs[] 当前线程的用户寄存器值
uint64_t vtimestamp 以纳秒(ns)为单位的时间计数器,实际是当前线程在CPU中已运行的时间减去DTrace谓词和操作所花费的时间。同timestamp一样,仅用于相对计算。
uint64_t walltimestamp 自1970年1月1日00:00世界标准时间以来的纳秒数。

Dtrace还可以使用反引号(backquote `)访问操作系统内核中定义的变量,但不能进行修改。

内核中定义的变量可以通过查看内核的变量符号表(Name Symbol)来找到。

#echo "::nm"|mdb -k|more

比如我们想通过DTrace来查看每秒钟freemem的值。freemem表示当前系统中可用的内存页数

#dtrace -qn 'tick-1sec{printf("%d pages of freemem\\n",`freemem)}'

由于Solaris可用动态加载模块,各个模块可能有相同的变量名,为了避免冲突,可用使用模块名来区分,比如访问a模块的x变量:a`x

DTrace还提供操作和子例程。

如果DTrace子句为空,则会采用缺省操作。缺省操作即显示已启用的探测器的标志符。

数据记录操作

数据记录操作总会往指定的缓冲区中放入数据。 
      void trace(expression) 将expression的结果放到指定的缓冲区(在后面会提到)。

void tracemem(address,size_t nbytes),从address地址复制nbytes的内容到指定缓冲区。

void printf(string format,...) 格式化输出。具体格式可用参见printf(3C)手册页。

void printa(aggregation)/void printa(string format,aggregation) 显示及格式化聚合(在后面会提到)

void stack(void)/void stack(int nframes) 将指定长度的栈帧记录拷贝到指定的缓冲区。

void ustack(int nframes,int size)/void ustack(int nframes)/void ustack(void),同上,只是操作的是用户栈

破坏性操作

void stop(void) 停止触发当前探测器的进程

void raise(int signal) 将指定的信号signal发送至触发当前探测器的进程

void copyout(void \*buf,uintptr_t addr,size_t nbytes) 从buf地址拷贝nbytes字节到当前进程的addr地址处。

void copyoutstr(string str,uintptr_t addr,size_t maxlen) 将字符串string拷贝到当前进程的addr地址处

void system(string program,..) 执行程序

内核破坏性操作(下面的操作将会影响整个系统的运行)

void breakpoint(void) 发生一个内核断点

void panic(void) 触发panic()操作(这个相信大家都再熟悉不过了)

void chill(int nanoseconds) DTrace执行nanoseconds时间的spin操作(循环),如果nanoseconds> 500milliseconds,则会失败。

特殊操作

推测性操作(Speculative Actions),有speculate(),commit(),discard(),在后面会提到。

void exit(int status) 立即停止DTrace跟踪。

子例程

与操作不同,子例程只会影响DTrace的内部状态。

void \*alloca(size_t size)  分配size字节的临时空间,返回一个8字节对齐的指针。

string basename(char \*str)  从str中去除前缀和目录名

void bcopy(void \*src,void \*dest,sizt_t size) 从src拷贝size字节到dest。

string cleanpath(char \*str) 去除str中的/./和/../等

void \*copyin(uintptr_t addr,size_t size) 从用户地址空间addr处拷贝size字节到Dtrace临时缓冲区中,并返回缓冲区地址。

string \*copyinstr(uintptr_t addr) 从用户地址空间addr除拷贝已null结尾的ASCII字符串到Dtrace临时缓冲区,并返回缓冲区地址。

void copyinto(uintptr_t addr,size_t size,void \*dest) 从用户地址空间addr处拷贝size字节到Dtrace临时缓冲区的dest处。

string dirname(char \*str) 返回str的目录名

size_t msgdsize(mblk_t \*mp) 返回mp指向的数据消息的字节数

size_t msgsize(mblk_t \*mp) 返回mp消息字节数

int mutex_owned(kmutex_t \*mutex) 如果当前线程拥有互斥锁mutex,则返回非零;否则返回0

kthread_t \*mutex_owner(kmutex_t \*mutex) 返回mutex互斥锁的属主的线程数据结构kthread_t的指针。如果没有属主或者该互斥锁是自旋锁(Spin Mutex),则返回null。

int mutex_type_adaptive(kmutex_t \*mutex) 如果mutex是自适应互斥锁(MUTEX_ADAPTIVE类型),则返回非0,否则返回0。

int progenyof(pid_t pid) 如果触发当前探测器的进程是指定进程的子孙,则返回非0

int rand(void) 返回一个伪随机整数

int rw_iswriter(krwlock_t \*rwlock) 如果指定的读写锁rwlock被一个写入者占有或者要求获得,则返回非0,否则返回0

int rw_write_held(krwlock_t \*rwlock) 如果指定的读写锁当前被一个写入者占有,则返回非0,否则返回0

int speculation(void) 为speculate()操作预留一个推测性跟踪缓冲区,并返回这个缓冲区的标志符。

string strjoin(char \*str1,char \*str2) 串联str1和str2到临时空间,并返回其地址。

size_t strlen(string str) 返回指定字串的长度(不包括结尾的空字节null)

看了这么多操作和子例程,是不是有点受打击了?没有关系,慢慢来,先熟悉一下,在今后具体使用时,就会印象深刻。

关于前面的copyin/copyinstr/copyinto子例程再多作一点说明:

在Solaris(UNIX)系统中,用户程序是运行在用户地址空间里面,当用户程序执行系统调用比如open(2)时,才会进入到内核空间执行(我们通常称之为"陷入trap",为了访问用户地址空间的字符串,就必须将其拷贝到内核空间里面来,否则内核找不到相应的地址,就会报错。看下面的一个例子。

我们想知道是什么程序在调用open(2),以及打开什么文件。这里很自然我们会用到syscall提供器的open:entry探测器。此探测器的参数可用从open(2)的手册页查到(所有的syscall提供器提供的探测器都可用在相对应的系统调用手册中查到)

int open(const char \*path, int oflag, /\* mode_t mode \*/);

我们只关心第一个参数arg0,这是一个字符串指针(即将要打开的文件名)。对于字符串指针,可以使用"%s"进行格式化输出。

#!/usr/sbin/dtrace -qs
syscall::open:entry,
syscall::open64:entry
{
     printf("%s[%d] opened %s\\n",execname,pid,arg0);
}

运行一下看看


 # ./who_open_what.d
dtrace: failed to compile script ./who_open_what.d: line 5: printf( ) argument #4 is incompatible with conversion #3 prototype:
        conversion: %s
         prototype: char [] or string (or use stringof)
          argument: int64_t


错误,为什么,因为传递给内核的是一个用户地址空间的指针,内核无法访问该地址,内核只看到一个指针,因此Dtrace认为格式化用的是"%s",但是传递的却是一个int64_t类型,不匹配。

正确的程序应该是:

#!/usr/sbin/dtrace -qs
syscall::open:entry,
syscall::open64:entry
{
     printf("%s[%d] opened %s\\n",execname,pid,copyinstr(arg0));
}

再来看看


# ./who_open_what.d
nfsmapid[272] opened /etc/default/nfs
nfsmapid[272] opened /etc/resolv.conf
init[1] opened /etc/inittab
init[1] opened /etc/svc/volatile/init-next.state
init[1] opened /etc/svc/volatile/init-next.state
init[1] opened /etc/inittab
...

DTRACE简介(2)的更多相关文章

  1. DTRACE简介之完结篇3

    https://blogs.oracle.com/swan/entry/dtrace%E7%AE%80%E4%BB%8B_3 DTRACE简介之完结篇 By samwan on 四月 13, 2007 ...

  2. DTRACE简介(1)

    https://blogs.oracle.com/swan/entry/dtrace%E7%AE%80%E4%BB%8B By samwan on 三月 20, 2007 记得几年前看过一部美国大片叫 ...

  3. Linux 下的一个全新的性能测量和调式诊断工具 Systemtap, 第 2 部分: DTrace

    DTrace的原理本系列文章详细地介绍了一个 Linux 下的全新的调式.诊断和性能测量工具 Systemtap 和它所依赖的基础 kprobe 以及促使开发该工具的先驱 DTrace 并给出实际使用 ...

  4. 【转】ftrace 简介

    ftrace 简介 ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析. 最早 ftrace 是一个 function tracer,仅能够记录内核的函数 ...

  5. ftrace 简介【转】

    转自:http://www.ibm.com/developerworks/cn/linux/l-cn-ftrace/index.html Trace 对于软件的维护和性能分析至关重要,ftrace 是 ...

  6. ftrace 简介

    ftrace 简介 ftrace 的作用是帮助开发人员了解 Linux 内核的运行时行为,以便进行故障调试或性能分析. 最早 ftrace 是一个 function tracer,仅能够记录内核的函数 ...

  7. Apple SIP简介及在Clover中如何控制

    Apple SIP简介及在Clover中如何控制 来源 http://www.yekki.me/apple-sip-overview-and-how-to-disable-it-in-clover/ ...

  8. ASP.NET Core 1.1 简介

    ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...

  9. MVVM模式和在WPF中的实现(一)MVVM模式简介

    MVVM模式解析和在WPF中的实现(一) MVVM模式简介 系列目录: MVVM模式解析和在WPF中的实现(一)MVVM模式简介 MVVM模式解析和在WPF中的实现(二)数据绑定 MVVM模式解析和在 ...

随机推荐

  1. Windows 下 IIS与Apache 共存

    在Windows服务器下, 安装了IIS以及Apache服务器, 如何使他们一起工作. 目前我面对的问题是, 只有一个IP地址,要通过不同的端口来访问不同的程序. 解决方案如下: 1.找到 Apach ...

  2. RegisterClientScriptBlock和RegisterStartupScript的区别

    RegisterClientScriptBlock在 Page 对象的 元素的开始标记后立即发出客户端脚本,RegisterStartupScript则是在Page 对象的 元素的结束标记之前发出该脚 ...

  3. Node.js——fs常用API

    文件状态 文件删除 文件信息 读取文件夹 文件的截取 创建文件夹 删除目录 文件监视,可以设置监视频率 文件重命名,可以用来剪切文件 注意 fs.open() fs.close() 这是最原始的读写方 ...

  4. ButterKnife 在父类 点击事件没反应的解决方案

    在用继承的方式实现butterKnife的封装的时候遇到问题, butterKnife就在baseActivity中绑定的,但是父类中公共控件点击事件无效.找了半天原因,原来是子类和父类定义的点击方法 ...

  5. sysbench下载安装

    涉及到sysbench源码的配置和编译,首先确认系统安装了gcc gcc-c++编译器:确认安装了autoconf .automake.libtool等:[root@PC download]# rpm ...

  6. 服务器设置禁ping

    //设置Linux服务器禁ping!!!终端命令行直接输入 echo 1 >/proc/sys/net/ipv4/icmp_echo_ignore_all 这个是关闭ping的命令. 如果你想要 ...

  7. php生成订单号-当天从1开始自增

    /** * 生成订单号 * -当天从1开始自增 * -订单号模样:20190604000001 * @param Client $redis * @param $key * @param $back: ...

  8. JavaSE-20 IO序列化

    学习要点 定义 IO如何序列化 序列化 序列化:是将对象的状态存储到特定存储介质中的过程. 反序列化:从特定存储介质中的数据重新构建对象的过程. 实现了java.io.Serializable接口的类 ...

  9. php基础排序算法

    1.冒泡排序 $arr = array(12,34,57,42,165.4,73,51); function bubbling_sort($array) { $cou = count($array); ...

  10. python使用zipfile解压文件中文乱码问题

    中文在编程中真实后娘养的,各种坑爹,python3下中文乱码这个问题抓破了头皮,头疼.看了alex的文章,才有种恍然大悟的感觉(链接在底部). 一句话,就是转换成unicode,压缩前是什么编码,使用 ...