27.4.使用 DTrace
================

DTrace 脚本由一个或多个 *探测器* 或测量点组成,每个探测器关联一个动作。当探测器的条件被满足时,与之相关联的动作就会被执行。例如,当打开一个文件,启动一个进程或者运行一行代码时可能执行一个动作。这个动作可能是记录某些信息,或者变更上下文变量。对上下文变量的读取和写入使得探测器之间能够共享信息,并且对不同事件协同分析其相关性。

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

.. raw:: latex

   \diilbookstyleinputcell

.. code:: shell-session

   # dtrace -l | more

每个探测器都有一个 ``ID``\ ,一个 ``PROVIDER``\ (dtrace 或 fbt),一个 ``MODULE`` 和一个 ``FUNCTION NAME``\ 。关于这个命令的更多信息请见 `dtrace(1) <https://www.freebsd.org/cgi/man.cgi?query=dtrace&sektion=1&format=html>`__\ 。

这节中的例子大致演示了如何使用 DTrace Toolkit 中两个得到完整支持的脚本:\ **hotkernel** 和 **procsystime**\ 。

脚本 **hotkernel** 被设计用来识别占用内核时间最多的函数。它会生成类似下文的输出:

.. raw:: latex

   \diilbookstyleinputcell

.. code:: shell-session

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

如提示,用快捷键 ``Ctrl`` + ``C`` 结束进程。结束时,脚本会显示内核函数和用时信息列表,按用时升序排列:

.. raw:: latex

   \diilbookstyleinputcell

.. code:: shell-session

   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`` 执行这个脚本:

.. raw:: latex

   \diilbookstyleinputcell

.. code:: shell-session

   # ./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`` 中输入几个命令。这是该测试的结果:

.. raw:: latex

   \diilbookstyleinputcell

.. code:: shell-session

   # ./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()`` 占用的时间最少。