第 27 章 DTrace

如果发现翻译错误,请直接 发起PR修改

27.1. 简介

DTrace,也被称为动态跟踪,是由 Sun™ 开发的一种工具,用于定位生产和预生产系统中的性能瓶颈。除了诊断性能问题外,DTrace 还可以用于帮助调查和调试 FreeBSD 内核和用户程序中的意外行为。

DTrace 是一个出色的性能分析工具,具有令人印象深刻的一系列功能,用于诊断系统问题。它还可以用于运行预先编写的脚本,以利用其功能。用户可以使用 DTrace D 语言编写自己的实用工具,根据特定需求自定义性能分析。

FreeBSD 实现提供了对内核 DTrace 的完全支持,并对用户空间 DTrace 提供了实验性支持。用户空间 DTrace 允许用户使用 pid 提供程序对用户空间程序进行函数边界跟踪,并在用户空间程序中插入静态探针以供后续跟踪。一些 ports,如 databases/postgresql12-serverlang/php74,具有启用静态探针的 DTrace 选项。

DTrace 的官方指南由 Illumos 项目维护,网址为 DTrace 指南

阅读完本章后,您将了解:

  • 什么是 DTrace 及其提供的功能。

  • Solaris™ DTrace 实现与 FreeBSD 提供的实现之间的区别。

  • 如何在 FreeBSD 上启用和使用 DTrace。

在阅读本章之前,您应该:

  • 了解 UNIX® 和 FreeBSD 基础知识(FreeBSD Basics)。

  • 对 FreeBSD 的安全性有一定的了解(Security)。

27.2. 实现差异

尽管 FreeBSD 中的 DTrace 与 Solaris™ 中的 DTrace 相似,但仍存在一些差异。主要的差异在于,在 FreeBSD 中,DTrace 是作为一组内核模块实现的,只有在加载这些模块之后才能使用 DTrace。要加载所有必要的模块:

# kldload dtraceall

从 FreeBSD 10.0-RELEASE 开始,当运行 dtrace 时,模块会自动加载。

FreeBSD 使用 DDB_CTF 内核选项来启用对从内核模块和内核本身加载 CTF 数据的支持。 CTF 是 Solaris™ Compact C Type Format,它封装了一种类似于 DWARF 和古老的 stabs 的调试信息的简化形式。CTF 数据是由 ctfconvertctfmerge 构建工具添加到二进制文件中的。ctfconvert 实用程序解析由编译器创建的 DWARFELF 调试部分,而 ctfmerge 将对象中的 CTFELF 部分合并到可执行文件或共享库中。

FreeBSD 和 Solaris™ 存在一些不同的 providers 程序。其中最显著的是 dtmalloc provider,它允许在 FreeBSD 内核中按类型跟踪 malloc()。 Solaris™ 中的一些 provider,如 cpcmib,在 FreeBSD 中不存在。这些可能会出现在未来的 FreeBSD 版本中。此外,两个操作系统中都可用的一些 provider 不兼容,因为它们的探测器具有不同的参数类型。因此,在 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 许可证。

27.3. 启用 DTrace 支持

在 FreeBSD 9.2 和 10.0 中,DTrace 支持已经内置在 GENERIC 内核中。使用早期版本的 FreeBSD 或者更喜欢静态编译 DTrace 支持的用户应该在自定义内核配置文件中添加以下行,并按照 配置FreeBSD内核 中的说明重新编译内核。

options         KDTRACE_HOOKS
options         DDB_CTF
makeoptions	DEBUG=-g
makeoptions	WITH_CTF=1

使用 AMD64 架构的用户还应添加以下行:

options         KDTRACE_FRAME

此选项提供对 FBT 的支持。虽然在没有此选项的情况下 DTrace 也可以工作,但对于函数边界跟踪的支持将受到限制。

一旦 FreeBSD 系统重启到新内核,或者使用 kldload dtraceall 加载了 DTrace 内核模块,系统将需要支持 Korn shell,因为 DTrace Toolkit 中有几个工具是用 ksh 编写的。确保安装了 shells/ksh93 软件包或 port。也可以在 shells/pdkshshells/mksh 下运行这些工具。

最后,安装当前的 DTrace Toolkit,这是一个收集系统信息的现成脚本集合。其中包括检查打开文件、内存、CPU 使用情况等脚本。 FreeBSD 10 会将其中一些脚本安装在 /usr/share/dtrace 目录下。对于其他版本的 FreeBSD,或者要安装完整的 DTrace Toolkit,可以使用 sysutils/dtrace-toolkit 包或 port 进行安装。

/usr/share/dtrace 中找到的脚本已经专门移植到了 FreeBSD。并非所有在 DTrace Toolkit 中找到的脚本都可以直接在 FreeBSD 上运行,有些脚本可能需要一些努力才能在 FreeBSD 上运行。

DTrace 工具包中包含了许多使用 DTrace 特殊语言编写的脚本。这种语言被称为 D 语言,它与 C ++非常相似。本文档不会深入讨论该语言的细节,但在 Illumos 动态跟踪指南 中有详细介绍。

27.4. 使用 DTrace

DTrace 脚本由一个或多个探针(或称为仪器点)的列表组成,每个探针都与一个动作相关联。当探针的条件满足时,执行相应的动作。例如,当文件被打开、进程被启动或者代码行被执行时,可能会触发一个动作。动作可以是记录一些信息或修改上下文变量。读取和写入上下文变量允许探针共享信息并协同分析不同事件之间的关联性。

要查看所有探针,管理员可以执行以下命令:

# dtrace -l | more

每个探针都有一个 ID,一个 PROVIDER(dtrace 或 fbt),一个 MODULE 和一个 FUNCTION NAME。有关此命令的更多信息,请参阅 dtrace(1)

本节中的示例提供了如何使用 DTrace Toolkit 中两个完全支持的脚本的概述:hotkernelprocsystime 脚本。

hotkernel 脚本旨在识别哪个函数使用了最多的内核时间。它将产生类似以下的输出:

# cd /usr/local/share/dtrace-toolkit
# ./hotkernel
Sampling... Hit Ctrl-C to end.

按照指示,使用 Ctrl+C 键组合来停止进程。在终止后,脚本将显示一个内核函数列表和时间信息,按时间递增的顺序排序输出:

kernel`_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 选项运行脚本:

# ./hotkernel -m
Sampling... Hit Ctrl-C to end.
^C
MODULE                                                  COUNT   PCNT
0xc107882e                                                  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%

procsystime 脚本用于捕获并打印给定进程 ID(PID) 或进程名称的系统调用时间使用情况。在下面的示例中,一个新的 /bin/csh 实例被创建。然后,执行了 procsystime 并保持等待状态,同时在另一个 csh 实例中输入了一些命令。这是这个测试的结果:

# ./procsystime -n csh
Tracing... Hit Ctrl-C to end...
^C

Elapsed 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() 系统调用使用的时间最长,以纳秒为单位,而 getpid() 系统调用使用的时间最短。


上次修改时间: September 18, 2024 by fiercex