Handbuch zur dynamischen Ablaufverfolgung in Solaris

Strukturen

Das D-Schlüsselwort struct, Abkürzung für structure (Struktur), dient zur Einführung eines neuen, aus einer Gruppe anderer Typen zusammengesetzten Typs. Der neue Struct-Typ kann als Typ für D-Variablen und Vektoren verwendet werden, sodass Sie Gruppen zusammenhängender Variablen unter einem einzigen Namen gruppieren können. D-Strukturen sind mit den entsprechenden Konstrukten in C und C++ identisch. An Java gewöhnte Programmierer sollten sich eine D-Struktur wie eine Klasse (class) mit Datenmitgliedern, aber ohne Methoden vorstellen.

Nehmen wir an, es soll ein anspruchsvolleres D-Programm für die Ablaufverfolgung von Systemaufrufen geschrieben werden, das verschiedene Informationen über jeden von der Shell ausgeführten read(2) und write(2) Systemaufruf aufzeichnet. Bei diesen Informationen kann es sich um die verstrichene Zeit, die Anzahl der Aufrufe oder die größte als Argument übergebene Byteanzahl handeln. Sie könnten, wie in folgendem Beispiel, eine D-Klausel zum Aufzeichnen dieser Eigenschaften in drei separaten assoziativen Vektoren schreiben:

syscall::read:entry, syscall::write:entry
/pid == 12345/
{
	ts[probefunc] = timestamp;
	calls[probefunc]++;
	maxbytes[probefunc] = arg2 > maxbytes[probefunc] ?
	    arg2 : maxbytes[probefunc];
}

Diese Klausel ist jedoch nicht effizient, da DTrace drei separate assoziative Vektoren erzeugen und für jeden eine separate Kopie der identischen Tupelwerte von probefunc speichern muss. Wenn Sie stattdessen eine Struktur verwenden, können Sie nicht nur Platz sparen, sondern erhalten auch ein Programm, das sich leichter lesen und pflegen lässt. Deklarieren Sie zu Beginn der Programmquelldatei zuerst einen neuen Struct-Typ:

struct callinfo {
	uint64_t ts;      /* timestamp of last syscall entry */
	uint64_t elapsed; /* total elapsed time in nanoseconds */
	uint64_t calls;   /* number of calls made */
	size_t maxbytes;  /* maximum byte count argument */
};

Auf das Schlüsselwort struct folgt ein optionaler Bezeichner zum Verweis auf unseren neuen Typ, der nun als struct callinfo bekannt ist. Anschließend sind die Komponenten der Struktur in geschweiften Klammern { } aufgeführt und die gesamte Deklaration endet mit einem Strichpunkt (; ). Die Definition der einzelnen Strukturkomponenten (members) erfolgt in der Syntax für D-Variablendeklarationen. Dabei steht zuerst der Typ der Komponente, gefolgt von einem Bezeichner, der die Komponente benennt, und einem weiteren Strichpunkt (;).

Die Strukturdeklaration selbst definiert einfach den neuen Typ; sie erzeugt weder eine Variable noch reserviert sie Speicherplatz in DTrace. Nach einmaliger Deklaration können Sie struct callinfo im gesamten Rest des D-Programms als Typ verwenden, und jede Variable des Typs struct callinfo speichert eine Kopie der vier mit unserer Strukturvorlage beschriebenen Variablen. Die Komponenten werden im Speicher gemäß der Komponentenliste und mit dem ggf. für die Datenobjektausrichtung benötigten Zwischenraum zwischen den einzelnen Komponenten angeordnet.

Für den Zugriff auf die einzelnen Komponentenwerte mit dem Operator „.” können Sie die Komponentennamen verwenden. Schreiben Sie hierzu einen Ausdruck in der Form:

Variablenname.Komponentenname

Das folgende Beispiel zeigt ein verbessertes Programm mit dem neuen Strukturtyp. Geben Sie dieses D-Programm in einen Texteditor ein und speichern Sie es unter dem Namen rwinfo.d:


Beispiel 7–1 rwinfo.d: : Erfassen statistischer Daten über read(2) und write(2)

struct callinfo {
	uint64_t ts;      /* timestamp of last syscall entry */
	uint64_t elapsed; /* total elapsed time in nanoseconds */
	uint64_t calls;   /* number of calls made */
	size_t maxbytes;  /* maximum byte count argument */
};

struct callinfo i[string];	/* declare i as an associative array */

syscall::read:entry, syscall::write:entry
/pid == $1/
{
	i[probefunc].ts = timestamp;
	i[probefunc].calls++;
	i[probefunc].maxbytes = arg2 > i[probefunc].maxbytes ?
		arg2 : i[probefunc].maxbytes;
}

syscall::read:return, syscall::write:return
/i[probefunc].ts != 0 && pid == $1/
{
	i[probefunc].elapsed += timestamp - i[probefunc].ts;
}

END
{
	printf("        calls  max bytes  elapsed nsecs\n");
	printf("------  -----  ---------  -------------\n");
	printf("  read  %5d  %9d  %d\n",
	    i["read"].calls, i["read"].maxbytes, i["read"].elapsed);
	printf(" write  %5d  %9d  %d\n",
	    i["write"].calls, i["write"].maxbytes, i["write"].elapsed);
}

Wenn Sie das Programm eingegeben haben, führen Sie dtrace -q -s rwinfo.d aus und geben dabei einen Ihrer Shell-Prozesse an. Geben Sie dann den ein oder anderen Befehl in die Shell ein und drücken Sie abschließend im dtrace-Terminal die Tastenkombination Strg-C, um den Prüfpunkt END auszulösen und die Ergebnisse anzeigen zu lassen:


# dtrace -q -s rwinfo.d `pgrep -n ksh`
^C
        calls  max bytes  elapsed nsecs
------  -----  ---------  -------------
  read     36       1024  3588283144
 write     35         59  14945541
#