Chinaunix首页 | 论坛 | 博客
  • 博客访问: 120195
  • 博文数量: 24
  • 博客积分: 32
  • 博客等级: 民兵
  • 技术积分: 109
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-28 16:23
文章分类
文章存档

2014年(6)

2013年(8)

2012年(6)

2011年(4)

分类:

2014-05-20 17:30:28

本文旨在帮助新用户学习如何使用DTraceSun Solaris 10系统中收集及使用系统和应用程序信息。文中介绍了创建D脚本的方法,并使大家明白,每位系统管理员和开发人员都应清楚何时要学习、使用DTrace。书中提供了一些例子,这将帮助读者开始使用DTrace

读完本文之后,读者将能够创建脚本,以收集运行中应用程序的有用信息,这些信息也可用以提升Solaris的性能。

DTrace介绍

DTraceSun Solaris内置的全面动态追踪工具,可供管理员和开发人员检查用户程序和操作系统自身的行为。您可利用DTrace浏览系统,以了解它的工作方式、追踪在软件的许多层面中存在的性能问题,或者探明异常行为的原因。在软件开发系统中使用该工具是安全的,且不必重启系统或应用程序。

DTrace动态地修改操作系统内核和用户进程,以记录特别位置的数据,这称为probes)。probe就是位置或活动,DTrace可将其与请求绑定在一起,以执行一组活动,如记录堆栈追踪、时间戳或函数的实参。probe就像遍布于Solaris系统中各个感兴趣位置的可编程传感器。DTrace probe来自一组称为“provider”的内核模块,每个模块都执行某种特殊方法的来创建probe

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

D脚本介绍

首先,对D脚本的构建进行简单讨论。D脚本由probe描述、predicateaction组成,如下所示:

prode description

/predicate/

{

actions

}

在执行D脚本时,在probe description中描述的probe就被启动了。当probe被激活且predicate计算为真时,就会执行动作语句。可将probe当作事件:当事件发生时,就激活probe。下面是一个简单的D脚本例子:

syscall::write:entry

/execname = = “bash”/

{

printf(“bash with pid %d called write system calln”,pid);

}

此处probe描述就是syscall::write:输入,它描述了写系统调用。predicate就是/ execname = = “bash”/。这将确认调用写系统调用的执行语句是否为bash shellprintf语句就是bash每次调用写系统调用时执行的动作。

probe描述

现在进一步了解probe描述。probe是使用四个字段描述的:providermodulefunctionname

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

l modulefunction——描述要观察的模块和函数。

l name——通常代表函数中的位置。例如,在输入函数时,使用名称输入实现功能。请注意,可以使用*?通配符。空字段被解释为通配符(表1就是一些例子)。

probe描述

解释

syscall::open: entry

open系统调用中的输入

syscall::open*: entry

任何系统调用中以openopenopen64)开头的输入

syscall::: entry

任何系统中被调用的输入

syscall:::

系统调用提供者发布的所有probe

1probe解释的例子

predicate语句

predicate可以是任何D表达式(表2演示了一些例子)。只要当predicate计算为真时,才执行动作。

predicate

解释

cpu == 0

如果在cpu0上执行probe时为真

pid == 1029

如果激活probe的进程pid1029,则为真

execname !=“ sched”

如进程未被调度 sched),则为真

ppid!= 0 && arg0 == 0

如果父进程id不为0,且第一个实参为0,则为真

2predicate的例子

action语句

action部分可以包含一系列以分号(;)隔开的动作命令。表3提供了一些例子。

action

解释

printf()

使用C格式的printf()命令打印

ustack()

打印用户级堆栈

trace

打印指定变量

3action的例子

请注意,predicateaction语句是可选的。如果predicate不存在,则总会执行action。如果action不存在,则打印被激活的probe的名称。

利用最少的信息就可开发有用的D脚本。在本文的下一部分,将专门从应用程序开发和系统管理两个视角介绍DTrace的使用方法。

DTrace开发人员指引

使用下列provider,可以获得应用程序开发人员感兴趣的信息类型:syscallprocpidsdt vminfo。开发人员可以使用这些provider查看运行中进程,进程的创建和终止、LWP的创建和终止以及信号处理——本部分只介绍pid provider

pid provider

pid provider从运行中进程的任何用户级函数中使用输入和返回。您可使用pid provider追踪系统上任何进程中的任何指令。pid provider的名称包括您感兴趣的正在运行的进程的pid。表4提供了一些使用pid providerprobe描述示例。

例子

解释

pid2439:libc:malloc: entry

libc中进程id2439malloc函数的输入

pid1234:a.out:main: return

从进程id1234的主函数返回

pid1234:a.out:: entry

进程id1234的主程序中任何函数的输入

pid1234::: entry

任何库中进程id1234的任何函数的输入

4pid provider及相关Probe的例子

您可运行下列命令,以打印进程id1234调用的所有函数:

# dtrace –n pid1234:::entry

如下所示,同样的命令也可用在脚本中:

#!/usr/sbin/dtrace –s

/* The above line means that the script that follows needs to be

Interpreted using dtrace. D uses C – style comments.

*/

pid1234:::entry

{}

通过用进程pid替换1234,您使用该命令或脚本。

下列步骤可用于使用pid provider创建D脚本:

1. 现在试运行上述命令和脚本。您可看到,这并不容易配置。

2. 修改脚本,使用进程id作为形参。脚本将变成这样:

#!/usr/sbin/dtrace –s

pid$1:::entry

{}

3. 现在可以将进程id作为实参提供给脚本。请注意,如果不能给脚本提供一个且是惟一的实参,DTrace将提示错误,且无法执行。请记着给脚本添加执行权。

您可能已注意到,该脚本的输出太快,因此尚没有实际用处。D语言拥有一个优良的称为“Aggregation(聚合)”的构造函数,它用以收集内存中的所有详细信息,并打印摘要。聚合使您能够收集内存中的信息表。聚合具有如下构造函数:

@name[table indes(es)] =aggregate_function()

例如:

@count_table[probefunc] = count() ;

该聚合将收集信息,并将其归入以函数名命名的表中(probefunc是一个含有函数名的内置变量)。聚合函数count()追踪函数被调用的次数。其他常用的聚合函数有averageminmaxsum

4. 下例将在您正在开发的脚本中添加聚合函数,以查看用户函数的摘要表:

#!/usr/sbin/dtrace –s

pid$1:::entry

{

@count_table[probeunc] = count() ;

}

该脚本将收集信息,然后将其归入表中,并且将持续运行,直至按下^ cControl c键)。一旦脚本停止,DTrace将打印信息表。请注意,您不必编写任何代码来打印该表。DTrace将自动替您完成该动作。

您可能已注意到,probe并不是在运行该脚本时动态创建的。只要脚本一停止,probes就被禁用。您不必编写任何特殊代码以禁用这些probe。此外,DTrace将自动清理它分配的任何内存。您不必编写任何清理代码。

5. 通过修改toprobemodprobefunc索引,可以方便地修改该脚本,以收集含有函数名和库名的表。

#!/usr/sbin/dtrace –s

pid$1:::entry

{

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

}

6. 之后,查看每个函数耗费了多长时间。这可通过内置的时间戳变量而实现。您可以在输入中或函数的返回中创建probe,并根据从输入到返回的时间戳差异,计算耗费的时间。时间戳变量以纳秒为单位报告时间。以下就是修改后的脚本:

#!/usr/sbin/dtrace –s

pid$1:::entry

{

ts[probefunc] = timestamp;

}

pid$1:::return

{

@func_time[probefunc] = sum(timestamp – ts[probefunc]);

ts[probefunc] = 0;

}

注意,ts[]是数组,D已经自动替您声明并初始化了该数组。将变量置为0,这是节省空间的好方法。

7. 大多数情况该脚本都可正常运行。但是有一些边界异常。

l 异常情况1:监控函数输入和返回

由于活动中的正在运行的应用程序里创建probe,因此在运行D脚本时,程序很有可能正在执行函数。因此,虽然没有输入数据,但您仍可能看到函数的返回。通过向返回probe部分中添加/ts[ probefunc] != 0/predicate,即可轻松解决此问题。该predicate将使您能够忽略上述边界情况。

l 异常情况2:多线程应用程序

第二个边界情况较为棘手,它涉及多线程应用程序。此时有可能造成竞争状态,即两个线程可能同时执行同一函数。在这种情况下,每个线程都需要ts[]的一个副本。DTrace通过self变量解决此问题。向self变量添加的任何数据都被设置为线程局部数据。

8.下列脚本已被修改,以处理这两种边界情况:

#!/usr/sbin/dtrace –s

pid$!:::entry

{

self - >ts[probefunc] = timestamp;

}

pid$1:::return

/self - >ts[probefunc]/

{

@func_time[probefunc] = sum(timestamp – ts[probefunc]);

ts[probefunc] = 0;

}

注意,/self->ts[ probefunc]/等同于/self->ts[ probefunc] != 0/

对于大型应用程序而言,上述脚本将启用大量的probe(可能多达几十万个)。即使每个probe都是相当轻量的,但向活动中的正在运行的系统添加如此多的probe,将对应用程序的性能造成不良影响。

9. 通过修改probe描述,可以限制启用的probe数量。请参考表5中的例子。

probe描述

解释

pid$1:libc:: entry

只限制指定的库

pid$1:a.out:: entry

只限制非库函数的probe

pid$1:libc:printf: entry

只限制一个函数的probe

5:修改probe描述

10. 之后,您将看到如何监控进程开始到终止的整个过程。DTrace允许您利用e$ target变量和-c选项做到这一点。该脚本将统计指定应用程序调用libc函数的次数。

#!/usr/sbin/dtrace –s

pid$target:libc::entry

{

@[probefunc] = count();

}

11. 将该脚本存为libc_func.d,并运行该脚本。

# libc_fun.d –c “cat /etc/hosts”

12. 您可方便地用感兴趣的命令替换“cat/etc/ hosts”。

其他有用的脚本

还有一些示例脚本,可用以获取系统的附加信息。了解系统调用的次数、堆栈深度以及了解谁在运行什么,这将有助于您了解工作负荷,并发现可能存在的瓶颈。

1. 下列脚本将在系统进行写系统调用时,查找堆栈追踪。注意,您需要使用-c选项运行该脚本。

#!/usr/sbin/dtrace –s

syscall::write:entry

{

@[ustack()] = count();

}

2. 下列脚本统计在CPU中各个进程的运行次数。注意,当CPU运行时间切换到进程时,将激活sysinfo::: pswitch probe。需要几分钟后按下^ cControl c)键。

#!/usr/sbin/dtrace –s

sysinfo:::pswitch

{

@[execname] = count();

}

3. 下列脚本在系统启动新进程时,将打印进程名称、piduid。注意,当新进程成功启动时,将激活proc:::exec- success

#!/usr/sbin/dtrace –qs

proc:::exec-success

{

printf(“%s(pid=%d) started by uid - %d n”,execname,pid,uid);

}

DTrace系统管理员指引

每个系统管理员都会面对的任务,是那些预定义环境中运行的应用程序的行为或错误行为。这类信息可通过下列provider获得:syscallprocioschedsysinfovminfolockstatprofile。在这些provider中,syscallprociosched都是最容易使用的。使用这些provider,系统管理员可获得进程、线程、堆栈状态以及许多其他内核指标的有关信息。从syscallprociosched provider开始介绍较为容易。

syscall provider

这可能是要学习和使用的最重要的provider,因为系统调用是用户级应用程序和内核之间的主要通信渠道。通过了解其他信息中正在使用哪些系统调用,可建立系统利用指标,并识别可能的错误行为。使用syscall provider,可以很容易地识别谁在执行什么,以及某个操作耗费了多长时间,这有助于您识别系统错误行为的根本原因。

1. 使用下列脚本,可以在激活probe时列举probe每次出现的信息,且当进入正在执行close(2)系统调用的系统中时,提供有关系统调用的信息:

# dtrace –n syscall::close:entry

2. 要开始识别向特定进程发送了kill(2)信号的进程,可以使用下列脚本:

#!/usr/sbin/dtrace –s

syscall::kill:entry

{

trace(pid);

trace(execname);

}

3. 要确定webserverread(2)上耗费的时间,可以使用下列脚本。注意,可以方便地修改该脚本,以将其用于其他进程。

#!/usr/sbin/dtrace –qs

BEGIN

{

printf(“sizettimen”);

}

syscall::read:return

/self - >start/

{

printf(“%dt%dn”,arg0 ,timestamp – self ->start);

self ->start = 0;

}

proc provider

provider在进程和线程创建、终止以及发送信号时被激活。它是一种较为复杂的方法,用以注销probe,并将告诉您哪位用户向哪个进程发送了指定信号(3head)。

1. 追踪向当前在系统中运行的所有进程发送的所有信号。

#!/usr/sbin/dtrace –wqs

proc:::signal – send

{

printf(“%d was sent to %s by ”,args[2],args[1]->pr_fname);

system(“getent passwd %d | cut –d: -f5”,uid);

}

2. 在脚本中添加条件语句(/args[ 2] == SIGKILL/),并向来自不同用户的不同进程发送SIGKILL信号。

#!/usr/sbin/dtrace –wqs

proc:::signal –send

/args[2 ] = = SIGKILL/

{

printf(“SIGKILL war sent to %s by ”,args[1]->pr_fname);

system(“getent passwd %d | cut –d: -f5”,uid);

}

这里您可看到引入了pr_fname,它是接收进程的psinfo_t结构的一部分。

sched provider

provider动态地追踪调度事件。使用该provider,可以了解线程何时及为何睡眠、运行、修改优先权或者唤醒其他线程。

下列脚本确定CPU耗费在I/O等待和工作上的时间。它还中断I/O进程,并指明在I/O等待时间内,数据由StarOffice检索。可以方便地修改该脚本,以适应您的特殊情形。

#!/usr/bin/dtrace –sq

sched:::on – cpu

/execname = = “soffice.bin”/

{

self->on = vtimestamp;

}

sched:::off –cpu

/self->on/

{

@time[“”] = sum(vtimestamp – self->on);

self->on = 0

}

io provider

provider监督磁盘输入和输出(I/O)子系统。使用io provider,您可深入了解ofiostat(1M)输出。io probe在所有的磁盘请求时都可被激活,包括NFS服务,但元数据请求除外。

下例基于/usr/demo/dtrace/iosnoop.d脚本。使用该脚本可追踪哪个设备上有哪些文件正在被访问,并可确定正在执行的任务是否为读操作或写操作。

#!/usr/bin/dtrace –qs

BEGIN

{

Printf(“%10s %58s %2sn”,”DEVICE”,”FILE”,”RW”);

}

io:::start

{

printf(“%10s %58s %2sn”,args[1]->dev_statname,

args[2]->fi_pathname,args[0]->b_flags & B_READ ? “R” : “W”);

}

阅读(1755) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~