作者:Richard Friedman
简要介绍适用于 Oracle Linux 的 DTrace 探测器和提供程序,以及与 Oracle Solaris 中 DTrace 探测器和提供程序的区别。还介绍了 DTrace 命令和脚本编写。
2013 年 6 月发布
DTrace 是一个全面的动态跟踪工具,最初是为 Oracle Solaris 操作系统开发的,现在 Oracle Linux 客户也可以使用。DTrace 旨在提供运营洞察力,允许用户实时动态调整和排除操作系统和应用程序故障。DTrace 让 Oracle Linux 开发人员能够在整个软件体系中分析性能和追溯性能问题。DTrace 有助于提高应用程序开发质量、减少停机时间、降低成本和提高现有资源的利用率。Oracle Linux 支持计划的客户可从Unbreakable Linux Network 下载使用。
|
DTrace 入门
DTrace 是一个全面的动态跟踪工具,最初是为 Oracle Solaris 操作系统开发的,后来移植到 Oracle Linux。DTrace 使您能够了解运行中的系统并跨多层软件体系追溯性能问题。
在 Oracle Linux 上使用 DTrace 的一些优点是您可以:
- 观察整个软件体系、操作系统内核、系统库和应用程序的动态运行时性能。
- 通过在运行时定义实时探测点识别性能瓶颈。
- 开发探测器在谓词控制下触发时执行的脚本。
- 检测并报告内存访问错误,而不是听任系统崩溃。
DTrace 针对 Oracle Linux Unbreakable Enterprise Kernel 的移植是一项仍在不断发展的技术。其目标是为动态跟踪提供集成解决方案,最终与 Oracle Solaris 功能兼容,同时为 Oracle Linux 带来新特性。
概览
要使用 DTrace,需要指定内核中感兴趣的位置(称为探测器),其中 DTrace 可以绑定一个请求来执行一组操作,如记录堆栈跟踪、时间戳或函数参数。探测器的作用类似于深埋于操作系统中用于记录信息的可编程遥感传感器。探测器触发时,DTrace 会从其收集数据并向您回报数据。Oracle Linux DTrace 探测器运行于被称作提供程序 的 DTrace 内核模块中,提供程序执行插装以支持探测器。
DTrace 可安全用于生产系统,无需重启系统或运行任何应用程序。可以通过 dtrace
(1) 命令或 DTrace D 脚本语言调用运行时跟踪。无论采用哪种方式,都可以查询系统探测器,针对设计的任意问题提供即时、准确的答案。
例如,清单 1 显示了一个简单的命令行 dtrace
调用,指定调用 open()
系统调用的命令以及被打开的文件的名称(在控制台登录过程中):
# dtrace -q -n syscall::open:entry'{ printf("%-16s%-16s ", execname,copyinstr(arg0)); }' login /etc/ld.so.cache login /lib64/libpam.so.0 login /lib64/libpam_misc.so.0 login /lib64/libselinux.so.1 login /lib64/libaudit.so.1 login /lib64/libc.so.6 login /lib64/libdl.so.2 login /lib64/libcrypt.so.1 login /lib64/libfreebl3.so login /dev/tty1 login /etc/pam.d/login login /lib64/security/pam_securetty.so login /etc/pam.d/system-auth login /lib64/security/pam_env.so login /lib64/security/pam_fprintd.so login /etc/ld.so.cache login /usr/lib64/libdbus-glib-1.so.2 login /lib64/libdbus-1.so.3 login /lib64/libpthread.so.0 login /lib64/librt.so.1 login /lib64/libgobject-2.0.so.0 login /lib64/libglib-2.0.so.0 login /lib64/security/pam_unix.so login /etc/ld.so.cache login /lib64/libnsl.so.1 login /lib64/security/pam_succeed_if.so login /lib64/security/pam_deny.so login /lib64/security/pam_nologin.so login /etc/pam.d/system-auth login /lib64/security/pam_localuser.so login /lib64/security/pam_permit.so login /etc/pam.d/system-auth login /lib64/security/pam_cracklib.so login /etc/ld.so.cache login /usr/lib64/libcrack.so.2 login /lib64/security/pam_selinux.so login /lib64/security/pam_loginuid.so login /lib64/security/pam_console.so login /lib64/security/pam_namespace.so login /lib64/security/pam_keyinit.so login /etc/pam.d/system-auth login /lib64/security/pam_limits.so login /lib64/security/pam_ck_connector.so login /usr/lib64/tls/x86_64/libck-connector.so.0 login /usr/lib64/tls/libck-connector.so.0 login /usr/lib64/x86_64/libck-connector.so.0 login /usr/lib64/libck-connector.so.0 login /etc/pam.d/other login /etc/nsswitch.conf login /etc/ld.so.cache login /lib64/libnss_files.so.2 login /etc/passwd login /etc/securetty dbus-daemon /selinux/access dbus-daemon /proc/1464/cmdline dbus-daemon /proc/1464/cmdline dbus-daemon-lau /etc/ld.so.cache dbus-daemon-lau /lib64/libexpat.so.1 dbus-daemon-lau /lib64/libpthread.so.0 dbus-daemon-lau /lib64/librt.so.1 dbus-daemon-lau /lib64/libc.so.6 dbus-daemon-lau /etc/dbus-1/system.conf dbus-daemon-lau /etc/nsswitch.conf dbus-daemon-lau /etc/ld.so.cache dbus-daemon-lau /lib64/libnss_files.so.2 dbus-daemon-lau /etc/passwd dbus-daemon-lau /usr/share/dbus-1/system-services/net.reactivated.Fprint.service dbus-daemon-lau /etc/passwd dbus-daemon-lau /proc/sys/kernel/ngroups_max dbus-daemon-lau /etc/group ^C
清单 1
注意,这是在运行的系统上实时发生的。按 Ctrl + C 可终止跟踪。
DTrace 脚本语言 D 专为动态跟踪而设计。使用 D 脚本,可以动态启用探测器、收集数据和处理数据。D 脚本可与其他人共享,Oracle 技术网上发布了许多 D 脚本。
在清单 2 的示例中,调用 D 语言脚本 syscalls.d
显示进程 5178 正在使用的系统调用及其频率:
# cat syscalls.d #!/usr/sbin/dtrace -qs syscall:::entry /pid == $1/ { @num[probefunc] = count(); } # ./syscalls.d 5178 ^C chdir 2 clone 2 pipe 2 rt_sigreturn 2 setpgid 2 access 4 getegid 4 geteuid 4 getgid 4 getuid 4 wait4 4 sendmsg 5 socket 5 close 9 newstat 14 read 23 write 29 ioctl 40 rt_sigaction 120 rt_sigprocmask 136
清单 2
探测器和提供程序
DTrace 探测器来自一组被称作提供程序的内核模块,每个提供程序执行特定类型的插装来创建探测器。使用 DTrace 时,每个提供程序都有机会发布它可以为 DTrace 框架提供的探测器。然后,您可以对已发布的任何探测器启用和绑定跟踪操作。
要列出系统上的所有可用探测器,请键入清单 3 中所示命令。您系统上的清单可能与此不同,因为根据 Oracle Linux 平台、安装的软件和加载的提供程序模块的不同,探测器的数量也不同。
# dtrace -l ID PROVIDER MODULE FUNCTION NAME 1 dtrace BEGIN 2 dtrace END 3 dtrace ERROR 4 syscall read entry 5 syscall read return 6 syscall write entry 7 syscall write return 8 syscall open entry 9 syscall open return 10 syscall close entry 11 syscall close return 12 syscall newstat entry 13 syscall newstat return 14 syscall newfstat entry 15 syscall newfstat return 16 syscall newlstat entry 17 syscall newlstat return 18 syscall poll entry 19 syscall poll return 20 syscall lseek entry 21 syscall lseek return 22 syscall mmap entry 23 syscall mmap return 24 syscall mprotect entry 25 syscall mprotect return 26 syscall munmap entry 27 syscall munmap return 28 syscall brk entry ... #
清单 3
每个探测器都显示一个整数 ID 和一个易于理解的名称,由四部分组成,在 dtrace
输出中显示为单独的四列。
provider
— 发布此探测器的 DTrace 提供程序的名称。提供程序名称通常对应于执行插装以启用探测器的 DTrace 内核模块的名称。module
— 如果此探测器对应于特定程序位置,则为探测器所在内核模块的名称。function
— 如果此探测器对应于特定程序位置,则为探测器所在程序函数的名称。name
— 探测器名称的最后一部分,即表明探测器语义的名称。
当提及具体探测器时,这些部分一起显示,中间用冒号分隔,如:
provider:module:function:name
表 1 中所示提供程序在 Oracle Linux 上的当前 DTrace 实现中可用。
表 1
提供程序 | 内核模块 | 说明 |
---|---|---|
dtrace |
dtrace |
为 DTrace 本身提供探测器:BEGIN 、END 、ERROR ,用于可选地在跟踪开始之前初始化 DTrace、执行跟踪后处理、处理执行期间其他探测器中的意外错误。 |
io |
io |
提供与数据输入和输出有关的监视探测器。 |
proc |
proc |
为监视进程创建和终止、新程序映像执行以及发送和处理信号提供探测器。 |
profile |
profile |
提供与定时中断关联的探测器。可以使用这些探测器以固定时间间隔对系统状态进行采样。 |
sched |
sdt |
提供与 CPU 调度有关的探测器。 |
sdt |
sdt |
提供静态定义的、位于内核中多个感兴趣的重要位置的跟踪探测器。 |
syscall |
systrace |
在每个系统调用的入口点和返回点提供探测器。这些探测器对了解应用程序与底层系统之间的交互尤为有用。 |
可以在探测器描述中使用通配符(*
和 ?
),空白字段会被解释为通配符。以下是几个例子。
进入 open()
系统调用:
syscall::open:entry
进入任何 open*()
系统调用,如 open64()
:
syscall::open*:entry
进入任意系统库调用:
syscall:::entry
syscall
提供程序发布的所有调用:
syscall:::
Oracle Linux 和 Oracle Solaris 中 DTrace 之间的差异
Oracle Solaris 中的有些 DTrace 提供程序在 Oracle Linux 中尚未实现。有关提供程序的最新列表以及对 Oracle Linux 实现的任何限制,请参见 Oracle Linux 第 6 版管理员解决方案指南。
要列出一个具体提供程序发布的探测器,使用以下命令:
# dtrace -l -P provider
要验证探测器是否可用,使用以下命令:
# dtrace -l -n probe_name
dtrace
(1) 命令行
我们已经看到,许多非常有用的跟踪都可以直接从 dtrace
(1) 命令行运行。这些称为单行命令,许多都可以在相关文档和互联网上找到。
例如,清单 4 所示单行命令执行定向内核堆栈跟踪:
# dtrace -n 'gettimeofday:entry {stack()}' dtrace: description 'gettimeofday:entry' matched 1 probe CPU ID 0 196 0 196 ... FUNCTION:NAME gettimeofday:entry vmlinux`pollwake vmlinux`dtrace_stacktrace+0x30 vmlinux`__brk_limit+0x1e1832d7 vmlinux`__brk_limit+0x1e1913a1 vmlinux`pollwake vmlinux`do_gettimeofday+0x1a vmlinux`ktime_get_ts+0xad vmlinux`systrace_syscall+0xde vmlinux`audit_syscall_entry+0x1d7 vmlinux`system_call_fastpath+0x16 gettimeofday:entry vmlinux`dtrace_stacktrace+0x30 vmlinux`__brk_limit+0x1e1832d7 vmlinux`__brk_limit+0x1e1913a1 vmlinux`security_file_permission+0x8b vmlinux`systrace_syscall+0xde vmlinux`audit_syscall_entry+0x1d7 vmlinux`system_call_fastpath+0x16 ...
清单 4
Oracle Linux 第 6 版动态跟踪指南 中提供了所有 dtrace
命令选项的完整列表。下面列出几个重要的选项标志:
-c command
— 运行指定命令,完成后退出。如果指定多个-c
选项,dtrace
将在所有命令退出后退出,并在终止时报告每个子进程的退出状态。-f function
— 指定您想要跟踪或列出的函数(也可以指定提供程序和模块)。可以附加一个可选的 D 探测器子句。可以多次为命令指定-f
选项。function
还可以采用此形式:[[provider:]module:]function [[predicate]action]
。-l
— 列出探测器,而非启用探测器。dtrace
基于-f
、-i
、-m
、-n
、-P
和-s
选项的参数筛选探测器列表。如果未指定选项,dtrace
将列出所有探测器。-m module
— 指定您想要跟踪或列出的模块(也可以指定提供程序)。可以附加一个可选的 D 探测器子句。可以多次为命令指定-m
选项。module
还可以采用此形式:[[provider:]module [[predicate]action]]
。-n name
— 指定您想要跟踪或列出的探测器名称(也可以指定提供程序、模块和函数)。可以附加一个可选的 D 探测器子句。可以多次为命令指定-n
选项。name
还可以采用此形式:[[provider:]module:]function:]name [[predicate]action]]
。-p PID
— 捕获由进程 ID 指定的进程,缓存其符号表,并在完成后退出。如果指定多个-p
选项,dtrace
将在所有进程退出后退出,并在终止时报告每个子进程的退出状态。-P provider
— 指定您想要跟踪或列出的提供程序。可以附加一个可选的 D 探测器子句。可以多次为命令指定-P
选项。provider
还可以采用此形式:provider['D- probe_clause']
。-s sourcefile
— 编译指定的 D 程序源文件并开始跟踪。
谓词 是用于有条件跟踪数据的 D 语言逻辑表达式。命令行中还可以出现以斜线 (//
) 括起的谓词,D 语句的动作列表放在括号 ({ }
) 中。(下一节谈到 D 语言时将详细介绍这一点。)但是,可选谓词和动作文本在命令行中使用时必须适当地用引号括起来,以免 shell 对其进行解释。在清单 4 中,-n
选项标志为以下形式:-n 'function:name {action}'
。
下面是谓词的一些示例。
如果探测器在 cpu0 上执行,则为 true:
cpu == 0
如果触发探测器的进程 PID 为 1029,则为 true:
pid == 1029
如果进程不是调度程序 (sched
),则为 true:
execname != "sched"
如果父 PID 不为 0 且第一个参数为 0,则为 true:
ppid != 0 && arg0 == 0
如果只想了解特定 CPU 上触发的特定探测器,而不跟踪任何数据或执行任何其他操作,可以指定一组内部无语句的空括号,DTrace 就会只打印触发的探测器的名称。
下面是操作的一些示例。
使用 C 语言式 printf()
命令打印内容:
printf()
打印用户级堆栈:
ustack()
打印给定变量:
trace
使用 D 编写 DTrace 脚本
使用 D 语言编写的 DTrace 脚本包括一组子句,描述要启用的探测器以及绑定到这些探测器的谓词和操作。D 程序也可以包含杂注、变量、指针和数组声明,以及新类型定义,与 C 程序很相似。这些程序也可能很复杂。
不过,与 C 或 C++ 程序不同,但与 Java 程序类似,DTrace 将 D 程序编译成安全的中间形式,用于在探测器触发时执行。DTrace 验证此中间形式能否安全运行,报告 D 程序执行期间可能发生的任何运行时错误,如除以零或取消引用无效内存。因此,您不可能构造不安全的 D 程序。您可以在生产环境中使用 DTrace,不必担心导致系统崩溃或损坏。如果发生了编程错误,DTrace 会禁用插装并向您报告错误。
每个探测器子句以一个或多个探测器描述 列表开头,每个描述采用通常的 provider:module:function:name
形式,后面跟一个谓词子句和一组要采取的操作:
probe descriptions / predicate / { action statements }
D 与 C、C++ 和 Java 等其他编程语言的一个主要区别在于它没有如 if
语句和循环之类的控制流构造。D 程序子句写作单行语句列表,跟踪可选的固定数量的数据。D 的确能够使用被称作谓词 的逻辑表达式有条件地跟踪数据和修改控制流,这些谓词可用作程序子句的前缀。
谓词表达式包含在一对斜线 (//
) 中,在执行与对应子句关联的任何语句之前在探测器触发时进行求值。如果谓词计算结果为 true(用任何非零值表示),则执行语句列表。如果谓词为 false(用零值表示),则不执行任何操作语句,并忽略探测器触发。
谓词是用于在 D 程序中构建更复杂控制流的主要条件构造。可以对任何探测器整个省略探测器子句的谓词部分,这样,在探测器触发时始终执行操作。
探测器操作由语句列表描述,这些语句由分号 (;
) 分隔,包含在括号 {}
中。如果只想了解特定 CPU 上触发的特定探测器,而不跟踪任何数据或执行任何附加操作,可以指定一组内部无语句的空括号。
可以在 -s pathname
选项中提供脚本路径直接通过 dtrace
命令运行 DTrace 脚本,也可以通过创建一个以 #!/usr/sbin/dtrace -s
调用开头的独立脚本运行 DTrace 脚本。
清单 5 是一个 DTrace 脚本,使用 -s
选项显示用 -c
选项指定的命令执行的系统调用:
$ cat syscalls.d syscall:::entry /pid == $target/ { @[probefunc] = count(); } $ dtrace -s syscalls.d -c date dtrace: script 'syscalls.d' matched 296 probes Thu May 23 00:40:52 CEST 2013 access 1 arch_prctl 1 exit_group 1 getrlimit 1 lseek 1 rt_sigprocmask 1 set_robust_list 1 set_tid_address 1 write 1 futex 2 rt_sigaction 2 brk 3 munmap 3 read 5 open 6 mprotect 7 close 8 newfstat 8 mmap 16
清单 5
在 syscalls.d
脚本中,$target
宏取命令 (/bin/date
) 的 PID 值,谓词只跟踪针对该 PID 触发的系统调用入口探测器。有许多有用的宏(如 $target
)允许脚本访问运行中进程的特性。
括号中的操作只在谓词计算结果为 true 时执行。操作允许 DTrace 程序与 DTrace 之外的系统交互。最常见的操作将数据记录到 DTrace 缓冲区。
DTrace 提供了一组内置函数,用于聚合各探测器收集的数据。在清单 5 中,count()
函数返回函数被调用的次数。DTrace 将聚合函数的结果存储在被称作聚合 的对象中,这些对象使用表达式字节组进行索引。在 D 中,聚合的语法如下所示,其中 name
是用户定义的聚合名称,keys
是 D 表达式的逗号分隔列表,aggfunc
是一个 DTrace 聚合函数,args
是聚合函数相应的逗号分隔参数列表。聚合名称是以特殊字符 @
为前缀的 D 标识符。
@name[ keys ] = aggfunc ( args );
下面是 DTrace 提供的一些聚合。
返回其参数的数学含义:
avg(expressions)
返回函数的调用次数:
count()
返回其参数的标准偏差:
stddev(expressions)
D 语言富含 C 语言式对象和语法,允许定义变量、数组、指针、字符串、结构和联合。该语言新增了子例程库功能。Oracle Linux 第 6 版动态跟踪指南 介绍了完整的语言及示例。
在 Oracle Linux 上安装和配置 DTrace
DTrace 软件包和 Unbreakable Enterprise Kernel (UEK) 软件包可从 Unbreakable Linux Network (ULN) 上获取,但不能从公共 yum 服务器上获取。必须先在 ULN 上注册您的系统,然后才能下载所需的软件包。Oracle Linux 第 6 版管理员解决方案指南列出了需要从 ULN 下载的软件包,并介绍了如何安装它们。
管理员解决方案指南 还介绍了如何使用 modprobe
命令加载支持您需要使用的 DTrace 探测器的模块。例如,如果要使用 proc
提供程序发布的探测器,将加载 systrace
模块:
# modprobe systrace
DTrace 的价值
DTrace 使系统管理员和应用程序开发人员能够从内部、通过系统库和系统调用、深入内核了解操作系统和运行中应用程序的工作。
使用 DTrace 作为工具去发现运行中系统问题的根本原因并进行定量分析,即使问题发生在内核设备驱动程序内部或者软件体系中任何其他位置。而且,这样做不会干扰系统、无需重启,也不会产生任何其他不利影响。您可以将其视作工具箱中的瑞士军刀。
另请参见
- Oracle Linux 第 6 版动态跟踪指南 和 Oracle Linux 第 6 版管理员解决方案指南 均可从 Oracle Linux 6 产品文档网站http://docs.oracle.com/cd/E37670_01/ 获取。
- “教程:DTrace 示例”: http://www.oracle.com/technetwork/server-storage/solaris/dtrace-tutorial-142317.html
- 《DTrace:Dynamic Tracing in Oracle Solaris, Mac OS X and FreeBSD》,作者:Brendan Gregg 和 Jim Mauro;Prentice Hall 2011: http://www.dtracebook.com/index.php/Main_Page
- DTrace 论坛:https://forums.oracle.com/forums/forum.jspa?forumID=1017
关于作者
Richard Friedman 是一位自由技术撰稿人,为多家高科技公司和组织开发内容和文档。Richard 在 Sun Microsystems 公司有超过 15 年的工作经历,是编译器和开发工具文档的主要编写人。早在此之前,他曾任职纽约大学 Courant 学院、劳伦斯伯克利实验室计算机中心和各种软件咨询组织的系统和应用程序程序员。他现在居住在加州奥克兰。