Handbuch zur dynamischen Ablaufverfolgung in Solaris

Vektoren

D bietet Ihnen die Möglichkeit, Variablen des Typs Integer und anderer Typen zur Darstellung von Zeichenketten sowie gemischte Typen zu definieren, die als structs (Strukturen) und unions (Unionen) bezeichnet werden. Wenn Sie sich mit der C-Programmierung auskennen, wird es Sie freuen, dass Sie in D dieselben Typen wie in C verwenden können. Wenn Sie kein C-Experte sind, gibt es trotzdem keinen Grund zur Besorgnis: Sämtliche verschiedenen Datentypen werden in Kapitel 2Typen, Operatoren und Ausdrücke erklärt. Außerdem unterstützt D eine besondere Variablenart, den so genannten assoziativen Vektor. Ein assoziativer Vektor gleicht einem einfachen Vektor (Array) insofern, als er Schlüsseln Werte zuordnet. In einem assoziativen Vektor sind die Schlüssel jedoch nicht auf Ganzzahlen eines festgelegten Wertebereichs beschränkt.

Assoziative Vektoren in D lassen sich durch eine Liste von einem oder mehreren Werten beliebigen Typs indizieren. Die Schlüsselwerte bilden zusammen ein Tupel, das zur Indizierung im Vektor und zum Zugriff auf den oder Ändern des Werts des entsprechenden Schlüssels eingesetzt wird. Jedes für einen bestimmten assoziativen Vektor verwendetes Tupel muss derselben Typensignatur entsprechen. Das heißt, dass jedes Tupel dieselbe Länge und dieselben Schlüsseltypen in derselben Reihenfolge aufweisen muss. Außerdem sind die den Elementen eines gegebenen assoziativen Vektors zugeordneten Werte für den gesamten Vektor auf einen einzigen Typ festgelegt. Die folgende D-Anweisung definiert beispielsweise einen neuen assoziativen Vektor a des Wertetyps int mit der Tupelsignatur [ string, int ] und speichert den ganzzahligen Wert 456 im Vektor:

a["hello", 123] = 456;

Auf die Elemente eines definierten Vektors kann wie auf jede andere D-Variable zugegriffen werden. So ändern wir beispielsweise mit der folgenden D-Anweisung das zuvor in a gespeicherte Vektorelement, indem wir den Wert von 456 auf 457 erhöhen:

a["hello", 123]++;

Die Werte aller noch nicht zugewiesenen Vektorelemente werden auf Null gesetzt. Setzen wir nun einen assoziativen Vektor in ein D-Programm ein. Geben Sie das folgende Programm ein und speichern Sie es unter dem Namen rwtime.d:


Beispiel 1–3 rwtime.d: Zeit für·read(2)- und write(2)-Aufrufe

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

Geben Sie bei der Ausführung von rwtime.d wie schon bei trussrw.d die ID des Shell-Prozesses an. Wenn Sie einige Shell-Befehle eingeben, sehen Sie die Dauer jedes Systemaufrufs. Geben Sie folgenden Befehl ein und drücken Sie anschließend in der anderen Shell mehrmals die Eingabetaste:


# dtrace -s rwtime.d `pgrep -n ksh`
dtrace: script 'rwtime.d' matched 4 probes
CPU     ID                    FUNCTION:NAME
  0     33                      read:return 22644 nsecs
  0     33                      read:return 3382 nsecs
  0     35                     write:return 25952 nsecs
  0     33                      read:return 916875239 nsecs
  0     35                     write:return 27320 nsecs
  0     33                      read:return 9022 nsecs
  0     33                      read:return 3776 nsecs
  0     35                     write:return 17164 nsecs
...
^C
#

Um ein Protokoll der für jeden Systemaufruf abgelaufenen Zeit zu erhalten, müssen Sie sowohl den Eintritt in als auch die Rückkehr aus read(2) und write(2) instrumentieren und die Zeit an jedem Punkt prüfen. Anschließend ist bei der Rückkehr aus einem gegebenen Systemaufruf die Differenz zwischen der ersten und der zweiten Zeitmarke zu berechnen. Hierbei könnten Sie für jeden Systemaufruf eine eigene Variable verwenden, doch das würde die Erweiterung des Programms um zusätzliche Systemaufrufe sehr beschwerlich machen. Einfacher ist es, stattdessen auf einen durch den Namen der Prüfpunktfunktion indizierten assoziativen Vektor zurückzugreifen. Sehen Sie hier die erste Prüfpunktklausel:

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

Diese Klausel definiert einen Vektor namens ts und weist dem entsprechenden Element den Wert der DTrace-Variable timestampzu. Diese Variable gibt den Wert eines stets anwachsenden Nanosekundenzählers zurück, ähnlich der Solaris-Bibliotheksroutinegethrtime(3C). Wenn die Zeitmarke des Eintritts gespeichert ist, wird timestamp erneut durch den Prüfpunkt für die Rückkehr geprüft, der dann die Differenz zwischen der aktuellen Uhrzeit und dem gespeicherten Wert meldet:

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

Das Prädikat des return-Prüfpunkts setzt voraus, dass DTrace den betreffenden Prozess verfolgt und der entsprechende entry-Prüfpunkt bereits ausgelöst wurde und ts[probefunc] einen Wert ungleich Null zugewiesen hat. Durch diesen Trick lässt sich eine ungültige Ausgabe beim Start von DTrace vermeiden. Wartet die Shell, wenn Sie dtrace ausführen, bereits in einem read(2)-Systemaufruf auf eine Eingabe, wird der Prüfpunkt read:return ohne einen vorangehenden read:entry für diesen ersten read(2)-Aufruf ausgelöst, und die Prüfung von ts[probefunc] ergibt Null, da noch keine Zuweisung stattgefunden hat.