Handbuch zur dynamischen Ablaufverfolgung in Solaris

Kapitel 22 Der Provider sdt

Der SDT-Provider (Statically Defined Tracing) erzeugt Prüfpunkte an Positionen, die vom Programmierer formal bestimmt werden. Der SDT-Mechanismus ermöglicht es Programmierern, den DTrace-Benutzern bewusst bestimmte Positionen anzubieten und ihnen mit dem Prüfpunktnamen semantische Kenntnisse über die einzelnen Positionen zu vermitteln. Im Solaris-Kernel sind eine Handvoll SDT-Prüfpunkte definiert, die voraussichtlich weiter zunehmen werden. DTrace stellt darüber hinaus·für Entwickler von Benutzeranwendungen einen Mechanismus zur Definition statischer Prüfpunkte bereit (siehe Kapitel 34Statisch definierte Ablaufverfolgung für Benutzeranwendungen).

Prüfpunkte

Die vom Solaris-Kernel definierten SDT-Prüfpunkte sind in Tabelle 22–1 aufgeführt. Die Namensstabilität und Datenstabilität dieser Prüfpunkte ist „Private“. Ihre Beschreibung spiegelt damit die Kernel-Implementierung wider und sollte nicht als Schnittstellenbindung gedeutet werden. Weitere Informationen zum DTrace-Stabilitätsmechanismus finden Sie unter Stabilität.

Tabelle 22–1 SDT-Prüfpunkte

Prüfpunktname 

Beschreibung 

arg0

callout-start

Prüfpunkt, der unmittelbar vor der Ausführung eines Callouts ausgelöst wird (siehe <sys/callo.h>). Die Zeitüberschreitungen des Typs Callout werden durch regelmäßigen Systemtakt ausgeführt und stellen die Implementierung von timeout(9F) dar.

Zeiger auf callout_t (siehe <sys/callo.h>) für den auszuführenden Callout.

callout-end

Prüfpunkt, der unmittelbar nach der Ausführung eines Callouts ausgelöst wird (siehe <sys/callo.h>).

Zeiger auf callout_t (siehe <sys/callo.h>) für den gerade ausgeführten Callout.

interrupt-start

Prüfpunkt, der unmittelbar vor dem Aufruf in eine Interrupt-Behandlungsroutine eines Geräts ausgelöst wird. 

Zeiger auf die Struktur dev_info (siehe <sys/ddi_impldefs.h>) für das unterbrechende Gerät.

interrupt-complete

Prüfpunkt, der unmittelbar nach der Rückkehr von der Interrupt-Behandlungsroutine eines Geräts ausgelöst wird. 

Zeiger auf die Struktur dev_info (siehe <sys/ddi_impldefs.h>) für das unterbrechende Gerät.

Beispiele

Das folgende Beispiel ist ein Skript zur Beobachtung des Callout-Verhaltens auf Sekundenbasis:

#pragma D option quiet

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

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

Dieses Beispielskript deckt die häufigen Benutzer von timeout(9F) im System auf, wie die folgende Ausgabe zeigt:


# 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

Die Schnittstelle timeout(9F) erzeugt nur einen einzelnen Timer-Ablauf. Verbraucher von timeout(), die eine intervallbasierte Timer-Funktion benötigen, installieren in der Regel das Timeout ihrer timeout()-Behandlungsroutine neu. Das folgende Beispiel demonstriert dieses Verhalten:

#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);
}

Wenn Sie dieses Skript ausführen, einige Sekunden warten und dann Strg-C eingeben, erhalten Sie eine ähnliche Ausgabe wie diese:


# 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

Die Ausgabe zeigt, dass uhci_handle_root_hub_status_change() im Treiber uhci(7D) den Timer mit dem kürzesten Intervall auf dem System darstellt: Er wird mit jedem Systemuhr-Tick aufgerufen.

Mit dem Prüfpunkt interrupt-start lässt sich die Interrupt-Aktivität nachvollziehen. Das folgende Beispiel zeigt, wie die für die Ausführung einer Interrupt-Behandlungsroutine benötigte Zeit je Treibername quantisiert werden kann:

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);
}

Die Ausführung dieses Skripts erzeugt eine Ausgabe wie in folgendem Beispiel:


# 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 

Erstellen von SDT-Prüfpunkten

Entwickler von Gerätetreibern interessiert es wahrscheinlich, wie man eigene SDT-Prüfpunkte im Solaris-Treiber erstellen kann. Die ausgeschaltete Prüfaktivität von SDT ist im Wesentlichen der Preis für verschiedene NOP-Maschinenanweisungen. Sie können Ihren Gerätetreibern deshalb ganz nach Bedarf neue SDT-Prüfpunkte hinzufügen. Sofern sie die Leistung nicht negativ beeinflussen, besteht kein Grund, die Prüfpunkte nicht im Auslieferungszustand des Codes beizubehalten.

Deklarieren von Prüfpunkten

SDT-Prüfpunkte werden mit den Makros DTRACE_PROBE, DTRACE_PROBE1, DTRACE_PROBE2, DTRACE_PROBE3 und DTRACE_PROBE4 aus <sys/sdt.h> deklariert. Der Modulname und Funktionsname eines auf SDT basierenden Prüfpunkts entspricht dem Kernelmodul und der Funktion des Prüfpunkts. Der Name des Prüfpunkts ist von dem Namen in der Makro DTRACE_PROBEn abhängig. Wenn der Name keine zwei aufeinander folgenden Unterstriche (__) enthält, ist der Name des Prüfpunkts mit der Schreibweise in der Makro identisch. Enthält er jedoch zwei aufeinander folgende Unterstriche, werden diese im Prüfpunktnamen in einen Gedankenstrich ( -) umgewandelt. Wenn beispielsweise eine DTRACE_PROBE-Makro transaction__start angibt, heißt der SDT-Prüfpunkt transaction-start. Durch diese Ersetzung kann C-Code Makronamen bereitstellen, die keine gültigen C-Bezeichner sind, ohne eine Zeichenkette anzugeben.

DTrace nimmt den Kernelmodul- und den Funktionsnamen bereits als Bestandteil des Tupels auf, das einen Prüfpunkt bezeichnet. Sie brauchen diese Angaben also nicht mehr in den Prüfpunktnamen einzufügen, um Namensraumkollisionen zu vermeiden. Sie können den Befehl dtrace -l -P sdt -m Modul auf das Treibermodul anwenden, um die installierten Prüfpunkte sowie die für DTrace-Benutzer sichtbaren vollständigen Namen aufzulisten.

Prüfpunktargumente

Die Argumente der einzelnen SDT-Prüfpunkte sind die im entsprechenden DTRACE_PROBEn-Makroverweis angegebenen Argumente. Die Anzahl der Argumente hängt davon ab, welche Makro zum Erstellen des Prüfpunkts verwendet wurde: DTRACE_PROBE1 gibt ein Argument an, DTRACE_PROBE2 zwei usw. Beim Deklarieren der SDT-Prüfpunkte können Sie ihre ausgeschaltete Prüftätigkeit auf ein Minimum herabsetzen, indem Sie keine Zeiger dereferenzieren und keine Daten aus globalen Variablen in den Prüfpunktargumenten laden. Sowohl die Zeigerdereferenzierung als auch das Laden aus globalen Variablen kann über D-Aktionen, die Prüfpunkte aktivieren, sicher vorgenommen werden. Auf diese Weise können DTrace-Benutzer diese Aktionen nur dann anfordern, wenn sie benötigt werden.

Stabilität

Der Provider SDT beschreibt die verschiedenen Stabilitäten anhand des DTrace-Stabilitätsmechanismus gemäß der folgenden Tabelle. Weitere Informationen zum Stabilitätsmechanismus finden Sie in Kapitel 39Stabilität.

Element 

Namensstabilität 

Datenstabilität 

Abhängigkeitsklasse 

Provider 

Evolving 

Evolving 

ISA

Modul 

Private 

Private 

Unknown 

Funktion 

Private 

Private 

Unknown 

Name 

Private 

Private 

ISA

Argumente 

Private 

Private 

ISA