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).
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. |
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 |
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.
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.
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.
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 |