DTrace,也称为动态跟踪,由Sun™开发,作为定位生产和预生产系统中性能瓶颈的工具。除了诊断性能问题外,DTrace还可以用于帮助调查和调试FreeBSD内核和用户区程序中的意外行为。
DTrace是一个出色的分析工具,具有诊断系统问题的一系列令人印象深刻的功能。它还可以用于运行预先编写的脚本,以利用其功能。用户可以使用DTrace D语言编写自己的实用程序,允许他们根据特定需求自定义分析。
FreeBSD实现提供了对内核DTrace的全面支持,以及对用户区DTrace的实验性支持。Userland DTrace允许用户使用 pid 提供程序对用户区程序执行函数边界跟踪,并将静态探测插入用户区程序以供以后跟踪。一些端口,如 databases/postgresql12-server 和 lang/php74 ,有一个DTrace选项来启用静态探测。
DTrace的官方指南由illumos Dynamic Tracing guide的illumos项目维护。
阅读本章后,你会知道:
• DTrace是什么以及它提供了哪些功能。 • Solaris™DTrace实现与FreeBSD提供的实现之间的差异。 • 如何在FreeBSD上启用和使用DTrace。
在阅读本章之前,您应该:
• 了解UNIX®和FreeBSD基础知识(FreeBSD基础知识)。 • 熟悉安全性以及它与FreeBSD(安全)的关系。
虽然FreeBSD中的DTrace与Solaris™中的DTrace相似,但确实存在差异。主要区别在于,在FreeBSD中,DTrace是作为一组内核模块实现的,在加载模块之前不能使用DTrace。要加载所有必要的模块:
xxxxxxxxxx# kldload dtraceall从FreeBSD 10.0-RELEASE开始,当运行 dtrace(1) 时,模块会自动加载。
FreeBSD使用 DDB_CTF 内核选项来支持从内核模块和内核本身加载 ctf(5) 数据。CTF是Solaris™Compact C Type Format,它封装了一种简化形式的调试信息,类似于 DWARF 和古老的stabs。CTF 数据由 ctfconvert(1) 和 ctfmerge(1) 构建工具添加到二进制文件中。 ctfconvert 实用程序解析编译器创建的 DWARF ELF 调试部分, ctfmerge 将对象中的 CTF ELF 部分合并到可执行文件或共享库中。
FreeBSD与Solaris™存在一些不同的提供商。最值得注意的是 dtmalloc 提供程序,它允许在FreeBSD内核中按类型跟踪 malloc(9) 。Solaris™中的一些提供程序,如 cpc ,在FreeBSD中不存在。这些可能会出现在FreeBSD的未来版本中。此外,这两个操作系统中可用的一些提供程序是不兼容的,因为它们的探测器具有不同的参数类型。因此,在Solaris™上编写的 D 脚本可能在FreeBSD上不可修改地工作,反之亦然。
由于安全差异,只有 root 可以在FreeBSD上使用DTrace。Solaris™有一些FreeBSD中尚不存在的低级安全检查。因此,/dev/dtrace/dtrace 被严格限制为 root 用户。
DTrace属于通用开发和分发许可证(Common Development and Distribution License,CDDL)许可证。要在FreeBSD上查看此许可证,请参阅 /usr/src/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE 或在线查看 http://opensource.org/licenses/CDDL-1.0 。虽然支持DTrace的FreeBSD内核是 BSD 许可的,但当模块以二进制形式分发或加载二进制文件时,会使用 CDDL 。
在FreeBSD 9.2和10.0中,DTrace支持内置于GENERIC内核中。FreeBSD早期版本的用户或更喜欢在DTrace支持中静态编译的用户应将以下行添加到自定义内核配置文件中,并使用【10. 配置FreeBSD内核】中的说明重新编译内核:
xxxxxxxxxxoptions KDTRACE_HOOKSoptions DDB_CTFmakeoptions DEBUG=-gmakeoptions WITH_CTF=1AMD64架构的用户还应添加以下行:
xxxxxxxxxxoptions KDTRACE_FRAME此选项提供对 dtrace_fbt(4) 的支持。虽然DTrace可以在没有此选项的情况下工作,但对函数边界跟踪的支持有限。
一旦FreeBSD系统重新启动到新内核中,或者使用 kldload dtraceall 加载了DTrace内核模块,请安装当前的DTrace Toolkit(sysutils/dtrace-toolkit),这是一组用于收集系统信息的现成脚本。有脚本可以检查打开的文件、内存、CPU使用率等等。FreeBSD在基础系统中也包含了一些脚本;请参阅 /usr/share/dtrace 。
xxxxxxxxxx/usr/share/dtrace中的脚本已专门移植到FreeBSD。并非DTrace Toolkit中的所有脚本都能在FreeBSD上正常工作,有些脚本可能需要一些努力才能在FreeBSD上工作。DTrace工具包包含许多DTrace特殊语言的脚本。这种语言被称为D语言,它与C++非常相似。对语言的深入讨论超出了本文的范围。有关FreeBSD上d语言的概述,请参阅FreeBSD d(7) 手册页。《illumos动态跟踪指南》中也广泛介绍了D语言。
要将DTrace支持添加到内核外模块(Out-of-Kernel module)中,这对开发和调试很有用,请在模块的Makefile中包含以下行:
xxxxxxxxxxCFLAGS+= -DKDTRACE_HOOKS此标志在编译期间启用DTrace挂钩(hooks),允许对模块进行高级调试和监控。确保在此修改后重新编译模块以激活DTrace功能。
DTrace脚本由一个或多个探测或检测点的列表组成,其中每个探测都与一个操作相关联。只要满足探测条件,就会执行相关操作。例如,当打开文件、启动进程或执行一行代码时,可能会发生操作。操作可能是记录一些信息或修改上下文变量。上下文变量的读写允许探测器共享信息,并协同分析不同事件的相关性。
要查看所有探测器,管理员可以执行以下命令:
xxxxxxxxxx# dtrace -l | more每个探测器都有一个 ID 、一个 PROVIDER(例如 dtrace 或 fbt )、一个 MODULE 和一个 FUNCTION NAME 。有关此命令的更多信息,请参阅 dtrace(1) 。
本节中的示例概述了如何使用DTrace Toolkit中两个完全支持的脚本:hotkernel 和 procsystem 脚本。
hotkernel 脚本旨在识别哪个函数使用了最多的内核时间。它将产生类似于以下内容的输出:
xxxxxxxxxx# cd /usr/local/share/dtrace-toolkit# ./hotkernelSampling... Hit Ctrl-C to end.按照指示,使用 Ctrl+C 组合键停止该过程。终止后,脚本将显示内核函数和定时信息的列表,按时间升序对输出进行排序:
xxxxxxxxxxkernel`_thread_lock_flags 2 0.0%0xc1097063 2 0.0%kernel`sched_userret 2 0.0%kernel`kern_select 2 0.0%kernel`generic_copyin 3 0.0%kernel`_mtx_assert 3 0.0%kernel`vm_fault 3 0.0%kernel`sopoll_generic 3 0.0%kernel`fixup_filename 4 0.0%kernel`_isitmyx 4 0.0%kernel`find_instance 4 0.0%kernel`_mtx_unlock_flags 5 0.0%kernel`syscall 5 0.0%kernel`DELAY 5 0.0%0xc108a253 6 0.0%kernel`witness_lock 7 0.0%kernel`read_aux_data_no_wait 7 0.0%kernel`Xint0x80_syscall 7 0.0%kernel`witness_checkorder 7 0.0%kernel`sse2_pagezero 8 0.0%kernel`strncmp 9 0.0%kernel`spinlock_exit 10 0.0%kernel`_mtx_lock_flags 11 0.0%kernel`witness_unlock 15 0.0%kernel`sched_idletd 137 0.3%0xc10981a5 42139 99.3%此脚本也适用于内核模块。要使用此功能,请使用 -m 运行脚本:
xxxxxxxxxx# ./hotkernel -mSampling... Hit Ctrl-C to end.^CMODULE COUNT PCNT0xc107882e 1 0.0%0xc10e6aa4 1 0.0%0xc1076983 1 0.0%0xc109708a 1 0.0%0xc1075a5d 1 0.0%0xc1077325 1 0.0%0xc108a245 1 0.0%0xc107730d 1 0.0%0xc1097063 2 0.0%0xc108a253 73 0.0%kernel 874 0.4%0xc10981a5 213781 99.6%procsystemtime 脚本捕获并打印给定进程 ID(PID)或进程名称的系统调用时间使用情况。在下面的示例中,生成了 /bin/csh 的新实例。然后,procsystime 被执行并保持等待,同时在 csh 的另一个化身上键入一些命令。以下是本次测试的结果:
x# ./procsystime -n cshTracing... Hit Ctrl-C to end...^CElapsed Times for processes csh, SYSCALL TIME (ns) getpid 6131 sigreturn 8121 close 19127 fcntl 19959 dup 26955 setpgid 28070 stat 31899 setitimer 40938 wait4 62717 sigaction 67372 sigprocmask 119091 gettimeofday 183710 write 263242 execve 492547 ioctl 770073 vfork 3258923 sigsuspend 6985124 read 3988049784如图所示, read(2) 系统调用使用了最多的时间(以纳秒为单位),而 getpid(2) 的系统调用使用的时间最少。