Handbuch zur dynamischen Ablaufverfolgung in Solaris

Formatierung der Ausgabe

Die Ablaufverfolgung von Systemaufrufen ist eine sehr wirkungsvolle Methode zur Beobachtung des Verhaltens der meisten Benutzerprozesse. Wenn Sie das Solaris-Dienstprogramm truss(1) als Administrator oder Entwickler bereits verwendet haben, wissen Sie wahrscheinlich, dass es im Problemfall immer einmal nützlich sein kann. Wenn Sie truss noch nie eingesetzt haben, sollten Sie es jetzt versuchen, indem Sie diesen Befehl in eine Ihrer Shells eingeben:


$ truss date

Das Ergebnis ist ein formatiertes Ablaufprotokoll aller von date(1) ausgeführten Systemaufrufe, gefolgt von einer einzeiligen Befehlsausgabe. Das folgende Beispiel ist eine im Hinblick auf die Formatierung der Ausgabe verbesserte Version des vorherigen Programms rw.d. Die an truss(1) angelehnte Ausgabe ist nun leichter verständlich. Geben Sie das folgende Programm ein und speichern Sie es unter dem Namen trussrw.d :


Beispiel 1–2 trussrw.d: Ablaufverfolgung von Systemaufrufen mit dem Ausgabeformat von truss(1)

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);
}

syscall::read:return,
syscall::write:return
/pid == $1/
{
	printf("\t\t = %d\n", arg1);
}

Hier wurde die Konstante 12345 in jedem Prädikat durch den Bezeichner $1 ersetzt. Über diesen Bezeichner kann der gewünschte Prozess dem Skript als Argument übergeben werden: $1 wird bei der Kompilierung des Skripts durch den Wert des ersten Arguments ersetzt. Zum Ausführen von trussrw.d verwenden Sie die dtrace-Optionen -q und -s, gefolgt von der Prozess-ID der Shell als abschließendes Argument. Die Option -q gibt an, dass dtrace mit minimaler Ausgabe ausgeführt werden und sowohl die Kopfzeile als auch die CPU- und ID-Spalten aus den vorangehenden Beispielen unterdrücken soll. Sie sehen also nur die Ausgabe für die Daten, die ausdrücklich verfolgt werden sollten. Geben Sie den folgenden Befehl ein (wobei Sie 12345 durch die Prozess-ID eines Shell-Prozesses ersetzen) und drücken Sie in der angegebenen Shell wiederholt die Eingabetaste:


# dtrace -q -s trussrw.d 12345
	                 = 1
write(2, 0x8089e48,    1)                = 1
read(63, 0x8090a38, 1024)                = 0
read(63, 0x8090a38, 1024)                = 0
write(2, 0x8089e48,   52)                = 52
read(0, 0x8089878,    1)                 = 1
write(2, 0x8089e48,    1)                = 1
read(63, 0x8090a38, 1024)                = 0
read(63, 0x8090a38, 1024)                = 0
write(2, 0x8089e48,   52)                = 52
read(0, 0x8089878,    1)                 = 1
write(2, 0x8089e48,    1)                = 1
read(63, 0x8090a38, 1024)                = 0
read(63, 0x8090a38, 1024)                = 0
write(2, 0x8089e48,   52)                = 52
read(0, 0x8089878,    1)^C
#

Nun werden wir uns das D-Programm und seine Ausgabe genauer betrachten. Zuerst wird mit einer Klausel wie im vorigen Programm jeder Aufruf von read(2) und write(2) der Shell instrumentiert. In diesem Beispiel verwenden wir jedoch die neue Funktion printf(), um Daten zu verfolgen und diese in einem bestimmten Format auszugeben:

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);
}

Die Funktion printf() bietet zwei Fähigkeiten in Einem: Ähnlich wie die bereits verwendete Funktion trace() verfolgt sie Daten und zusätzlich kann sie die Daten und anderen Text in einem spezifischen, von Ihnen beschriebenen Format ausgeben. Die Funktion printf() weist DTrace an, die jedem Argument nach dem ersten Argument zugehörigen Daten zu verfolgen und die Ergebnisse gemäß den mit dem ersten printf()-Argument, der Formatzeichenkette, beschriebenen Regeln auszugeben.

Die Formatzeichenkette ist eine normale Zeichenkette (string). Sie kann beliebig viele mit dem Zeichen % angeführte Formatumwandlungen enthalten, die beschreiben, wie das entsprechende Argument formatiert werden soll. Die erste Konvertierung in der Formatzeichenkette bezieht sich auf das zweite Argument von printf(), die zweite Konvertierung auf das dritte Argument und so weiter. Text zwischen den Umwandlungen wird wörtlich wiedergegeben. Das auf das %-Umwandlungszeichen folgende Zeichen beschreibt das für das entsprechende Argument zu verwendende Format. Die drei Formatumwandlungen in trussrw.d haben folgende Bedeutung:

%d

Der entsprechende Wert wird als Dezimalzahl ausgegeben. 

%s

Der entsprechende Wert wird als Zeichenkette ausgegeben. 

%x

Der entsprechende Wert wird als Hexadezimalzahl ausgegeben. 

DTrace printf() wirkt wie die C-Bibliotheksroutine printf(3C) bzw. das Shell-Dienstprogramm printf(1). Falls Ihnen·printf() neu ist, klärt Sie Kapitel 12Formatierung der Ausgabe im Detail über die Formate und Optionen auf. Lesen Sie dieses Kapitel aber auch dann aufmerksam durch, wenn Sie printf() bereits aus einer anderen Sprache kennen. printf() ist in D integriert und bietet Ihnen einige neue, speziell für DTrace entwickelte Formatumwandlungen.

Zur Unterstützung beim Schreiben fehlerfreier Programme prüft der D-Compiler jede printf()-Formatzeichenkette auf ihre Argumentliste. Ändern Sie probefunc in der obigen Klausel in die Ganzzahl 123 ab. Wenn Sie das abgeänderte Programm ausführen, erhalten Sie eine Fehlermeldung, die besagt, dass die String-Formatumwandlung %s nicht für ein Integer-Argument geeignet ist:


# dtrace -q -s trussrw.d
dtrace: failed to compile script trussrw.d: line 4: printf( )
	   argument #2 is incompatible with conversion #1 prototype:
	        conversion: %s
	         prototype: char [] or string (or use stringof)
	          argument: int
#

Für die Ausgabe der read- oder write-Systemaufrufe und ihrer Argumente verwenden Sie die Anweisung printf():

printf("%s(%d, 0x%x, %4d)", probefunc, arg0, arg1, arg2);

Damit werden der Name der aktuellen Prüfpunktfunktion und die ersten drei Integer-Argumente des Systemaufrufs, die in den DTrace-Variablen arg0, arg1 und arg2 zur Verfügung stehen, verfolgt. Weitere Informationen zu Prüfpunktargumenten finden Sie in Kapitel 3Variablen. Das erste Argument von read(2) und write(2) ist ein als Dezimalzahl dargestellter Dateideskriptor. Das zweite Argument ist eine als Hexadezimalwert formatierte Pufferadresse. Bei dem letzen Argument handelt es sich schließlich um die in Form eines Dezimalwerts wiedergegebene Puffergröße. Die Formatangabe %4d für das dritte Argument bedeutet, dass der Wert mit der Formatumwandlung %d und einer minimalen Feldbreite von 4 Zeichen dargestellt werden soll. Ist die Ganzzahl weniger als 4 Zeichen lang, fügt printf() zur Ausrichtung der Ausgabe zusätzliche Leerzeichen ein.

Um das Ergebnis des Systemaufrufs wiederzugeben und jede Ausgabezeile abzuschließen, verwenden Sie die folgende Klausel:

syscall::read:return,
syscall::write:return
/pid == $1/
{
	printf("\t\t = %d\n", arg1);
}

Beachten Sie, dass der Provider syscall außer entry für jeden Systemaufruf auch einen Prüfpunkt namens return veröffentlicht. Durch die DTrace-Variable arg1 für die syscall-Prüfpunkte return wird der Rückgabewert des Systemaufrufs eingesetzt. Der Rückgabewert wird als Dezimalzahl formatiert. Die in der Formatzeichenkette mit umgekehrten Schrägstrichen beginnenden Zeichenfolgen erstrecken sich bis zum Tabulator (\t) bzw. zur neuen Zeile (\n). Diese „Escape-Folgen“ erleichtern die Wiedergabe oder Aufzeichnung schwer darzustellender Zeichen. D unterstützt denselben Ersatzdarstellungssatz wie C, C++ und Java. Eine vollständige Liste der Escape-Sequenzen finden Sie in Kapitel 2Typen, Operatoren und Ausdrücke.