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. iOS programming UITabBarController

    iOS programming UITabBarController 1.1 View controllers become more interesting when the user's acti ...

  2. iOS---UICollectionView自定义流布局实现瀑布流效果

    自定义布局,实现瀑布流效果 自定义流水布局,继承UICollectionViewLayout 实现一下方法 // 每次布局之前的准备 - (void)prepareLayout; // 返回所有的尺寸 ...

  3. CHECKPOINT - 强制一个事务日志检查点

    SYNOPSIS CHECKPOINT DESCRIPTION 描述 预写式日志(Write-Ahead Logging (WAL))缺省时在事务日志中每隔一段时间放一个检查点. (要调整这个原子化的 ...

  4. ALTER DATABASE 修改一个数据库

    SYNOPSIS ALTER DATABASE name SET parameter { TO | = } { value | DEFAULT } ALTER DATABASE name RESET ...

  5. java网络编程_IP地址

    InetAddress类,此类表示Internet协议(IP)地址.具体使用方法查看文档:https://docs.oracle.com/en/java/javase/11/docs/api/java ...

  6. Java IO(一)--File类

    File类不是单指文件,它既可以代表一个文件名称,又可以代表一个目录下的一组文件.可以用来创建.删除.遍历文件等 public static void main(String[] args) { St ...

  7. jQuery中Ajax事件beforesend及各参数含义1

    jQuery中Ajax事件beforesend及各参数含义 转自:http://blog.sina.com.cn/s/blog_609f9fdd0100wprz.html Ajax会触发很多事件. 有 ...

  8. EZOJ 宝石迷阵 建图+网络流匹配

    题面: 封印恶魔的地方可以看作是一个 n*m 的矩形,包含了 n*m 个祭坛,并且其 中有一些祭坛已经损坏了. 如果 i+j 为偶数,那么第 i 行第 j 列的祭坛只要没有损坏,就一定会封印有一个恶魔 ...

  9. HYSBZ - 2763 飞行路线(分层图最短路线)

    题目: Alice和Bob现在要乘飞机旅行,他们选择了一家相对便宜的航空公司.该航空公司一共在n个城市设有业务,设这些城市分别标记为0到n-1,一共有m种航线,每种航线连接两个城市,并且航线有一定的价 ...

  10. SpringBoot 全局处理以及注入请求参数

    后端接口,经常会用token获取对应的账号信息.于是考虑将这个步骤封装起来. 之前项目使用ThreadLocal去做这样的事情,但昨天看SpringBoot的官方文档,发现借助框架的功能也可以做这样的 ...