Home ORDIX AG             Dienstleistung             Trainingsshop    Kunden / Referenzen Aktuelles    Kontakt
Home  Pfeil  ORDIX News  Pfeil  4/2005
suche: 
Dieser Artikel richtet sich an erfahrene Systemadministratoren und Entwickler, die umfassendere Analysetechniken unter Solaris 10 benötigen und z.B. gern mit truss oder strace arbeiten.

Glossar

Binary
Ausführbares, kompiliertes und gebundenes Programm.
DTrace
Dynamic Tracing: Technik, mit der Systemereignisse unter Solaris 10 analysiert werden können.
Kernel
Der Kernel ist der zentrale Bestandteil des Betriebssystems. In ihm ist die Prozess- und Datenorganisation festgelegt, auf der alle weiteren Softwarebestandteile des Betriebssystems aufbauen. Im Kernel ist die Kommunikation mit der Hardware, die Prozessorverwaltung, die Speicherverwaltung, die Prozessverwaltung, die Geräteverwaltung und das Dateisystem integriert.
Kernel-Modul
In sich abgeschlossener Teil des Kernels. Häufig wird der Code zum Ansprechen einer bestimmten Hardware in einem separaten Modul abgelegt. Dafür wird auch der Begriff „Treiber“ verwendet.
Modul
Ein Modul oder eine Komponente ist ein abgeschlossener Teil eines Softwareprogramms. Häufig sind diese in einer eigenen Datei abgelegt, die bei Bedarf hinzugeladen wird.
Prozess
Programm, welches in den Hauptspeicher geladen wurde und gerade abgearbeitet wird.
Privileg
Vorrecht (eines Prozesses, respektive des Benutzers) eine besondere Aktion ausführen zu dürfen. Hat ein Benutzer alle Privilegien, so entspricht dies der traditionellen Root-Kennung mit UID 0. Unter Solaris 10 wird zwischen 48 verschiedenen Privilegien unterschieden, die einzeln vergeben oder entzogen werden können. Z. B. kann ein Benutzer die Privilegien zugeteilt bekommen, um DTrace zu nutzen.
RBAC
RBACRole Based Access Control. RBAC ist ein Verfahren, womit de.niert wird, welche Befehle mit zusätzlichen Rechten (z. B. Privilegien oder unter bestimmter Benutzerkennung) ausgeführt werden dürfen.
System Space
Abläufe, die vom Kernel durchgeführt werden, finden im System Space statt.
System Call
Aufruf einer Kernel-Funktion durch einen Prozess im User Space.
Thread
Elementaraufgabe. Die Befehle eines Threads sind in sich so abgeschlossen, dass sie auf einer CPU zusammenhängend ausgeführt werden können. Um Programme mehrprozessorfähig zu gestalten, müssen die Abläufe in Threads untergliedert sein.
truss
Analyseprogramm unter System V.4 zum Protokollieren von System-Calls eines Prozesses.
User Space
Abläufe, die ohne Beihilfe des Kernels durchgeführt werden, finden im User Space statt. Fast alle Programme werden im User Space gestartet.

Tiefe Einblicke in Solaris 10 mit DTrace (Teil I)

Schon lange vor der Freigabe von Solaris 10 hat Sun die Werbetrommel gerührt. Unter vielen neuen Eigenschaften taucht ganz vorne DTrace auf. Der folgende Artikel bietet einen Einstieg und ermöglicht erfahrenen Systemadministratoren und Entwicklern DTrace kennenzulernen. Da diese ORDIX News nicht ausschließlich über DTrace berichtet, ist der folgende Artikel nur ein kleiner Ausschnitt aus der Welt der tiefen Einblicke.

Das Versprechen

Mit DTrace lässt Sun eine Software antreten, um auf einfache Weise das System zu analysieren und Fehler oder Optimierungspotenzial zu finden. Als Hauptnutzer von DTrace werden Administratoren und Entwickler genannt, die eine unzureichende Performanz des Systems oder der Applikation mit DTrace analysieren möchten. DTrace will die inneren Verhaltensweisen des Systems in Echtzeit offenbaren, so dass Hardware-Probleme erkannt und Software-Probleme gelöst werden können.

Die Komponenten

In Abbildung 1 sind die Komponenten von DTrace im Zusammenhang dargestellt. Links oben finden sich die vom Anwender geschriebenen Skripte. Skripte werden in der neu entwickelten Programmiersprache D geschrieben. Diese werden mittels /usr/sbin/dtrace kompiliert. Das Binary nutzt die Bibliothek libdtrace zur Kommunikation mit dem Kernelmodul gleichen Namens. Das Kernelmodul dtrace benötigt die Provider, also weitere Kernelmodule, die die Messpunkte bereitstellen.

Aus der Abbildung wird deutlich, dass es neben /usr/sbin/dtrace weitere Konsumenten (intrstat, plockstat und lockstat) gibt, die sich der DTrace-Mechanismen bedienen.

Der Ablauf

DTrace bietet mit Hilfe unterschiedlicher Provider über 30.000 Kernel-Messpunkte. Der Aufruf eines System-Calls ist ein Beispiel für einen Messpunkt. Provider sind Kernelmodule, die für bestimmte Bereiche im Kernel die Messpunkte bereitstellen. Der syscall-Provider stellt alle Messpunkte bzgl. System-Calls bereit. Der Anwender schreibt ein DTrace-Skript, mit dem er definiert, welche Messpunkte zu aktivieren sind, welche Informationen beim Durchlaufen eines Messpunktes genutzt werden sollen und wie diese aufbereitet werden.

Dieses D-Skript wird mit Hilfe des Programms /usr/sbin/dtrace aufgerufen und dabei zunächst kompiliert. Der entstandene Binärcode wird an das Kernelmodul dtrace weitergereicht. /usr/bin/dtrace wird auch als Konsument bezeichnet, denn es empfängt bzw. konsumiert die relevanten Messdaten.

Das D-Trace-Kernel-Modul erhält den kompilierten Code und schaltet die Messpunkte ein. Der jeweilige Provider stellt sicher, dass der relevante D-Programmcode beim Durchlaufen des Messpunktes ausgeführt wird.

Asynchron

Die Kommunikation zwischen Kernel und Konsument erfolgt über Puffer. Während die Kernelmodule in einen Puffer schreiben, kann der Konsument aus einem anderen Puffer lesen. Regelmäßig wird zwischen diesen beiden Puffern umgeschaltet. Das Schalten geschieht ohne Datenverlust und stellt gleichzeitig sicher, dass der Konsument nur konsistente Daten liest.

Die Nutzung des Puffers ist ein wesentliches Designmerkmal von DTrace. Im Unterschied zu truss muss ein Thread oder ein Prozess nicht zu Zwecken der Protokollierung gestoppt werden. Die Nutzung von DTrace erfolgt somit ohne nennenswerte Performanzeinbußen.

Das Privileg

Das zu nutzende Programm /usr/sbin/dtrace taucht nicht im Pfad eines „normalen“ Benutzers auf. Dennoch ist dieser in der Lage /usr/bin/dtrace zu starten. Allerdings sind weitere Privilegien notwendig, um dtrace tatsächlich zu nutzen. Erteilt der Administrator z. B. mittels RBAC einem Benutzer die Privilegien dtrace_kernel, dtrace_proc und dtrace_user, so kann dieser alle Analysen mit dtrace durchführen. Von dtrace als destruktiv eingestufte Aktionen erfordern den Root-Account.

Erste Schritte in der Skriptsprache D

Zur Definition der relevanten Daten bzw. der Ausgabe dient ein in D geschriebenes Skript, welches beim Aufruf von DTrace mit angegeben wird. D ähnelt vom Konzept sehr stark AWK, die Syntax ist C oder AWK ähnlich. Bestimmte Konstrukte in D ermöglichen u. a. die Angabe bestimmter, vom Anwender gewünschter Messpunkte, die Einschränkung (z. B. auf eine ProzessID) und die Formatierung der Ausgabe.

Aufbau eines Skripts

Ein Skript besteht aus einem oder mehreren Anweisungsblöcken (siehe Abbildung 2, Zeile 3 - 7, beziehungsweise Zeile 8 - 12). Zu Beginn eines jeden Blocks werden die Messpunkte angegeben, für die der Anweisungsblock auszuführen ist (Zeile 3, bzw. Zeile 8). Durch zusätzliche Bedingungen kann die Ausführung des Anweisungsblocks weiter beschränkt werden (Zeile 4 bzw. Zeile 9).

Komponenten von DTrace
Abb. 1. Komponenten von DTrace.

1 #!/usr/sbin/dtrace -qs
2
3 syscall:::entry
4 /pid == $target/
5 {
6  printf(„%s(%d, 0x%x, %4d)“, probefunc, arg0, arg1, arg2) ;
7 }
8 syscall:::return
9 /pid == $target/
10 {
11  printf(„\t\t = %d\n“, arg1) ;
12 }
Abb. 2: Nachbilden von truss – truss.d.

Wird beim Aufruf von DTrace zusätzlich noch ein Programmaufruf angegeben, so wird dieser ausgeführt und von DTrace bearbeitet. Auf die Prozess-ID des aufgerufenen Programms kann in dem Skript mit $target zugegriffen werden (Zeile 4 bzw. Zeile 9).

Das Skript in Abbildung 2 besteht aus zwei Anweisungsblöcken. Der erste Block (Zeile 3 - 7) greift beim Aufruf eines System-Calls (definiert durch Zeile 3). Bei jedem Aufruf eines System-calls, egal durch welchen Prozess, wird dieser Messpunkt durchlaufen. In der Variablen pid findet sich die Prozess-ID des Prozesses, der diesen Messpunkt durchlaufen hat. Die Variable pid wird für jedes Mal, wenn ein Messpunkt durchlaufen wird, neu gesetzt.

Da nur die System-Calls des Prozesses $target, also des beim Aufruf mit angegebenen Programms, protokolliert werden sollen, wird mit Hilfe einer Bedingung die Ausführung auf die relevante Prozess-ID beschränkt (Zeile 4). Für jeden aufgerufenen System-Call wird eine Zeile mit dem Namen des System-Calls und weiteren Argumenten ausgegeben (Zeile 6). Der zweite Anweisungsblock (Zeile 8 - 12) wird bei Verlassen des System-Calls (Zei-le 8) aufgerufen. Ausgegeben wird der Returnwert des System-Calls (Zeile 11).

Aufruf von DTrace

Das Aufrufen von DTrace erfolgt mit der in Abbildung 3 gezeigten Syntax. Die Dtrace Option –s erhält als Optionsargument das auszuführende D-Skript. Optional kann mit -c ein zu analysierender Befehl angegeben werden. Wichtig ist hierbei, dass DTrace den Befehl als ein Argument erhält. Soll das in Abbildung 2 beschriebene D-Skript alle System-Calls des ls –l–Befehls aufzeigen, so kann dies mit dem Beispiel 1 aus Abbildung 3 geschehen.

Syntax:
dtrace –s <skript> [-c "auszuführendes Programm inkl. Optionen"]
Beispiel 1:
dtrace –s truss.d -c "ls -l"
Beispiel 2: Mit Interpreter Zeile #! Für exec-Systemcall
./truss.d -c "ls -l"
Abb. 3: Syntax zum Aufrufen von DTrace.

Genau wie bei anderen Skriptsprachen, kann der Aufruf vereinfacht werden. Durch Angabe einer #!Zeile ermittelt der exec-System-Call, mit welchem Programm das Skript interpretiert werden soll. In der Abbildung 2 sorgt die Zeile 1 dafür, dass der Aufruf auch in der Form des Beispiels 2 in Abbildung 3 möglich ist.

Optionen

An den bisherigen Beispielen wurde der prinzipielle Aufruf deutlich. Von den ca. 30 Optionen die /usr/bin/dtrace bietet, werden hier nur einige wenige vorgestellt:

-q Es wird nur noch ausgegeben, was in dem D-Skript explizit angegeben ist. Die automatische Ausgabe der CPU, der Messpunkt-ID und der durchlaufenen Funktion durch DTrace entfällt.
-o <datei> Ausgabe erfolgt in eine Datei.
-p <pid> Angabe einer Prozess-ID (bereits laufender Prozess). Auf diese kann im D-Skript mit $target bezug genommen werden. (nicht in Verbindung mit –c).
-c <command> Angabe eines Kommandos. Auf dessen Prozess-ID kann im D-Skript mit $target Bezug genommen werden. Das Kommando muss an DTrace als ein Argument übergeben werden. (nicht in Verbindung mit –p).
-l Liste aller auf dem System verfügbaren Messpunkte ausgeben. Diese Option kann alleine verwendet werden.
-s <script> Angabe eines D-Skriptes

Messpunkte und zugehörige Aktionen können durch Optionen direkt an der Kommandozeile angegeben werden. Weitere Optionen ermöglichen die Feinabstimmung von DTrace z. B. durch Verändern der Puffer-Eigenschaften.

Messpunkte

Die Bezeichnung eines Messpunktes ist wie folgt aufgebaut: provider:module:function:name

Das erste Feld gibt den Provider an. Im nächsten Feld kann das (Kernel-)Modul angegeben werden, auf das die Analyse beschränkt werden soll. Das Feld „function“ bestimmt die relevante Funktion. Bei dem syscall-Provider kann in function der Name des zu analysierenden System-Calls (beispielsweise open, read, write) eingetragen werden.

syscall:::entry Alle System-Calls beim Aufrufen.
syscall:::open:entry Alle Open-System-Calls.
profil:::tick-1sec Jede Sekunde auftretender Messpunkt.
dtrace:::BEGIN
dtrace:::END
Nur zu Beginn/am Ende des DTrace-
Skriptes auftretender Messpunkt.

Als letztes gibt „name“ den eigentlichen Namen des Messpunktes an. Werden Felder leergelassen, so wird keine besondere Einschränkung vorgenommen. Sollen z. B. alle System-Calls ausgewertet werden, werden die Felder „module“ und „function“ leer gelassen. Messpunkte werden auch „probe“ genannt. Hier einige Beispiele für Messpunkte:

Macro-Variablen

Ohne weitere Angaben analysiert DTrace alle durchlaufenen Messpunkte. Häufig soll die Analyse jedoch auf eine Prozess-ID, einen bestimmten Benutzer oder anderweitig beschränkt werden, was auch sinnvoll ist.

Um dies zu ermöglichen, werden ähnlich wie bei AWK einige Variablen vorbelegt. Macro-Variablen werden einmal zu Beginn des Skriptes belegt und ändern sich während der Laufzeit nicht. In einem Skript kann diese Variable mit den Angaben des gerade durchlaufenen Messpunktes verglichen werden.

Ein Beispiel für eine solche Auswahl wurde in Abbildung 2, Zeile 4 bzw. Zeile 7 schon dargestellt. Eine Auswahl weiterer Macro-Variablen finden sich in Abbildung 4.

Name Beschreibung
$0, $1, $2, $3 An das Skript übergebene Argumente.
$egid Effektive Gruppen-Kennung des DTrace-Prozesses.
$euid Effektive Benutzer-Kennung des DTrace-Prozesses
$gid Reale Gruppen-Kennung des DTrace-Prozesses
$pid Prozess-Kennung des DTrace-Prozesses
$pgid Prozessgruppen-Kennung des DTrace-Prozesses
$ppid Prozess-Kennung des Vaterprozesses des DTrace-Prozesses
$projid Projekt-Kennung des DTrace-Prozesses: Dies ist eine besondere Eigenschaft von Prozessen unter Solaris, die der Ressourcenverwaltung dient.
$sid Session-Kennung des DTrace-Prozesses.
$target Prozess-Kennung des durch DTrace mit –c gestarteten Programms.
$tasked Task-ID des DTrace-Prozesses.
$uid Reale Benutzer-Kennung des DTrace-Prozesses.
Abb. 4: Eine Auswahl an möglichen vorbelegten Variablen.

Eingebaute Variablen

Neben den fest belegten Macro-Variablen gibt es dynamisch in Abhängigkeit der durchlaufenen Messpunkte belegte Variablen. Die Abbildung 5 liefert eine Auswahl dieser Variablen.

Variablen-Namen Beschreibung
arg0, ...,arg9 Die ersten 10 Eingabe-Argumente, die ein Messpunkt liefert. Die Werte werden in raw 64-bit Integer dargestellt.
cpu Die CPU-Kennung der aktuellen CPU.
cwd Der Name des Arbeitsverzeichnisses.
errno Fehlernummer, die vom letzten System-Call dieses Threads zurückgegeben wurde.
execname Der Name, der an exec(2) übergeben wurde.
id Die Kennung eines Messpunktes, wie sie von DTrace -l ausgegeben wird. Sie wird auch Probe-ID genannt und ist systemweit eindeutig.
pid Die aktuelle Prozesskennung.
timestamp Aktueller Wert eines Nanosekundenzählers (Zeitstempel). Dieser Wert sollte nur für die relative Zeitberechnung verwendet werden, da der Anfangszeitpunkt des Zählers willkürlich ist.
uid Die reale Benutzerkennung.
vtimestamp Aktueller Wert eines Nanosekundenzählers (Zeitstempel), bezogen auf die CPU-Zeit, die der aktuelle Prozess verbraucht hat. Die durch DTrace verursachte Zeit wird abgezogen. Dieser Wert sollte nur für die relative Zeitberechnung verwendet werden, da der Anfangszeitpunkt des Zählers willkürlich ist.
walltimestamp Aktueller Wert eines Nanosekundenzählers seit 00:00 Universal Coordinated Time, 1. Januar 1970.
Abb. 5: Eine Auswahl an dynamisch belegten Variablen.

Im Beispiel liefert die Variable pid die Prozess-Kennung des Prozesses, der den Messpunkt gerade durchläuft. Somit lässt sich die Auswertung des Messpunktes auf einen Prozess beschränken (siehe Zeile 4 beziehungsweise Zeile 9, Abbildung 2).

1 Begin
2 {
3	start = timestamp ;
4 }
5  
6 END
7 {
8	printf("\n\nTotal Time: %d secs" (timestamp - start) / 1000000000) ;
9 }
Abb. 6: Messung der verbrauchten Zeit.

Die Laufzeit des zu analysierenden Prozesses kann gemessen werden, indem die Blöcke aus Abbildung 6 dem entsprechenden DTrace-Skript hinzugefügt werden.

/truss.d -c ‚ls -l/‘
munmap(4281991168, 0x2000, 18) 		= 0
mmap(65536, 0x6000, 7) = 4281991168
setcontext(0, 0xffbff8b8, 679240) = 0
getrlimit(3, 0xffbff8b0, 4290764800) = 0
getpid(8388608, 0x93c8710, 4281764800) = 38878043972443
setcontext(3, 0xff3a2088, 2147483647) = 0
brk(159984, 0x0, 0) = 0
brk(168176, 0x0, 0) = 0
stat(4282316524, 0xffbfef40, 4290769148) = 0
resolvepath(4282316524, 0xffbfeb40, 1023) = 33
open(4282316524, 0x0, 0) = 3
...
open(4290766388, 0x0, 4997983) = -1
open(4278066368, 0x0, 4278066406) = 3
fstat64(3, 0xffbfea88, 4278066432) = 0
read(3, 0xff3a2400, 837) = 837
close(3, 0x93c8710, 837) = 0
open64(4281665120, 0x0, 0) = 3
....
brk(241904, 0x0, 0) = 0
open(4277946676, 0x2000, 438) = 3
fcntl(3, 0x0, 256) = -1
read(3, 0x39778, 1024) = 275
close(3, 0xff32fbf4, 0) = 0
write(1, 0x31264, 718) = 718
rexit(0, 0x26800, 1)
Abb. 7: Ausgabe des Skriptes aus Abbildung 2.

Zeile 1 bis 4 wird vor allen anderen Blöcken und vor dem Start der eigentlichen Analyse ausgeführt und speichert einen Zeitstempel in der Variablen „start“. Die Zeilen 6 bis 9 werden nach allen anderen Blöcken und nach Beenden der Analyse ausgeführt. Vom nun aktuellen Zeitstempel wird der Wert von „start“ abgezogen und das Ergebnis in Sekunden ausgegeben.

Die Ausgabe des Beispielskriptes finden Sie in den Abbildungen 2 und 6. Ohne die Ausgabe des ls-Kommandos präsentiert sich das Ergebnis in Abbildung 7.

Ausblick

Wie der Artikel zeigt, ist der erste Einstieg in DTrace nicht allzu schwer. Zumindest truss lässt sich durch DTrace problemlos nachbauen. Wer darüber hinaus DTrace ergründen und mit DTrace tiefere Einblicke in Solaris 10 erhalten möchte, darf sich auch auf eine der nächsten ORDIX News freuen. Wir werden das Thema Variablen in D genauer beleuchten und einen Vergleich mit sar anstreben.

Markus Schreier (info@ordix.de).