> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/di-41-zhang-kai-fa-gong-ju/di-41.4-jie-dtrace.md).

# 41.4 DTrace

DTrace 源于 Sun Microsystems 的 Solaris 操作系统，以 CDDL 许可证开源。FreeBSD 自 GENERIC 内核起即内置支持，由内核模块实现。

> **警告**
>
> 由于提交 [1ca7542a1b31](https://cgit.freebsd.org/src/commit/?id=1ca7542a1b31fffefaac5a2ff45ef23f908b33c2) 的变更，本节内容仅适用于 16.0-CURRENT。

## 概述

DTrace 可用于诊断系统性能问题，调查和调试 FreeBSD 内核及用户空间程序中的意外行为，支持运行预编写的脚本，也可使用 D 语言编写自定义工具以满足特定分析需求。

FreeBSD 实现提供了对内核 DTrace 的完整支持，以及对用户空间 DTrace 的实验性支持。通过用户空间 DTrace 的 `pid` 提供者，可跟踪用户空间程序的函数边界，并在用户空间程序中插入静态探针以供后续跟踪。某些 Port，如 **databases/postgresql18-server** 和 **lang/php85**，提供了 DTrace 选项以启用静态探针。

Port databases/postgresql18-server 的 Makefile 部分源代码：

```makefile
DTRACE_CONFIGURE_ENABLE=dtrace
DTRACE_LDFLAGS=		-lelf
DTRACE_INSTALL_TARGET=	install
```

Port lang/php85 的 Makefile 部分源代码：

```makefile
OPTIONS_DEFINE+=CGI CLI DEBUG DTRACE EMBED FPM IPV6 LINKTHR \
				MYSQLND NOASLR PCRE PHPDBG ZTS JIT
```

DTrace 的官方指南由 illumos 项目维护，网址为 [illumos DTrace Guide](https://illumos.org/books/dtrace/bookinfo.html)。

> **注意**
>
> FreeBSD 的 DTrace 实现与 illumos/macOS 上的 DTrace 存在差异，部分探针和 D 脚本在不同平台间不可直接移植。用户空间 DTrace（USDT）在 FreeBSD 上为实验性功能。

## 实现上的差异

虽然 FreeBSD 中的 DTrace 与 Solaris 类似，但存在一些差异。主要区别在于，FreeBSD 中的 DTrace 由一组内核模块实现。自 FreeBSD 10.0-RELEASE 起，运行 dtrace 时会自动加载所需模块。

FreeBSD 使用内核选项 `DDB_CTF` 以支持从内核模块及内核本身加载 CTF 数据。`CTF` 是 Solaris 的 Compact C Type Format，用于封装简化的调试信息，类似 `DWARF` 或传统的 stabs。`CTF` 数据由 ctfconvert 和 ctfmerge 构建工具添加到二进制文件中。`ctfconvert` 工具解析编译程序生成的 `DWARF` `ELF` 调试段，而 `ctfmerge` 将对象文件中的 `CTF` `ELF` 段合并到可执行文件或共享库中。

CTF 数据生成流程如下：

![CTF 数据生成流程](/files/x4oBKYNsg8bemUkiZa3i)

FreeBSD 提供的 provider 与 Solaris 略有不同。`dtmalloc` provider 较为特别，它能在 FreeBSD 内核中按类型跟踪 malloc。某些 Solaris 中的 provider 在 FreeBSD 中不存在。同时，两者均有的 provider 可能不兼容，探针的参数类型不同。因此，在 Solaris 上编写的 D 脚本在 FreeBSD 上可能需要修改才能运行，反之亦然。

由于安全机制差异，FreeBSD 上仅 `root` 用户可使用 DTrace。Solaris 中存在一些低级安全检查，而 FreeBSD 尚未实现。因此，**/dev/dtrace/dtrace** 设备文件严格限定为 `root` 用户可访问。

FreeBSD 与 Solaris/illumos 的 DTrace 实现差异概要：

| 特性          | FreeBSD       | Solaris / illumos |
| ----------- | ------------- | ----------------- |
| 实现方式        | 内核模块（可加载）     | 内核内置              |
| 模块加载        | 自动加载          | 不适用               |
| 调试信息格式      | CTF（DDB\_CTF） | CTF               |
| 特有 Provider | dtmalloc      | 部分 provider 不兼容   |
| 用户空间 DTrace | 实验性支持         | 完整支持              |
| 安全模型        | 仅 root 可访问    | 细粒度权限控制           |
| D 脚本可移植性    | 可能需要修改        | 可能需要修改            |
| 许可证         | CDDL + BSD 内核 | CDDL              |

DTrace 本身以通用开发与分发许可证（CDDL）发布。支持 DTrace 的 FreeBSD 内核本身采用 BSD 许可，但 DTrace 内核模块以二进制形式分发或加载时适用 CDDL（许可证见 **sys/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE**，具体源代码见 **sys/cddl/dev/dtrace/dtrace\_cddl.h**）。

## 开启 DTrace 支持

DTrace 支持 **已内置于 GENERIC** 内核中。自 FreeBSD 10.0-RELEASE 起，运行 dtrace 时会自动加载所需模块。如需确保所有 DTrace 模块均已加载（某些脚本可能依赖未自动加载的 provider），可手动执行：

```sh
kldload dtraceall
```

可安装 DTrace 工具包 sysutils/dtrace-toolkit。

使用 pkg 安装：

```sh
# pkg install dtrace-toolkit
```

还可以使用 Ports 安装：

```sh
# cd /usr/ports/sysutils/dtrace-toolkit/
# make install clean
```

该工具包内置了用于收集系统信息的预制脚本，包括检查打开的文件、内存、CPU 使用情况等。FreeBSD 基本系统中也内置了若干脚本，存放在 **/usr/share/dtrace**，在源代码中位于 **share/dtrace/**。

> **注意**
>
> **/usr/share/dtrace** 中的脚本是专门为 FreeBSD 移植的。DTrace 工具包中的脚本并非全部可直接在 FreeBSD 上运行，个别脚本需要一定修改才能使用。

DTrace 工具包使用 DTrace 特有的脚本语言 D 语言，其语法与 C++ 相似。本节不对 D 语言深入讲解。如需概览，请参阅 FreeBSD 的 dtrace(1) 手册页。D 语言在 [illumos 动态跟踪指南](https://www.illumos.org/books/dtrace/bookinfo.html) 中也有详细介绍。

如需将 DTrace 静态编译进内核，应在自定义内核配置文件中添加以下行，并重新编译内核：

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

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

```ini
options         KDTRACE_FRAME
```

此选项提供了对 `dtrace_fbt` 的支持。虽然没有该选项 DTrace 仍可运行，但函数边界跟踪功能将受限。

要为内核外模块添加 DTrace 支持，可在模块的 Makefile 中加入以下行：

```cpp
CFLAGS+= -DKDTRACE_HOOKS
```

此标志在编译过程中启用 DTrace 钩子，从而便于调试和监控模块。修改后，需要重新编译该模块以激活 DTrace 功能。

## 使用 DTrace

DTrace 脚本由一个或多个 **探针（probes）** 组成，每个探针都是一个可监控的点，并与一个动作关联。当探针条件满足时，关联的动作即被执行。例如，动作可能发生在文件打开、进程启动或代码行执行时。动作可以记录信息或修改上下文变量。上下文变量的读写允许探针共享信息，从而协作分析不同事件之间的关联。

DTrace 的探针 - 动作模型如下：

![DTrace 的探针 - 动作模型](/files/GBbWywAeefVi9P0I2XGA)

要查看所有探针，需要以 root 身份执行以下命令：

```sh
# dtrace -l | more
   ID   PROVIDER            MODULE                          FUNCTION NAME
    1     dtrace                                                     BEGIN
    2     dtrace                                                     END
    3     dtrace                                                     ERROR
    4        fbt            kernel                camstatusentrycomp entry
    5        fbt            kernel                camstatusentrycomp return
    6        fbt            kernel            cam_compat_handle_0x18 entry
    7        fbt            kernel            cam_compat_handle_0x18 return

……以下省略……
```

每个探针都有一个 `ID`、一个 `PROVIDER`（例如 `dtrace` 或 `fbt`）、一个 `MODULE` 和一个 `FUNCTION NAME`。

普通用户无法运行 DTrace：

```sh
$ dtrace -l
dtrace: failed to initialize dtrace: DTrace requires additional privileges
```

示例：**hotkernel** 脚本用于识别占用内核时间最多的函数。

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

按 Ctrl+C 停止执行。脚本结束后，会显示内核函数及其时间信息，并按时间升序排序：

```sh
dtrace: buffer size lowered to 1m
dtrace: aggregation size lowered to 1m
^C
FUNCTION                                                COUNT   PCNT
kernel`vm_page_alloc_domain_iter                            1   0.0%
kernel`cpu_idle                                             7   0.0%
kernel`em_update_stats_counters                             8   0.0%
kernel`spinlock_exit                                       33   0.0%
kernel`acpi_cpu_c1                                     128203 100.0%
```

该脚本也支持内核模块。使用 `-m` 选项运行：

```sh
# cd /usr/local/share/dtrace-toolkit
# ./hotkernel -m
Sampling... Hit Ctrl-C to end.
dtrace: buffer size lowered to 1m
dtrace: aggregation size lowered to 1m
^C
MODULE                                                  COUNT   PCNT
kernel                                                  80153 100.0%
```

示例：**procsystime** 脚本捕获并打印指定进程 `ID`（`PID`）或进程名称的系统调用时间。例如，启动一个新的 **/bin/sh** 实例后运行：

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

Elapsed Times for processes sh,

         SYSCALL          TIME (ns)
         geteuid               4203
       sigreturn              14858
        __sysctl              61708
           write             640999
            read         2507609226
```

如上所示，read 系统调用耗时最长，geteuid 耗时最短。

示例：基本系统内置的 **hotopen** 脚本会定期输出表格，列出哪些 UID 正在打开文件。

```sh
# cd /usr/share/dtrace
# ./hotopen
dtrace: buffer size lowered to 1m
dtrace: aggregation size lowered to 1m
Files opened per UID in the last second.
2026 May  2 19:20:52
uid 0	count 2
2026 May  2 19:20:53
uid 0	count 2
2026 May  2 19:20:54
uid 0	count 1
^C
2026 May  2 19:20:55
uid 0	count 1
```

由此可快速判断特定用户是否正在通过频繁调用 open/close 使系统进入“抖动”（thrashing）状态。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/di-41-zhang-kai-fa-gong-ju/di-41.4-jie-dtrace.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
