Handbuch zur dynamischen Ablaufverfolgung in Solaris

Kapitel 11 Puffer und Pufferung

Mit der Pufferung und Verwaltung von Daten stellt das DTrace-Framework seinen Clients wie dtrace(1M) einen zentralen Dienst zur Verfügung. In diesem Kapitel wird die Datenpufferung ausführlich unter die Lupe genommen. Darüber hinaus werden Optionen beschrieben, die es ermöglichen, die DTrace-Richtlinien für die Pufferverwaltung von DTrace zu ändern.

Hauptpuffer

Der Hauptpuffer ist bei jedem Aufruf von DTrace vorhanden. In ihm zeichnen die Ablaufverfolgungsaktionen standardmäßig ihre Daten auf. Dabei handelt es sich um folgende Aktionen:

exit()

printf()

trace()

ustack()

printa()

stack()

tracemem()

 

Die Hauptpuffer werden immer auf CPU-Basis (je CPU) zugewiesen. Diese Richtlinie kann nicht geändert werden. Es ist aber möglich, die Ablaufverfolgung und Pufferzuweisung mit der Option cpu auf eine einzige CPU zu beschränken.

Richtlinien für den Hauptpuffer

Mit DTrace lassen sich Abläufe in räumlich extrem beschränkten Kontexten im Kernel verfolgen. Insbesondere erlaubt DTrace die Überwachung auch in einem Kontext, in dem die Kernelsoftware keine zuverlässige Speicherplatzreservierung vornehmen kann. Als Folge dieser Flexibilität in Bezug auf den Kontext besteht immer die Möglichkeit, dass DTrace die Daten zu verfolgen versucht, wenn kein Speicherplatz zur Verfügung steht. Für den Umgang mit einer solchen Situation muss DTrace eine Richtlinie besitzen, die Sie allerdings auf die Bedürfnisse eines bestimmten Experiments abstimmen können. In manchen Fällen kann die Richtlinie angemessen sein, neue Daten zu verwerfen. In anderen Fällen ist es unter Umständen wünschenswert, den durch die ältesten aufgezeichneten Daten belegten Speicherplatz für neue Daten zu verwenden. Meistens soll mit einer Richtlinie dafür gesorgt werden, die Wahrscheinlichkeit zu verringern, dass der verfügbare Speicherplatz überhaupt erst aufgebraucht wird. Für diese unterschiedlichen Anforderungen unterstützt DTrace mehrere Pufferrichtlinien. Die Unterstützung wird mit der Option bufpolicy implementiert und kann je Verbraucher festgelegt werden. Weitere Informationen zum Setzen von Optionen finden Sie in Kapitel 16Optionen und Tunables .

Die Richtlinie switch

Standardmäßig gilt für den Hauptpuffer die Pufferrichtlinie switch. Unter dieser Richtlinie wird je CPU ein Pufferpaar zugewiesen: Ein Puffer ist aktiv, der andere inaktiv. Wenn ein DTrace-Verbraucher versucht, in einem Puffer zu lesen, vertauscht der Kernel zuerst den inaktiven und den aktiven Puffer (engl. switch). Dieser Wechsel der Puffer erfolgt so, dass kein Zeitfenster entsteht, in dem überwachte Daten verloren gehen könnten. Nach dem Wechsel der Puffer wird der jetzt inaktive Puffer an den DTrace-Verbraucher kopiert. Diese Richtlinie gewährleistet, dass der Verbraucher stets einen in sich stimmigen Puffer sieht: Es werden nie gleichzeitig Daten in einen Puffer geschrieben und aus ihm kopiert. Durch diese Technik werden außerdem Zeitfenster verhindert, in welchen die Ablaufverfolgung pausiert oder auf andere Weise behindert wird. Die Frequenz, mit der die Puffer vertauscht und ein Puffer ausgelesen wird, steuert der Verbraucher mithilfe der Option switchrate. Wie jede Frequenzoption kann auch switchrate mit einem beliebigen Zeitsuffix angegeben werden. Standardmäßig gilt Häufigkeit pro Sekunde. Weitere Informationen zu switchrate und anderen Optionen finden Sie in Kapitel 16Optionen und Tunables .


Hinweis –

Damit der Hauptpuffer auf Benutzerebene schneller als einmal pro Sekunde (der Standardwert) verarbeitet werden kann, sollten Sie den Wert von switchrate entsprechend anpassen. Die System verarbeitet Aktionen, die Aktivitäten auf Benutzerebene einleiten (wie z. B. printa () und system()), wenn der entsprechende Datensatz im Hauptpuffer verarbeitet wird. Der Wert von switchrate legt die Geschwindigkeit fest, mit der das System solche Aktionen verarbeiten kann.


Wenn unter der Richtlinie switch ein bestimmter aktivierter Prüfpunkt mehr Daten protokolliert, als Speicherplatz im aktiven Hauptpuffer verfügbar ist, werden die Daten ausgelassen und ein entsprechender Zähler für die jeweilige CPU wird erhöht. Kommt es zu einer oder mehreren solcher Auslassungen (engl. drops), zeigt dtrace(1M) eine Meldung wie in folgendem Beispiel an:


dtrace: 11 drops on CPU 0

Wenn eine Aufzeichnung die Gesamtgröße des Puffers übersteigt, wird diese unabhängig von der Pufferrichtlinie ausgelassen. Auslassungen können Sie reduzieren oder ganz umgehen, indem Sie entweder mit der Option bufsize die Größe des Hauptpuffers oder mit der Option switchrate die Frequenz des Pufferwechsels erhöhen.

Im Rahmen der Richtlinie switch wird dem Scratch-Bereich für copyin(), copyinstr() und alloca() Speicherplatz außerhalb des aktiven Puffers zugewiesen.

Die Richtlinie fill

Für bestimmte Probleme bietet sich der Einsatz eines einzelnen Puffers im Kernel an. Dieser Ansatz, der sich mit der Richtlinie switch und geeigneten D-Konstrukten durch Inkrementierung einer Variable in D und den angemessenen Einsatz einer exit()-Aktion implementieren lässt, schaltet die Möglichkeit von Auslassungen jedoch nicht ganz aus. Um einen einzelnen, großen Puffer im Kernelinneren anzufordern und die Ablaufverfolgung fortzusetzen, bis mindestens einer der Puffer auf CPU-Ebene angefüllt ist, verwenden Sie die Pufferrichtlinie fill. Sie bewirkt, dass die Ablaufverfolgung fortgesetzt wird, bis ein aktivierter Prüfpunkt versucht, mehr Daten zu protokollieren, als in den verbleibenden Speicherplatz des Hauptpuffers hineinpassen. Wenn nur unzureichender Speicherplatz übrig bleibt, wird der Puffer als gefüllt markiert und der Verbraucher wird benachrichtigt, dass mindestens einer seiner Puffer auf CPU-Ebene voll ist. Sobald dtrace(1M) einen einzigen vollen Puffer entdeckt, wird die Ablaufverfolgung angehalten, alle Puffer werden verarbeitet und dtrace wird beendet. Es werden auch dann keine weiteren Daten in einem als gefüllt markierten Puffer aufgezeichnet, wenn diese eigentlich in den Puffer hineinpassen würden.

Zum Aktivieren der Richtlinie fill setzen Sie die Option bufpolicy auf fill. Der folgende Befehl protokolliert beispielsweise jeden Systemaufrufeintrag in einem CPU-weiten 2K-Puffer, wobei die Pufferrichtlinie auf fill gesetzt ist:


# dtrace -n syscall:::entry -b 2k -x bufpolicy=fill

Die Richtlinie fill und END-Prüfpunkte

END-Prüfpunkte werden normalerweise erst ausgelöst, wenn die Ablaufverfolgung durch den DTrace-Verbraucher ausdrücklich angehalten wird. END-Prüfpunkte werden garantiert nur auf einer CPU ausgelöst. Um welche CPU es sich dabei handelt, ist jedoch unbestimmt. Bei Verwendung von fill-Puffern wird die Ablaufverfolgung explizit angehalten, wenn mindestens einer der CPU-weiten Hauptpuffer als gefüllt markiert wurde. Wurde die Richtlinie fill ausgewählt, kann der END-Prüfpunkt unter Umständen auf einer CPU ausgelöst werden, die einen gefüllten Puffer besitzt. Um die Ablaufverfolgung mit END in fill-Puffern unterzubringen, berechnet DTrace die potenziell von END-Prüfpunkten belegte Speicherplatzmenge und subtrahiert diesen Speicherplatz von der Größe des Hauptpuffers. Ist die Nettogröße negativ, verweigert DTrace den Start und dtrace(1M) gibt eine entsprechende Fehlermeldung aus:


dtrace: END enablings exceed size of principal buffer

Der Reservierungsmechanismus gewährleistet, dass ein als gefüllt gekennzeichneter Puffer stets ausreichend Speicherplatz für einen END-Prüfpunkt bietet.

Die Richtlinie ring

Die DTrace-Pufferrichtlinie ring erweist sich bei der Ablaufverfolgung der zu einem Ausfall führenden Ereignisse als hilfreich. Wenn die Reproduktion des Ausfalls Stunden oder gar Tage dauern kann, ist es wahrscheinlich vorzuziehen, nur die neuesten Daten beizubehalten. Sobald ein Hauptpuffer voll ist, wird die Aufzeichnung beim ersten Eintrag fortgesetzt und überschreibt dadurch die ältesten Ablaufverfolgungsdaten. Der Ringpuffer wird durch Setzen der Option bufpolicy auf die Zeichenkette ring eingerichtet:


# dtrace -s foo.d -x bufpolicy=ring

dtrace(1M) produziert, wenn zum Erzeugen eines Ringpuffers eingesetzt, erst dann eine Ausgabe, wenn der Prozess abgeschlossen ist. Dann wird der Ringpuffer verbraucht und verarbeitet. dtrace verarbeitet die einzelnen Ringpuffer in der Reihenfolge der CPUs. Innerhalb des Puffers einer CPU werden die Protokolle in chronologischer Reihenfolge von alt nach jung angezeigt. Analog zur Pufferrichtlinie switch wird für die Aufzeichnungen aus unterschiedlichen CPUs keine bestimmte Reihenfolge beachtet. Sollte eine solche Reihenfolge erforderlich sein, empfiehlt es sich, die Variable timestamp als Teil der Ablaufverfolgungsanforderung ebenfalls zu protokollieren.

Das folgende Beispiel veranschaulicht die Aktivierung der Ringpufferung mit einer #pragma option-Direktive:

#pragma D option bufpolicy=ring
#pragma D option bufsize=16k

syscall:::entry
/execname == $1/
{
	trace(timestamp);
}

syscall::rexit:entry
{
	exit(0);
}

Sonstige Puffer

Jede DTrace-Aktivierung beinhaltet Hauptpuffer. Bestimmte DTrace-Verbraucher können über die Hauptpuffer hinaus zusätzliche Datenpuffer innerhalb des Kernels besitzen: einen Aggregatpuffer (siehe Kapitel 9Aggregate) und einen oder mehrere spekulative Puffer (siehe Kapitel 13Spekulative Ablaufverfolgung).

Puffergrößen

Die Größe der einzelnen Puffer lässt sich pro Verbraucher anpassen. Die folgende Tabelle zeigt die verschiedenen Optionen zum Anpassen der Größe der einzelnen Puffer:

Puffer 

Größenoption 

Hauptpuffer 

bufsize

Spekulative Puffer 

specsize

Aggregatpuffer 

aggsize

Jede Option ist auf einen Wert gesetzt, der die jeweilige Größe wiedergibt. Wie alle Größenoptionen kann dem Wert ein optionales Größensuffix angefügt werden. Ausführliche Informationen finden Sie in Kapitel 16Optionen und Tunables . Um beispielsweise die Puffergröße in der Befehlszeile für dtrace auf 1 MB festzulegen, können Sie die Option mit -x setzen:


# dtrace -P syscall -x bufsize=1m

Alternativ übergeben Sie -dtrace die Option b :


# dtrace -P syscall -b 1m

Außerdem lässt sich bufsize auch über #pragma D option festlegen:

#pragma D option bufsize=1m

Die gewählte Puffergröße bestimmt die Größe des Puffers auf jeder CPU. Bei Verwendung der Pufferrichtlinie switch gibt bufsize die Größe jedes Puffers auf jeder CPU an. Die Puffergröße beträgt standardmäßig 4 MB.

Richtlinie für die Änderung der Puffergröße

Das System kann gelegentlich zu wenig freien Kernelspeicher verfügbar haben, um einem Puffer die gewünschte Menge an Speicherplatz zuzuweisen, weil entweder nicht genügend Speicher vorhanden ist oder der DTrace-Verbraucher einen der in Kapitel 16Optionen und Tunables beschriebenen abstimmbaren Grenzwerte überschritten hat. Sie können die Richtlinie für das Scheitern von Pufferspeicherzuweisungen mit der Option bufresize konfigurieren, die standardmäßig auf auto gesetzt ist. Im Rahmen der auto-Pufferrichtlinie für die Größenänderung wird die Größe eines Puffers so lange halbiert, bis eine erfolgreiche Speicherzuweisung stattfindet. Wenn ein Puffer nach der Speicherzuweisung kleiner ist, als angefordert wurde, generiert dtrace(1M) eine Meldung:


# dtrace -P syscall -b 4g
dtrace: description 'syscall' matched 430 probes
dtrace: buffer size lowered to 128m
...

oder:


# dtrace -P syscall'{@a[probefunc] = count()}' -x aggsize=1g
dtrace: description 'syscall' matched 430 probes
dtrace: aggregation size lowered to 128m
...

Alternativ können Sie nach der gescheiterten Pufferspeicherzuweisung einen manuellen Eingriff anfordern, indem Sie bufresize auf manual setzen. Bei dieser Richtlinie bedeutet eine fehlgeschlagene Speicherzuweisung, dass DTrace nicht starten kann:


# dtrace -P syscall -x bufsize=1g -x bufresize=manual
dtrace: description 'syscall' matched 430 probes
dtrace: could not enable tracing: Not enough space
#

Die Änderungsrichtlinie für die Größe aller Puffer, also der Haupt-, spekulativen und Aggregatpuffer, ist durch die Option bufresize vorgegeben.