Handbuch zur dynamischen Ablaufverfolgung in Solaris

Kapitel 3 Variablen

D bietet zwei einfache Variablentypen, die Sie in Ihren Tracing-Programmen verwenden können: skalare Variablen und assoziative Vektoren. In den Beispielen in·Kapitel 1 demonstrierten wir bereits kurz die Verwendung dieser Variablen. In diesem Kapitel werden die Regeln zur Verwendung von D-Variablen und die ·Zuweisung von Variablen zu verschiedenen Geltungsbereichen näher erläutert. Eine Sonderform der Vektorvariable, das Aggregat, wird in Kapitel 9Aggregate beschrieben.

Skalare Variablen

Mit skalaren Variablen werden einzelne Datenobjekte fester Größe wie zum Beispiel Ganzzahlen und Zeiger dargestellt. Skalare Variablen können auch Objekte fester Größe aufnehmen, die sich aus einem oder mehreren Grund- oder Verbundtypen zusammensetzen. D bietet die Möglichkeit, sowohl Vektoren von Objekten als auch zusammengesetzte Strukturen zu erstellen. DTrace stellt auch Zeichenketten als skalare Variablen fester Größe dar, indem sie die Möglichkeit erhalten, bis zu einer vordefinierten maximalen Länge anzuwachsen. Wie Sie auf die Zeichenkettenlänge in Ihren D-Programmen Einfluss nehmen, wird in Kapitel 6Zeichenketten näher besprochen.

Skalare Variablen werden automatisch erzeugt, wenn Sie einem zuvor nicht definierten Bezeichner im D-Programm zum ersten Mal einen Wert zuweisen. Um beispielsweise eine skalare Variable x des Typs int zu erzeugen, brauchen Sie ihr lediglich in einer beliebigen Prüfpunktklausel einen Wert des Typs int zuzuweisen:

BEGIN
{
	x = 123;
}

Auf diese Weise erzeugte skalare Variablen sind globaler Natur: Ihr Name und ihre Datenspeicherposition werden einmal definiert und sind in jeder Klausel des D-Programms sichtbar. Mit jeder Referenzierung des Bezeichners x verweisen Sie auf eine bestimmte Speicherposition, die dieser Variable zugewiesen wurde.

Im Gegensatz zu ANSI-C erfordert D keine explizite Variablendeklaration. Wenn Sie eine globale Variable dennoch deklarieren möchten, um ihr vor Gebrauch explizit einen Namen und Typ zuzuweisen, können Sie die Deklaration, wie in nachfolgendem Beispiel dargestellt, außerhalb der Prüfpunktklauseln in das Programm einfügen. Explizite Variablendeklarationen sind in den meisten D-Programmen nicht erforderlich, können sich aber dann als hilfreich erweisen, wenn Sie es vorziehen, die Variablentypen genau unter Kontrolle zu halten oder Ihre Programm mit Deklarationen und Kommentaren beginnen zu lassen, die Ihre Programmvariablen und ihre Bedeutung dokumentieren.

int x; /* declare an integer x for later use */

BEGIN
{
	x = 123;
	...
}

Im Gegensatz zu ANSI-C-Deklarationen dürfen Variablendeklarationen in D keine Anfangswerte zuweisen. Für die Zuweisung von Anfangswerten müssen Sie eine BEGIN-Prüfpunktklausel verwenden. Der gesamte Speicher der globalen Variable wird von DTrace mit Nullen aufgefüllt, bevor Sie die Variable zum ersten Mal referenzieren.

In der Definition der Programmiersprache D besteht keine Beschränkungen für die Größe und Anzahl von D-Variablen. Es sind jedoch Beschränkungen durch die DTrace-Implementierung und die auf dem System verfügbare Speicherkapazität gesetzt. Wenn Sie das Programm kompilieren, werden vom D-Compiler alle anwendbaren Beschränkungen in Kraft gesetzt. In Kapitel 16Optionen und Tunables erfahren Sie, wie Sie Optionen in Verbindung mit den Programmbeschränkungen abstimmen können.

Assoziative Vektoren

Assoziative Vektoren dienen zum Darstellen von Sammlungen von Datenelementen, die durch Angabe eines Namens, des Schlüssels, abgerufen werden können. In D werden die Schlüssel assoziativer Vektoren in Form von Listen skalarer Ausdruckswerte namens Tupel gebildet. Das Vektortupel selbst können Sie sich als eine Parameterliste für eine Funktion vorstellen, die zur Ermittlung des entsprechenden Vektorwerts aufgerufen wird, wenn Sie den Vektor referenzieren. Jeder assoziative Vektor in D besitzt eine feststehende Schlüsselsignatur, die aus einer festen Anzahl von Tupelelementen besteht. Dabei ist jedem Element ein fester Typ zugeordnet. Sie können für jeden Vektor in einem D-Programm unterschiedliche Schlüsselsignaturen definieren.

Assoziative Vektoren unterscheiden sich von normalen Vektoren mit fester Größe dadurch, dass sie keine vordefinierte Mengenbeschränkung für Elemente haben, die Elemente durch ein beliebiges Tupel indiziert werden können, anstatt dass nur Ganzzahlen als Schlüssel eingesetzt werden, und schließlich dadurch, dass die Elemente nicht an vorreservierten, aufeinander folgenden Speicherpositionen gespeichert werden. Assoziative Vektoren sind dann hilfreich, wenn Sie in einem Programm in C, C++ oder JavaTM eine Hash-Tabelle oder andere einfache Dictionary-Datenstrukturen einsetzen würden. Mit assoziativen Vektoren können Sie ein dynamisches Protokoll der innerhalb des D-Programms erfassten Ereignisse und Statusinformationen erstellen, auf dessen Grundlage sich komplexere Kontrollstrukturen bilden lassen.

Zum Definieren eines assoziativen Vektors schreiben Sie einen Zuweisungsausdruck der Form:

Name [ Schlüssel ] = Ausdruck ;

dabei ist Name ein beliebiger, gültiger D-Bezeichner und Schlüssel ist eine Liste eines oder mehrerer durch Komma getrennter Ausdrücke. Beispielsweise definiert die folgende Anweisung einen assoziativen Vektor a mit der Schlüsselsignatur [ int, string ] und speichert den ganzzahligen Wert 456 an einer nach dem Tupel benannten Position [ 123, "hello" ]:

a[123, "hello"] = 456;

Auch der Objekttyp ist für sämtliche Elemente in einem Vektor festgelegt. Da a zuerst die Ganzzahl 456 zugewiesen wurde, erhält auch jeder weitere in diesem Vektor gespeicherte Wert den Typ int. Zum Ändern der Elemente in assoziativen Vektoren stehen Ihnen alle in Kapitel 2 definierten Zuweisungsoperatoren zur Verfügung. Dabei sind die für die einzelnen Operatoren genannten Operandenregeln zu beachten. Bei unverträglichen Zuweisungsversuchen gibt der D-Compiler eine entsprechende Fehlermeldung aus. Für die Schlüssel oder Werte assoziativer Vektoren können alle Typen verwendet werden, die auch für skalare Variablen zulässig sind. Es ist nicht möglich, einen assoziativen Vektor als Schlüssel oder Wert in einen anderen assoziativen Vektor zu schachteln.

Ein assoziativer Vektor kann mit jedem mit der Vektorschlüsselsignatur verträglichen Tupel referenziert werden. Die Regeln für die Tupelkompatibilität stimmen mit jenen für Funktionsaufrufe und Variablenzuweisungen überein: Das Tupel muss dieselbe Länge haben, und jeder Typ in der Liste der tatsächlichen Parameter muss mit dem entsprechenden Typ in der formalen Schlüsselsignatur verträglich sein. Wenn zum Beispiel ein assoziativer Vektor x wie folgt definiert wird:

x[123ull] = 0;

dann erhält die Schlüsselsignatur den Typ unsigned long long und die Werte nehmen den Typ int an. Dieser Vektor kann auch mit dem Ausdruck x['a'] referenziert werden, da das aus der Zeichenkonstante 'a' bestehende Tupel des Typs int und der Länge 1 gemäß den unter·Typumwandlungen beschriebenen arithmetischen Umwandlungsregeln mit der Schlüsselsignatur unsigned long long verträglich ist.

Wenn Sie einen assoziativen Vektor in D vor der Verwendung explizit deklarieren müssen, können Sie in den Programmquellcode die Deklaration des Vektornamens und der Schlüsselsignatur außerhalb der Prüfpunktklauseln einfügen.

int x[unsigned long long, char];

BEGIN
{
	x[123ull, 'a'] = 456;
}

Nach der Definition eines assoziativen Vektors sind Referenzen auf ein beliebiges Tupel mit verträglicher Schlüsselsignatur möglich, selbst dann, wenn noch keine Zuweisung für das betreffende Tupel erfolgt ist. Der Zugriff auf ein Element eines assoziativen Vektors ohne Zuweisung gibt nach Definition ein mit Nullen angefülltes Objekt zurück. Eine Folge dieser Definition ist, dass für das Element des assoziativen Vektors erst nach Zuweisung eines Werts ungleich Null Speicher reserviert wird. Umgekehrt hebt DTrace die Speicherzuweisung auf, wenn einem Element eines assoziativen Vektors der Wert Null zugewiesen wird. Dieses Verhalten ist deshalb wichtig, weil der dynamische Variablenbereich, aus dem die Elemente assoziativer Vektoren zugewiesen werden, endlich ist. Sollte dieser Bereich bei einem Zuweisungsversuch bereits erschöpft sein, schlägt die Zuweisung mit einer Fehlermeldung über eine dynamische Variablenauslassung fehl. Weisen Sie Elementen assoziativer Vektoren, die nicht mehr benötigt werden, immer den Wert Null zu. Weitere Verfahren zur Eliminierung der dynamischen Übergabe von Variablen finden Sie in Kapitel 16Optionen und Tunables .

Thread-lokale Variablen

DTrace bietet die Möglichkeit, im Gegensatz zu den zuvor in diesem Kapitel besprochenen globalen Variablen auch Variablen mit einem Speicherbereich im jeweiligen Betriebssystem-Thread (lokal) zu deklarieren. Thread-lokale Variablen erweisen sich als nützlich, wenn Sie einen Prüfpunkt aktivieren und jeden den Prüfpunkt auslösenden Thread mit einem Etikett oder auf andere Weise markieren möchten. Ein solches Programm zu schreiben, ist in D eine einfache Angelegenheit, denn thread-lokale Variablen haben im D-Code einen gemeinsamen Namen, beziehen sich aber auf unterschiedliche Datenspeicherbereiche der verschiedenen Threads. Thread-lokale Variablen werden durch Anwendung des Operators -> auf den speziellen Bezeichner self referenziert:

syscall::read:entry
{
	self->read = 1;
}

In diesem D-Beispielfragment wird der Prüfpunkt am read(2)-Systemaufruf aktiviert und jedem Thread, der den Prüfpunkt auslöst, wird die thread-lokale Variable read zugewiesen. Ebenso wie globale Variablen werden auch thread-lokale Variablen bei ihrer ersten Zuweisung automatisch erzeugt und nehmen den auf der rechten Seite der ersten Zuweisungsanweisung verwendeten Typ an (in diesem Beispiel int).

Mit jeder Referenzierung der Variable self->read in Ihrem D-Programm wird das Datenobjekt referenziert, das dem Betriebssystem-Thread angehört, der zum Zeitpunkt der Auslösung des entsprechenden DTrace-Prüfpunkts ausgeführt wurde. Eine thread-lokale Variable kann man sich als einen assoziativen Vektor vorstellen, der durch ein die Identität des Threads im System beschreibendes Tupel implizit indiziert wird. Threads haben über die gesamte Lebensdauer des Systems eine eindeutige Identität: Wenn der Thread beendet wird und aufgrund derselben Betriebssystem-Datenstruktur ein weiterer Thread erzeugt wird, erhält dieser in DTrace nicht dieselbe thread-lokale Speicheridentität.

Nachdem Sie eine thread-lokale Variable definiert haben, können Sie diese für jeden beliebigen Thread im System referenzieren, selbst wenn die betreffende Variable für einen bestimmten Thread noch keine Zuweisung erhalten hat. Der Datenspeicherbereich für eine Kopie der thread-lokalen Variable im Thread wird gemäß der Definition mit Nullen angefüllt. Ebenso wie bei den Elementen assoziativer Vektoren erfolgt die Speicherreservierung für eine thread-lokale Variable erst, wenn dieser ein Wert ungleich Null zugewiesen wird. Ebenso analog zu den Elementen assoziativer Vektoren bewirkt die Zuweisung mit Null einer thread-lokalen Variable, dass DTrace die Zuweisung des Speicherbereichs aufhebt. Weisen Sie thread-lokalen Variablen, die nicht mehr benötigt werden, immer den Wert Null zu. In Kapitel 16Optionen und Tunables sind weitere Techniken zur Feinabstimmung des dynamischen Variablenbereichs beschrieben, aus dem thread-lokale Variablen allokiert werden.

Sie können in einem D-Programm thread-lokale Variablen jedes beliebigen Typs, einschließlich assoziativer Vektoren, definieren. Sehen Sie hier einige Beispieldefinitionen für thread-lokale Variablen:

self->x = 123;              /* integer value */
self->s = "hello";	          /* string value */
self->a[123, 'a'] = 456;    /* associative array */

Wie jede D-Variable müssen auch thread-lokale Variablen vor ihrer Verwendung nicht explizit deklariert werden. Wenn Sie dies trotzdem wünschen, bauen Sie die Deklaration außerhalb der Programmklauseln ein und stellen Sie ihnen das Schlüsselwort self voran:

self int x;    /* declare int x as a thread-local variable */

syscall::read:entry
{
	self->x = 123;
}

Thread-lokale Variablen werden von globalen Variablen getrennt in separaten Namensräumen geführt. Folglich können die Namen mehrmals verwendet werden. Denken Sie daran, dass es sich bei x und self->x nicht um dieselbe Variable handelt, wenn Sie Namen in Ihrem Programm mehrmals verwenden! Das folgende Beispiel verdeutlicht die Verwendung von thread-lokalen Variablen. Geben Sie das folgende Programm in einen Texteditor ein und speichern Sie es unter dem Namen rtime.d:


Beispiel 3–1 rtime.d: Berechnen der in read(2) abgelaufenen Zeit

syscall::read:entry
{
	self->t = timestamp;
}

syscall::read:return
/self->t != 0/
{
	printf("%d/%d spent %d nsecs in read(2)\n",
	    pid, tid, timestamp - self->t);
	
	/*
	 * We're done with this thread-local variable; assign zero to it to
	 * allow the DTrace runtime to reclaim the underlying storage.
	 */
	self->t = 0;
}

Wechseln Sie nun zu Ihrer Shell und starten Sie die Programmausführung. Nach einigen Sekunden sollte die Ausgabe beginnen. Wenn keine Ausgabe erscheint, geben Sie den ein oder anderen Befehl ein.


# dtrace -q -s rtime.d
100480/1 spent 11898 nsecs in read(2)
100441/1 spent 6742 nsecs in read(2)
100480/1 spent 4619 nsecs in read(2)
100452/1 spent 19560 nsecs in read(2)
100452/1 spent 3648 nsecs in read(2)
100441/1 spent 6645 nsecs in read(2)
100452/1 spent 5168 nsecs in read(2)
100452/1 spent 20329 nsecs in read(2)
100452/1 spent 3596 nsecs in read(2)
...
^C
#

In rtime.d wird mit der thread-lokalen Variable t beim Eintritt in read(2) durch einen beliebigen Thread eine Zeitmarke erfasst. In der Rückkehrklausel gibt das Programm dann die in read(2); verbrachte Zeit aus, die es ermittelt, indem es self->t von der aktuellen Zeitmarke subtrahiert. Die integrierten D-Variablen pid und tid melden die Prozess-ID und Thread-ID des Threads, der read(2) durchführt. Da self->t nach der Ausgabe dieser Information nicht mehr benötigt wird, wird ihr anschließend der Wert 0 zugewiesen, um DTrace die Möglichkeit zu geben, den Speicherbereich von t für den aktuellen Thread anderweitig zu verwenden.

Normalerweise sehen Sie eine etliche Zeilen lange Ausgabe, auch ohne Befehle eingeben zu müssen, da read(2) im Hintergrund ständig von Serverprozessen und Dämonen ausgeführt wird. Ändern Sie die zweite Klausel von rtime.d ab, indem Sie die Variable execname einsetzen, um zusätzlich den Namen des read(2) ausführenden Prozesses zu erfahren:

printf("%s/%d spent %d nsecs in read(2)\n",
    execname, tid, timestamp - self->t);

Wenn Sie auf einen Prozess stoßen, der Sie besonders interessiert, fügen Sie ein Prädikat ein, um seinem read(2)-Verhalten genauer auf den Grund zu gehen:

syscall::read:entry
/execname == "Xsun"/
{
	self->t = timestamp;
}

Klausel-lokale Variablen

Es besteht auch die Möglichkeit, D-Variablen zu deklarieren, deren Speicherbereich für jede D-Programmklausel wieder verwendet wird. Klausel-lokale Variablen sind mit automatischen Variablen in C-, C++- oder Java-Programmen vergleichbar, die während jedes Aufrufs einer Funktion aktiv sind. Wie alle anderen D-Programmvariablen werden auch klausel-lokale Variablen bei ihrer ersten Zuweisung erzeugt. Diese Variablen werden durch Anwendung des Operators -> auf den speziellen Bezeichner this referenziert und zugewiesen:

BEGIN
{
	this->secs = timestamp / 1000000000;
	...
}

Wenn Sie eine klausel-lokale Variable vor ihrer Verwendung explizit deklarieren möchten, greifen Sie hierzu auf das Schlüsselwort this zurück:

this int x;   /* an integer clause-local variable */
this char c;  /* a character clause-local variable */

BEGIN
{
	this->x = 123;
	this->c = 'D';
}

Klausel-lokale Variablen sind nur während der Lebensdauer der jeweiligen Prüfpunktklausel aktiv. Wenn DTrace die Aktionen der Klauseln für einen bestimmten Prüfpunkt durchgeführt hat, wird der Speicherbereich aller klausel-lokalen Variablen zurückgefordert und für die nächste Klausel verwendet. Deshalb werden klausel-lokale Variablen im Gegensatz zu allen anderen D-Variablen anfänglich nicht mit Nullen angefüllt. Wenn ein Programm mehrere Klauseln für denselben Prüfpunkt enthält, bleiben alle klausel-lokalen Variablen intakt, solange die Klauseln ausgeführt werden. Das folgende Beispiel verdeutlicht dieses Prinzip:


Beispiel 3–2 clause.d: Klausel-lokale Variablen

int me;			/* an integer global variable */
this int foo;		/* an integer clause-local variable */

tick-1sec
{
	/*
	 * Set foo to be 10 if and only if this is the first clause executed.
	 */
	this->foo = (me % 3 == 0) ? 10 : this->foo;
	printf("Clause 1 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

tick-1sec
{
	/*
	 * Set foo to be 20 if and only if this is the first clause executed. 
	 */
	this->foo = (me % 3 == 0) ? 20 : this->foo;
	printf("Clause 2 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

tick-1sec
{
	/*
	 * Set foo to be 30 if and only if this is the first clause executed.
	 */
	this->foo = (me % 3 == 0) ? 30 : this->foo;
	printf("Clause 3 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

Da die Klauseln immer in der Programmreihenfolge ausgeführt werden und klausel-lokale Variablen in unterschiedlichen Klauseln zur Aktivierung desselben Prüfpunkts bestehen, ergibt die Ausführung des obigen Programms stets dieselbe Ausgabe:


# dtrace -q -s clause.d
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
^C

Während klausel-lokale Variablen über die Klauseln hinweg beständig sind, die denselben Prüfpunkt aktivieren, sind deren Werte in der als Erste für einen Prüfpunkt ausgeführten Klausel nicht definiert. Denken Sie daran, jeder klausel-lokalen Variable vor ihrer Verwendung einen geeigneten Wert zuzuweisen. Anderenfalls bringt das Programm möglicherweise unerwartete Resultate.

Klausel-lokale Variablen können mit jedem skalaren Variablentyp definiert werden. Eine Definition von assoziativen Vektoren mit klausel-lokalem Gültigkeitsbereich ist jedoch nicht möglich. Der Gültigkeitsbereich klausel-lokaler Variablen bezieht sich nur auf die entsprechenden Variablendaten, nicht aber auf den für die Variable definierten Namen oder ihre Typidentität. Die Namens- und Typensignatur einer definierten klausel-lokalen Variable kann in jeder nachfolgenden D-Programmklausel verwendet werden. Es besteht keine Garantie, dass der Speicherbereich über die verschiedenen Klauseln hinweg identisch ist.

Klausel-lokale Variablen können zum Ansammeln von Zwischenergebnissen bei Berechnungen oder als temporäre Kopien anderer Variablen eingesetzt werden. Der Zugriff auf eine klausel-lokale Variable läuft wesentlich schneller ab als der Zugriff auf einen assoziativen Vektor. Wenn Sie also den Wert eines assoziativen Vektors in derselben D-Programmklausel mehrmals referenzieren müssen, erweist es sich als effizienter, den Wert zuerst in eine klausel-lokale Variable zu kopieren und die lokale Variable dann wiederholt zu referenzieren.

Integrierte Variablen

Die folgende Tabelle enthält eine vollständige Liste der in D integrierten Variablen. Dabei handelt es sich in allen Fällen um skalare, globale Variablen. Derzeit sind in D weder thread-lokale oder klausel-lokale Variablen noch integrierte assoziative Vektoren definiert.

Tabelle 3–1 Integrierte Variablen in DTrace

Typ und Name 

Beschreibung 

int64_t arg0, ..., arg9

Die ersten zehn Eingangsargumente für einen Prüfpunkt, dargestellt als unbearbeitete 64-Bit-Ganzzahlen. Werden dem aktuellen Prüfpunkt weniger als zehn Argumente übergeben, dann werden die übrigen Variablen auf Null zurückgesetzt. 

args[]

Ggf. die für den aktuellen Prüfpunkt eingegebenen Argumente. Auf den Vektor args[] wird über einen Ganzzahlenindex zugegriffen, doch die einzelnen Elemente sind als der dem jeweiligen Prüfpunktargument entsprechende Typ definiert. Wenn beispielsweise args[] durch einen Prüfpunkt für den Systemaufruf read(2) referenziert wird, nimmt args[0] den Typ int an, args[1] den Typ void * und args[2] den Typ size_t.

uintptr_t caller

Die Speicheradresse des aktuellen Threads im Programmschrittzähler kurz vor Eintritt in den aktuellen Prüfpunkt. 

chipid_t chip

Die CPU-Chipkennung des aktuellen physischen Chips. Weitere Informationen finden Sie in Kapitel 26Der Provider sched.

processorid_t cpu

Die CPU-Kennung der aktuellen CPU. Weitere Informationen finden Sie in Kapitel 26Der Provider sched.

cpuinfo_t *curcpu

Die CPU-Informationen der aktuellen CPU. Weitere Informationen finden Sie in Kapitel 26Der Provider sched.

lwpsinfo_t *curlwpsinfo

Der LWP-Status des leichtgewichtigen Prozesses (LWP), der für den aktuellen Thread steht. Diese Struktur ist in der Manpage proc(4) ausführlicher beschrieben.

psinfo_t *curpsinfo

Der Prozessstatus des Prozesses, der für den aktuellen Thread steht. Diese Struktur ist in der Manpage proc(4) ausführlicher beschrieben.

kthread_t *curthread

Die Adresse der im Betriebssystemkernel internen Datenstruktur für den aktuellen Thread kthread_t. Der Thread kthread_t ist in <sys/thread.h> definiert. Weitere Informationen zu dieser Variable und anderen Betriebssystemdatenstrukturen finden Sie in Solaris Internals.

string cwd

Name des aktuellen Arbeitsverzeichnisses des Prozesses, der für den aktuellen Thread steht. 

uint_t epid

Die EPID (ID des aktivierten Prüfpunkts) für den aktuellen Prüfpunkt. Diese Ganzzahl ist eine eindeutige Kennung eines bestimmten Prüfpunkts, der mit einem spezifischen Prädikat und Aktionssatz aktiviert wurde. 

int errno

Der von dem zuletzt durch diesen Thread ausgeführten Systemaufruf zurückgegebene Fehlerwert. 

string execname

Der für die Ausführung des aktuellen Prozesses an exec(2) übergebene Name.

gid_t gid

Die reale Gruppen-ID des aktuellen Prozesses. 

uint_t id

Die Prüfpunkt-ID für den aktuellen Prüfpunkt. Dabei handelt es sich um die systemweite, eindeutige Kennung des Prüfpunkts, wie sie von DTrace veröffentlicht und in der Ausgabe von dtrace -l angegeben wird.

uint_t ipl

Die Interrupt-Priorität (IPL) auf der aktuellen CPU zum Zeitpunkt der Auslösung des Prüfpunkts. Weitere Informationen zu Interrupt-Prioritäten und zur Interrupt-Behandlung im Solaris-Betriebssystemkernel finden Sie in Solaris Internals.

lgrp_id_t lgrp

Die Latenzgruppen-ID für die Latenzgruppe, der die aktuelle CPU angehört. Weitere Informationen finden Sie in Kapitel 26Der Provider sched.

pid_t pid

Die Prozess-ID des aktuellen Prozesses. 

pid_t ppid

Die Prozess-ID des dem aktuellen Prozess übergeordneten Prozesses. 

string probefunc

Der Funktionsname als Bestandteil der aktuellen Prüfpunktbeschreibung. 

string probemod

Der Modulname als Bestandteil der aktuellen Prüfpunktbeschreibung. 

string probename

Der Name als Bestandteil der aktuellen Prüfpunktbeschreibung. 

string probeprov

Der Providername als Bestandteil der aktuellen Prüfpunktbeschreibung. 

psetid_t pset

Die Prozessorsatz-ID des Prozessorsatzes, der die aktuelle CPU enthält. Weitere Informationen finden Sie in Kapitel 26Der Provider sched.

string root

Name des Root-Verzeichnisses des Prozesses, der für den aktuellen Thread steht. 

uint_t stackdepth

Die Stacktiefe des aktuellen Threads zum Zeitpunkt der Prüfpunktauslösung. 

id_t tid

Die Thread-ID des aktuellen Threads. Bei Threads für Benutzerprozesse stimmt dieser Wert mit dem Ergebnis des Aufrufs von pthread_self(3C) überein.

uint64_t timestamp

Der aktuelle Wert eines Nanosekundenzählers für Zeitmarken. Dieser zählt (wird erhöht) ab einem beliebigen Punkt in der Vergangenheit und sollte nur für relative Berechnungen eingesetzt werden. 

uid_t uid

Die reale Benutzer-ID des aktuellen Prozesses. 

uint64_t uregs[]

Die für den aktuellen Thread gespeicherten Benutzermodus-Registerwerte zum Zeitpunkt der Prüfpunktauslösung. Die Verwendung des uregs[]-Vektors wird in Kapitel 33Ablaufverfolgung von Benutzerprozessen behandelt.

uint64_t vtimestamp

Der aktuelle Wert eines Nanosekundenzählers für Zeitmarken, der auf die Ausführungsdauer des aktuellem Threads auf einer CPU minus der in DTrace-Prädikaten und -Aktionen verbrachten Zeit virtualisiert ist. Dieser zählt (wird erhöht) ab einem beliebigen Punkt in der Vergangenheit und sollte nur für relative Zeitberechnungen eingesetzt werden. 

uint64_t walltimestamp

Die seit 00.00 Uhr UCT am 1. Januar 1970 verstrichene Zeit in Nanosekunden. 

In D integrierte Funktionen wie trace() werden in Kapitel 10Aktionen und Subroutinen erläutert.

Externe Variablen

In D kommt das Backquote-Zeichen (oder Accent grave) (`) als spezieller Bereichsoperator zum Ansprechen von Variablen zum Einsatz, die zwar im Betriebssystem, nicht aber im D-Programm definiert sind. So enthält der Solaris-Kernel beispielsweise eine C-Deklaration eines über das System abstimmbaren Parameters (Tunables) namens kmem_flags zum Aktivieren von Debugging-Leistungsmerkmalen für die Speicherzuweisung. Im Solaris Tunable Parameters Reference Manual finden Sie weitere Informationen zu kmem_flags. Dieses Tunable ist im Kernel-Quellcode wie folgt als eine C-Variable deklariert:

int kmem_flags;

Um in einem D-Programm auf den Wert dieser Variable zuzugreifen, verwenden Sie die D-Notation:

`kmem_flags

DTrace weist jedem Kernelsymbol den im entsprechenden C-Betriebssystemcode für das Symbol verwendeten Typ zu und bietet dadurch einen einfachen quellcodebasierten Zugriff auf die nativen Datenstrukturen des Betriebssystems. Wenn Sie externe Betriebssystemvariablen einsetzen möchten, benötigen Sie Zugriff auf den entsprechenden Betriebssystemquellcode.

Mit dem Zugriff auf externe Variablen über ein D-Programm sprechen Sie die internen Implementierungsinformationen eines anderen Programms an, beispielsweise des Betriebssystemkernels oder dessen Gerätetreiber. Diese Implementierungsinformationen stellen keine stabile Schnittstelle dar, auf die Sie sich verlassen könnten! Mit D-Programmen, die von diesen Informationen abhängen, riskieren Sie, dass diese nach dem nächsten Upgrade der entsprechenden Software nicht mehr funktionsfähig sind. Aus diesem Grund werden externe Variablen in der Regel von Kernel- und Gerätetreiberentwicklern und von Wartungspersonal zum Aufspüren von Leistungs- oder Funktionsproblemen mit DTrace verwendet. Weitere Informationen über die Stabilität von D-Programmen finden Sie in Kapitel 39Stabilität.

Die Namen von Kernelsymbolen werden in einem von den D-Variablen- und Funktionsbezeichnern getrennten Namensraum gehalten, sodass keine Gefahr eines Konflikts zwischen diesen Namen und Ihren D-Variablen besteht. Stößt der D-Compiler auf eine Variable mit vorangestelltem Backquote-Zeichen, durchsucht er der Reihenfolge nach die bekannten Kernelsymbole unter Bezugnahme auf die Liste der geladenen Module nach einer passenden Variablendefinition. Da der Solaris-Kernel dynamisch geladene Module mit separaten Symbol-Namensräumen unterstützt, kann ein Variablenname im aktiven Betriebssystemkernel mehrmals verwendet werden. Diese Namenskonflikte können Sie lösen, indem Sie vor dem Backquote-Zeichen im Symbolnamen den Namen des Kernelmoduls angeben, auf dessen Variable zugegriffen werden soll. Beispielsweise bietet jedes ladbare Kernelmodul naturgemäß die Funktion _fini(9E), sodass Sie für die Bezugnahme auf die Adresse der Funktion _fini, die ein Kernelmodul namens foo zur Verfügung stellt, Folgendes schreiben würden:

foo`_fini

Alle beliebigen D-Operatoren außer denjenigen, die Werte ändern, können unter Beachtung der üblichen Regeln für Operandentypen auf externe Variablen angewendet werden. Wenn Sie DTrace starten, lädt der D-Compiler die für die aktiven Kernelmodule zutreffenden Variablennamen ein. Eine Deklaration dieser Variablen ist also nicht erforderlich. Operatoren wie beispielsweise = oder +=, die den Wert einer Variable ändern, dürfen nicht auf externe Variablen angewendet werden. Aus Sicherheitsgründen verhindert DTrace eine potenzielle Schädigung des Status der untersuchten Software.