---
title: 章 24. DTrace
part: 部 III. 系統管理
prev: books/handbook/cutting-edge
next: books/handbook/usb-device-mode
description: This chapter explains how to use DTrace in FreeBSD
tags: ["DTrace", "features", "guide", "tutorial", "kldload"]
showBookMenu: true
weight: 30
params:
  path: "/books/handbook/dtrace/"
---

[[dtrace]]
= DTrace
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 24
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/dtrace/

ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]

ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]

[[dtrace-synopsis]]
== 概述

DTrace，又被稱作 Dynamic Tracing ，由 Sun(TM) 開發，用在生產 (production) 跟預生產 (pre-production) 系統中找出效能瓶頸的工具。
除了診斷性能問題外，DTrace 還可以用於查詢以及除錯 FreeBSD 核心和使用者層級程式的未預期行為。

DTrace 是一個卓越的分析工具，具有一系列令人驚豔、用於診斷系統問題的功能。
它還可以執行預先寫好的腳本，以使用其功能。
使用者可以用 DTrace D 語言編寫自己的工具，從而允許他們根據特定的需求客製化。

FreeBSD 實做提供對核心層級的 DTrace 全面的支援，以及對使用者層級的 DTrace 實驗性的支援。
使用者層級的 DTrace 允許使用者使用 `pid` 執行函式邊界追蹤 (function boundary tracing)，並將 static probes 插入到使用者程式以供之後追蹤。
一些 ports，像是 package:databases/postgresql12-server[] 和 package:lang/php74[] 提供 DTrace 選項，以提供 static probes 功能。

DTrace 的官方指南由 Illumos 維護，在 https://illumos.org/books/dtrace/bookinfo.html[DTrace Guide]。

讀完這章，您將了解：

* 什麼是 DTrace 以及其提供的功能。
* Solaris(TM) 實做的 DTrace 跟 FreeBSD 提供的 DTrace 之間的不同之處。
* 如何在 FreeBSD 上啟用和使用 DTrace。

在開始閱讀這章之前，您需要：

* 了解 UNIX(TM) 及 FreeBSD 基礎 (crossref:basics[basics,FreeBSD 基礎])。
* 了解安全性以及其跟 FreeBSD 的關係 (crossref:security[security,安全性])。

[[dtrace-implementation]]
== 實作差異

雖然 FreeBSD 的 DTrace 和 Solaris(TM) 的 DTrace 類似，但是還是有存在差異。
最重要的區別為，在 FreeBSD 中，DTrace 是作為一組核心模組 (kernel modules) 實做的，並且在載入模組之前無法使用。
要載入所有需要的模組:

[source,shell]
....
# kldload dtraceall
....

從 FreeBSD 10.0-RELEASE 之後，模組會在執行 `dtrace` 時自動載入。

FreeBSD 使用 `DDB_CTF` 核心選項來支援從核心模組和核心本身載入 `CTF` 資料。
`CTF` 是 Solaris(TM) Compact C Type Format，它封裝了一種簡化形式的除錯資訊，類似於 `DWARF` 和 古老的 stabs。
`CTF` 資料通過`ctfconvert` and `ctfmerge` 建構工具，加入到二進制文件中。
`ctfconvert` 工具分析編譯器創建的 `DWARF``ELF` 除錯部份，而 `ctfmerge` 將目標的 `CTF``ELF` 部份合併到執行檔或函式庫中。

與 Solaris(TM) 相比，FreeBSD 存在一些不同的 providers。
最值得注意的是 `dtmalloc` provider 允許在 FreeBSD 核心中按照類型 (type) 追蹤 `malloc()`。
Solaris(TM) 中的一些 providers，例如 `cpc` 和 `mib`，在 FreeBSD 中則不存在。
這些可能會在 FreeBSD 未來的版本中出現。
此外，兩個作業系統中一些可用的 providers 是不相容的，因為他們具有不同的參數類型。
因此，在 Solaris(TM) 上拓寫的 `D` 腳本在未經修改的情況下可能可以或不可以在 FreeBSD 上執行，反之亦然。

因為安全的差異，只有 `root` 可以在 FreeBSD 上使用 DTrace。
Solaris(TM) 擁有一些 FreeBSD 中還不存在的低階 (low level) 安全檢查。
因此 [.filename]#/dev/dtrace/dtrace# 被嚴格限制成 `root`。

DTrace 使用 Common Development and Distribution License (`CDDL`) 授權。
要在 FreeBSD 上查看此授權, 請參閱 [.filename]#/usr/src/cddl/contrib/opensolaris/OPENSOLARIS.LICENSE# 或者在 http://opensource.org/licenses/CDDL-1.0[http://opensource.org/licenses/CDDL-1.0] 線上查看。
雖然具有 DTrace 支援的 FreeBSD 核心使用 `BSD` 授權，但當模組使用二進制形式或者二進制文件發布時，將使用 `CDDL` 授權。

[[dtrace-enable]]
== 開啟 DTrace 支援

在 FreeBSD 9.2 和 10.0 中，DTrace 內建於 [.filename]#GENERIC#  核心裡。
FreeBSD 早期版本的使用者或喜歡在 DTrace 支援下靜態編譯的使用者應加入下列幾行到客製化核心配置文件，並根據 crossref:kernelconfig[kernelconfig,Configuring the FreeBSD Kernel] 中的說明重新編譯核心:

[.programlisting]
....
options         KDTRACE_HOOKS
options         DDB_CTF
makeoptions	DEBUG=-g
makeoptions	WITH_CTF=1
....

AMD64 架構的使用者應加入下列幾行:

[.programlisting]
....
options         KDTRACE_FRAME
....

此選項提供對 `FBT` 的支援，
雖然 DTrace 可以在沒有此選項的情況下運作，但對函式邊界追蹤的支援有限。

一旦 FreeBSD 系統使用新的核心重新啟動，或者使用 `kldload dtraceall` 載入 DTrace 核心模組後，系統需要支援 Korn shell，因為 DTrace 工具箱有幾個用 `ksh` 拓寫的工具。
確保已經安裝 package:shells/ksh93[] 套件或者 port，
也可以在 package:shells/pdksh[] 或者 package:shells/mksh[] 下執行這些工具。

最後，安裝目前的 DTrace 工具箱，這是一組用於收集系統資訊的現成腳本，
有一些腳本可以檢查打開的文件、記憶體、`CPU` 使用情況等等。
FreeBSD 10 將其中一些腳本安裝在 [.filename]#/usr/share/dtrace# 中。
在其他 FreeBSD 的版本中，要安裝 DTrace 工具箱，請使用 package:sysutils/dtrace-toolkit[] 套件或者 port。

[NOTE]
====
[.filename]#/usr/share/dtrace# 中的腳本已專門移植到 FreeBSD，
並非所有在 DTrace 工具箱中的所有腳本都能在 FreeBSD 上按照原樣運作，一些腳本可能需要一些修改才能在 FreeBSD 上運作。
====

DTrace 工具箱包含許多使用 DTrace 特殊語言的腳本，
這種語言被稱為 D 語言，它與 C++ 非常類似，
對於該語言的深度討論超出了此文件的範圍，
他在 http://www.dtrace.org/guide[Illumos Dynamic Tracing Guide] 有廣泛的介紹。

[[dtrace-using]]
== 使用 DTrace

DTrace 腳本由一個或多個 _probes_ 或檢查點 (instrumentation points) 的列表組成，其中每個 probe 都與一個行為有關，
只要能滿足 probe 的條件，就會執行相關的行為，
舉例來說，打開文件、啟動一個行程或執行一行程式。
該行為可能是紀錄一些資訊，或修改上下文變數 (context variables)，
上下文變數的讀寫允許 probes 分享資訊和共同分析不同事件的相關性。

想要查看所有的 probes，系統管理員可以執行以下指令:

[source,shell]
....
# dtrace -l | more
....

每個 probe 都有一個 `ID`、一個 `PROVIDER` (dtrace 或者 fbt)、一個 `MODULE` 和一個 `FUNCTION NAME`。
有關此指令的更多資訊，請參閱 man:dtrace[1]。

本節中的例子概述如何使用 DTrace 工具箱中完全支援的兩個腳本: [.filename]#hotkernel# 和 [.filename]#procsystime# 腳本。

[.filename]#hotkernel# 腳本設計成觀察哪個函式使用的核心時間最多，
它會產生類似於以下內容的輸出:

[source,shell]
....
# cd /usr/local/share/dtrace-toolkit
# ./hotkernel
Sampling... Hit Ctrl-C to end.
....

按照說明，使用 kbd:[Ctrl+C] 組合鍵停止行程，
中止後，腳本將顯示一整列的核心函式和時間資訊，按照時間遞增排序:

[source,shell]
....
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` 執行腳本:

[source,shell]
....
# ./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%
....

[.filename]#procsystime# 抓取和輸出系統調用時間，給設定行程 ID (PID) 或行程名稱的行程。
在以下的例子中，生成了 [.filename]#/bin/csh# 新物件，
然後，[.filename]#procsystime# 被執行並一直等待，同時在 `csh` 的另一個化身上輸入一些指令，
以下是本次測試的結果:

[source,shell]
....
# ./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()` 系統調用使用的時間最少。
