Solaris 动态跟踪指南

第 22 章 sdt 提供器

静态定义的跟踪 (Statically Defined Tracing,SDT) 提供器在软件程序员正式指定的位置创建探测器。使用 SDT 机制,程序员可以有意识地选择 DTrace 用户关注的位置,并通过探测器名称告知有关每个位置的一些语义知识。Solaris 内核已定义了少量的 SDT 探测器,在以后可能会添加更多的探测器。DTrace 还为用户应用程序开发者提供了定义静态探测器的机制,如第 34 章中所述。

探测器

表 22–1 中列出了 Solaris 内核定义的 SDT 探测器。这些探测器的名称稳定性和数据稳定性均为“专用”,因为此处对其进行的说明仅反映内核的实现,不应推断为接口约定。有关 DTrace 稳定性机制的更多信息,请参见稳定性

表 22–1 SDT 探测器

探测器名称 

说明 

arg0

callout-start

在执行 callout(请参见 <sys/callo.h>)之前的瞬间触发的探测器。Callout 由系统时钟定期执行,代表 timeout(9F) 的实现。

指向 callout_t(请参见 <sys/callo.h>)的指针,它对应于要执行的 callout。

callout-end

执行 callout(请参见 <sys/callo.h>)之后将立即触发的探测器。

指向 callout_t(请参见 <sys/callo.h>)的指针,它对应于已执行的 callout。

interrupt-start

在调入设备的中断处理程序之前的瞬间触发的探测器。 

指向 dev_info 结构(请参见 <sys/ddi_impldefs.h>)的指针,它对应于中断设备。

interrupt-complete

从设备的中断处理程序返回后将立即触发的探测器。 

指向 dev_info 结构(请参见 <sys/ddi_impldefs.h>)的指针,它对应于中断设备。

示例

以下示例是用于观察每秒钟内 callout 行为的脚本:

#pragma D option quiet

sdt:::callout-start
{
	@callouts[((callout_t *)arg0)->c_func] = count();
}

tick-1sec
{
	printa("%40a %10@d\n", @callouts);
	clear(@callouts);
}

运行此示例可以发现系统中经常使用 timeout(9F) 的用户,如以下输出所示:


# dtrace -s ./callout.d
                                    FUNC      COUNT
                            TS`ts_update          1
              uhci`uhci_cmd_timeout_hdlr          3
                          genunix`setrun          5
                     genunix`schedpaging          5
                         ata`ghd_timeout         10
 uhci`uhci_handle_root_hub_status_change        309

                                    FUNC      COUNT
              ip`tcp_time_wait_collector          1
                            TS`ts_update          1
              uhci`uhci_cmd_timeout_hdlr          3
                     genunix`schedpaging          4
                          genunix`setrun          8
                         ata`ghd_timeout         10
 uhci`uhci_handle_root_hub_status_change        300

                                    FUNC      COUNT
              ip`tcp_time_wait_collector          0
                        iprb`mii_portmon          1
                            TS`ts_update          1
              uhci`uhci_cmd_timeout_hdlr          3
                     genunix`schedpaging          4
                          genunix`setrun          7
                         ata`ghd_timeout         10
 uhci`uhci_handle_root_hub_status_change        300

timeout(9F) 接口仅生成单个计时器过期。需要时间间隔计时器功能的 timeout() 的使用者通常通过 timeout() 处理程序安装 timeout。以下示例显示了此行为:

#pragma D option quiet

sdt:::callout-start
{
	self->callout = ((callout_t *)arg0)->c_func;
}

fbt::timeout:entry
/self->callout && arg2 <= 100/
{
	/*
	 * In this case, we are most interested in interval timeout(9F)s that
	 * are short.  We therefore do a linear quantization from 0 ticks to
	 * 100 ticks.  The system clock's frequency — set by the variable
	 * "hz" — defaults to 100, so 100 system clock ticks is one second. 
	 */
	@callout[self->callout] = lquantize(arg2, 0, 100);
}

sdt:::callout-end
{
	self->callout = NULL;
}

END
{
	printa("%a\n%@d\n\n", @callout);
}

运行此脚本,等待几秒钟,然后按 Ctrl-C 组合键,将产生与以下示例类似的输出:


# dtrace -s ./interval.d
^C
genunix`schedpaging

           value  ------------- Distribution ------------- count    
              24 |                                         0        
              25 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 20       
              26 |                                         0        


ata`ghd_timeout

           value  ------------- Distribution ------------- count    
               9 |                                         0        
              10 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 51       
              11 |                                         0        


uhci`uhci_handle_root_hub_status_change

           value  ------------- Distribution ------------- count    
               0 |                                         0        
               1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1515     
               2 |                                         0

该输出说明,uhci(7D) 驱动程序中的 uhci_handle_root_hub_status_change() 代表系统中最短的时间间隔计时器:在每个系统时钟周期都将调用该计时器。

interrupt-start 探测器可用于了解中断活动。以下示例说明了如何按驱动程序名称量化执行中断处理程序花费的时间:

interrupt-start
{
	self->ts = vtimestamp;
}

interrupt-complete
/self->ts/
{
	this->devi = (struct dev_info *)arg0;
	@[stringof(`devnamesp[this->devi->devi_major].dn_name),
	    this->devi->devi_instance] = quantize(vtimestamp - self->ts);
}

运行此脚本将会生成与以下示例类似的输出:


# dtrace -s ./intr.d
dtrace: script './intr.d' matched 2 probes
^C
 isp                                                       0
           value  ------------- Distribution ------------- count    
            8192 |                                         0        
           16384 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 1        
           32768 |                                         0        

  pcf8584                                                   0
           value  ------------- Distribution ------------- count    
              64 |                                         0        
             128 |                                         2        
             256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@         157      
             512 |@@@@@@                                   31       
            1024 |                                         3        
            2048 |                                         0        

  pcf8584                                                   1
           value  ------------- Distribution ------------- count    
            2048 |                                         0        
            4096 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@          154      
            8192 |@@@@@@@                                  37       
           16384 |                                         2        
           32768 |                                         0        

  qlc                                                       0
           value  ------------- Distribution ------------- count    
           16384 |                                         0        
           32768 |@@                                       9        
           65536 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@      126      
          131072 |@                                        5        
          262144 |                                         2        
          524288 |                                         0        

  hme                                                       0
           value  ------------- Distribution ------------- count    
            1024 |                                         0        
            2048 |                                         6        
            4096 |                                         2        
            8192 |@@@@                                     89       
           16384 |@@@@@@@@@@@@@                            262      
           32768 |@                                        37       
           65536 |@@@@@@@                                  139      
          131072 |@@@@@@@@                                 161      
          262144 |@@@                                      73       
          524288 |                                         4        
         1048576 |                                         0        
         2097152 |                                         1        
         4194304 |                                         0        

  ohci                                                      0
           value  ------------- Distribution ------------- count    
            8192 |                                         0        
           16384 |                                         3        
           32768 |                                         1        
           65536 |@@@                                      143      
          131072 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@     1368     
          262144 |                                         0 

创建 SDT 探测器

如果您是设备驱动程序开发者,可能对在 Solaris 驱动程序中创建自己的 SDT 探测器感兴趣。禁用 SDT 的探测效果实际上是以不执行多个计算机指令为代价。所以,建议根据需要将 SDT 探测器添加到设备驱动程序中。如果这些探测器不对性能造成负面影响,则可以将其保留在提供的代码中。

声明探测器

使用 <sys/sdt.h> 中的 DTRACE_PROBEDTRACE_PROBE1DTRACE_PROBE2DTRACE_PROBE3DTRACE_PROBE4 宏声明 SDT 探测器。基于 SDT 探测器的模块名称和函数名称对应于探测器的内核模块和函数。探测器的名称取决于 DTRACE_PROBEn 宏中指定的名称。如果名称中不包含两个连续下条 (__),则探测器的名称与宏中指定的名称相同。如果名称中包含任何两个连续下条,则探测器名称会将该连续下条转换为一个破折号 (-)。例如,如果 DTRACE_PROBE 宏指定 transaction__start,则 SDT 探测器将被命名为 transaction-start。此替代允许 C 代码提供不是有效 C 标识符的宏名称,无需指定字符串。

DTrace 将内核模块名称和函数名称作为用于标识探测器的元组的一部分,因此无需在探测器名称中包括这些信息,以避免名称空间冲突。可以对驱动程序 module 使用命令 dtrace -l -P sdt -m module 来列出已安装的探测器和 DTrace 用户将可以看到的全名。

探测器参数

每个 SDT 探测器的参数都是在相应的 DTRACE_PROBEn 宏引用中指定的参数。参数的数量取决于用于创建该探测器的宏:DTRACE_PROBE1 指定一个参数,DTRACE_PROBE2 指定两个参数,依此类推。声明 SDT 探测器时,可通过不取消引用指针,并且不从探测器参数的全局变量中装入,来将已禁用的探测影响降至最低。在启用探测器的 D 操作中可以安全地取消引用指针和装入全局变量,因此 DTrace 用户可以只在需要这些操作时才请求这些操作。

稳定性

SDT 提供器使用 DTrace 的稳定性机制描述其稳定性,如下表所示。有关稳定性机制的更多信息,请参见第 39 章

元素 

名称稳定性 

数据稳定性 

相关性类 

提供器 

发展中 

发展中 

ISA

模块 

专用 

专用 

未知 

功能 

专用 

专用 

未知 

名称 

专用 

专用 

ISA

参数 

专用 

专用 

ISA