Handbuch zur dynamischen Ablaufverfolgung in Solaris

Kapitel 9 Aggregate

Für die Instrumentierung des Systems zur Beantwortung leistungsbezogener Fragestellungen bietet es sich an, zu überlegen, wie Daten zu einer Antwort auf eine bestimmte Frage zusammengefasst werden können, anstatt die Daten von mehreren verschiedenen Prüfpunkten erfassen zu lassen. Wenn beispielsweise die Anzahl der von einer Benutzer-ID durchgeführten Systemaufrufe festgestellt werden soll, sind die bei den einzelnen Systemaufrufen erfassten Daten wahrscheinlich weniger interessant. Wir möchten einfach nur eine Tabelle mit Benutzer-IDs und Systemaufrufen sehen. Traditionsgemäß würde man zum Beantworten dieser Frage bei jedem Systemaufruf Informationen sammeln und die Informationen mit einem Tool wie awk(1) oder perl(1) nachträglich verarbeiten. In DTrace ist die Zusammenfassung von Daten jedoch eine Operation der ersten Klasse. Dieses Kapitel befasst sich mit den DTrace-Einrichtungen für die Manipulation von Aggregaten.

Aggregatfunktionen

Eine Aggregatfunktion ist eine Funktion mit den folgenden Eigenschaften:

f(f(x0) U f(x 1) U ... U f(xn)) = f(x 0 U x1  U ... U xn)

wobei xn eine Menge beliebiger Daten ist. Das bedeutet, dass die Anwendung einer Aggregatfunktion auf Untergruppen des Ganzen und anschließend auf die Ergebnisse dasselbe Resultat erbringt, wie die Anwendung der Funktion auf das Ganze. Betrachten wir zum Beispiel eine Funktion SUM, die die Summe eines gegebenen Datensatzes ergibt. Wenn die unverarbeiteten Daten aus {2, 1, 2, 5, 4, 3, 6, 4, 2} bestehen, dann führt die Anwendung von SUM auf den gesamten Datensatz zum Ergebnis {29}. Analog führt die Anwendung von SUM auf die aus den ersten drei Elementen bestehende Untergruppe zum·Ergebnis \{5\}. Wenn wir SUM auf die drei nachfolgenden Elemente anwenden, erhalten wir \{12\}, und die Anwendung von SUM auf die übrigen drei Elemente ergibt ebenso \{12}. SUM ist eine Aggregatfunktion, da wir durch ihre Anwendung auf die Menge der Ergebnisse, {5, 12, 12}, dasselbe Ergebnis erhalten wie bei Anwendung von SUM auf die ursprünglichen Daten, nämlich \{29\}.

Nicht alle Funktionen haben diese zusammenfassende Wirkung. Ein Beispiel für eine Nicht-Aggregatfunktion ist die Funktion MEDIAN, die das mittlere Element eines Datensatzes ermittelt. (Ein Element, für das in einem Datensatz gleich viele größere wie kleinere Elemente vorhanden sind, ist das mittlere Element im Datensatz.) Der MEDIAN wird durch Sortieren des Datensatzes und Auswahl des mittleren Elements abgeleitet. Kehren wir zu den ursprünglichen unverarbeiteten Daten zurück und wenden wir MEDIAN auf die Untergruppe der ersten drei Elemente an. Das Ergebnis ist {2}. (Der sortierte Satz lautet {1, 2, 2}; {2} ist der aus dem mittleren Element bestehende Satz.) Analog ergibt die Anwendung von MEDIAN auf die nächsten drei Elemente \{4\} und die Anwendung von MEDIAN auf die letzten drei Elemente ebenfalls \{4}. Wenn wir MEDIAN auf die einzelnen Untergruppen anwenden, erhalten wir also den Datensatz {2, 4, 4}. Wenden wir MEDIAN nun auf diesen Satz an, beträgt das Ergebnis {4}. Wenn wir aber den ursprünglichen Satz sortieren, erhalten wir {1, 2, 2, 2, 3, 4, 4, 5, 6}. Das Ergebnis der Anwendung von MEDIAN auf diesen Satz beträgt {3}. Da diese beiden Ergebnisse nicht übereinstimmen, ist MEDIAN keine Aggregatfunktion.

Viele gebräuchliche Funktionen für die Analyse von Datensätzen sind Aggregatfunktionen. Dazu gehören Funktionen zum Zählen der Elemente im Datensatz, zum Berechnen des niedrigsten oder des höchsten Werts im Datensatz und zum Addieren aller Elemente im Datensatz. Die Bestimmung des arithmetischen Mittels einer Menge lässt sich aus der Funktion zum Zählen der Elemente und jener zum Berechnen der Summe aller Elemente im Datensatz kombinieren.

Verschiedene nützliche Funktionen sind jedoch keine Aggregatfunktionen. Hierzu gehören Funktionen zum Berechnen des Modus (des häufigsten Elements), des Mittelwerts oder der Standardabweichung einer Menge.

Die Anwendung von Aggregatfunktionen auf Daten während der Ablaufverfolgung bringt eine Reihe von Vorteilen mit:

Aggregate

DTrace speichert die Ergebnisse von Aggregatfunktionen in einem Objekt namens Aggregat. Die Aggregatergebnisse werden ähnlich wie bei assoziativen Vektoren mit einem Tupel von Ausdrücken indiziert. Die Syntax für Aggregate in D lautet:

@Name[ Schlüssel ] = Aggfunk ( Args );

wobei Name für den Namen des Aggregats, Schlüssel für eine Liste mit Komma getrennter D-Ausdrücke, Aggfunkt für eine der DTrace-Aggregatfunktionen und Argumente für eine Liste mit Komma getrennter Argumente für die Aggregatfunktion stehen. Der Aggregatname ist ein D-Bezeichner mit dem vorangestellten Sonderzeichen @. Alle in Ihren D-Programmen benannten Aggregate sind globale Variablen. Es gibt keine thread- oder klausel-lokalen Aggregate. Die Aggregatnamen werden, getrennt von anderen globalen D-Variablen, in einem separaten Bezeichner-Namensraum geführt. Denken Sie bei der Wiederverwendung von Namen daran, dass a und @a nicht dieselbe Variable sind. Der spezielle Aggregatname @ kann in einfachen D-Programmen zum Benennen anonymer Aggregate eingesetzt werden. Der D-Compiler behandelt den Namen als Alias für den Aggregatnamen @_.

Die folgende Tabelle zeigt die Aggregatfunktionen von DTrace. Die meisten Aggregatfunktionen nehmen nur ein einziges Argument an, das die neue Information darstellt.

Tabelle 9–1 Aggregatfunktionen in DTrace

Funktionsname 

Argumente 

Ergebnis 

count

none 

Anzahl der Aufrufe. 

sum

Skalarer Ausdruck 

Gesamtwert der angegebenen Ausdrücke. 

avg

Skalarer Ausdruck 

Arithmetisches Mittel der angegebenen Ausdrücke. 

min

Skalarer Ausdruck 

Kleinster Wert in den angegebenen Ausdrücken. 

max

Skalarer Ausdruck 

Größter Wert in den angegebenen Ausdrücken. 

lquantize

Skalarer Ausdruck, Untergrenze, Obergrenze, Schrittwert 

Lineare Häufigkeitsverteilung der Werte der angegebenen Ausdrücke, beschränkt durch den angegebenen Bereich. Inkrementiert den Wert in der höchsten Menge, die kleiner als der angegebene Ausdruck ist.

quantize

Skalarer Ausdruck 

Eine Quadrat-Häufigkeitsverteilung der Werte der angegebenen Ausdrücke. Inkrementiert den Wert in der höchsten Quadratmenge, die kleiner als der angegebene Ausdruck ist.

Wenn beispielsweise die Anzahl der write(2)-Systemaufrufe im System gezählt werden sollen, könnten Sie eine informative Zeichenkette als Schlüssel und die Aggregatfunktion count() verwenden:

syscall::write:entry
{
	@counts["write system calls"] = count();
}

Der Befehl dtrace gibt die Aggregatergebnisse standardmäßig nach Abschluss des Prozesses aus, entweder als Resultat einer expliziten END-Aktion oder auf die Betätigung von Strg-C durch den Benutzer hin. Die folgende Beispielausgabe zeigt das Ergebnis nach Ausführung dieses Befehls, einer Wartezeit von einigen Sekunden und der anschließenden Betätigung von Strg-C:


# dtrace -s writes.d
dtrace: script './writes.d' matched 1 probe
^C

  write system calls                                              179
#

Mit der Variable execname als Schlüssel für ein Aggregat lassen sich die Systemaufrufe per Prozessnamen zählen:

syscall::write:entry
{
	@counts[execname] = count();
}

Die folgende Beispielausgabe zeigt das Ergebnis nach Ausführung dieses Befehls, einer Wartezeit von mehreren Sekunden und Drücken von Strg-C:


# dtrace -s writesbycmd.d
dtrace: script './writesbycmd.d' matched 1 probe
^C

  dtrace                                                            1
  cat                                                               4
  sed                                                               9
  head                                                              9
  grep                                                             14
  find                                                             15
  tail                                                             25
  mountd                                                           28
  expr                                                             72
  sh                                                              291
  tee                                                             814
  def.dir.flp                                                    1996
  make.bin                                                       2010
#

Alternativ können die Schreibzugriffe nach Prozessnamen und Dateibezeichner zusammen organisiert werden. Der Dateibezeichner ist das erste Argument für write(2). Im folgenden Beispiel wird also ein aus execname und arg0 zusammengesetzter Schlüssel verwendet:

syscall::write:entry
{
	@counts[execname, arg0] = count();
}

Die Ausführung dieses Befehls ergibt eine Tabelle wie in folgendem Beispiel, mit Prozessnamen und Dateibezeichner:


# dtrace -s writesbycmdfd.d
dtrace: script './writesbycmdfd.d' matched 1 probe
^C

  cat                                                               1      58
  sed                                                               1      60
  grep                                                              1      89
  tee                                                               1     156
  tee                                                               3     156
  make.bin                                                          5     164
  acomp                                                             1     263
  macrogen                                                          4     286
  cg                                                                1     397
  acomp                                                             3     736
  make.bin                                                          1     880
  iropt                                                             4    1731
#

Im nächsten Beispiel wird die durchschnittlich im write-Systemaufruf verbrachte Zeit je Prozess angezeigt. Dabei wird der Aggregatfunktion avg() der Ausdruck als Argument übergeben, aus dem der Durchschnitt berechnet werden soll. In diesem Beispiel wird der Durchschnitt der im Systemaufruf abgelaufenen Gesamtzeit berechnet:

syscall::write:entry
{
	self->ts = timestamp;
}

syscall::write:return
/self->ts/
{
	@time[execname] = avg(timestamp - self->ts);
	self->ts = 0;
}

Die folgende Beispielausgabe zeigt das Ergebnis nach Ausführung dieses Befehls, einer Wartezeit von mehreren Sekunden und Drücken von Strg-C:


# dtrace -s writetime.d
dtrace: script './writetime.d' matched 2 probes
^C

  iropt                                                         31315
  acomp                                                         37037
  make.bin                                                      63736
  tee                                                           68702
  date                                                          84020
  sh                                                            91632
  dtrace                                                       159200
  ctfmerge                                                     321560
  install                                                      343300
  mcs                                                          394400
  get                                                          413695
  ctfconvert                                                   594400
  bringover                                                   1332465
  tail                                                        1335260
#

Ein Durchschnittswert kann zwar nützlich sein, liefert aber häufig nicht genug Detailinformationen zum Verstehen der Verteilung von Datenpunkten. Um die Verteilung genauer zu verstehen, setzen wir wie folgt die Aggregatfunktion quantize() ein:

syscall::write:entry
{
	self->ts = timestamp;
}

syscall::write:return
/self->ts/
{
	@time[execname] = quantize(timestamp - self->ts);
	self->ts = 0;
}

Da aus jeder Ausgabezeile ein Häufigkeitsverteilungsdiagramm wird, fällt die Ausgabe dieses Skripts bedeutend länger aus als die vorherigen. Dieses Beispiel zeigt nur eine Auswahl der Beispielausgabe:


  lint
           value  ------------- Distribution ------------- count
            8192 |                                         0
           16384 |                                         2
           32768 |                                         0
           65536 |@@@@@@@@@@@@@@@@@@@                      74
          131072 |@@@@@@@@@@@@@@@                          59
          262144 |@@@                                      14
          524288 |                                         0

  acomp
           value  ------------- Distribution ------------- count
            4096 |                                         0
            8192 |@@@@@@@@@@@@                             840
           16384 |@@@@@@@@@@@                              750
           32768 |@@                                       165
           65536 |@@@@@@                                   460
          131072 |@@@@@@                                   446
          262144 |                                         16
          524288 |                                         0
         1048576 |                                         1
         2097152 |                                         0

  iropt
           value  ------------- Distribution ------------- count
            4096 |                                         0
            8192 |@@@@@@@@@@@@@@@@@@@@@@@                  4149
           16384 |@@@@@@@@@@                               1798
           32768 |@                                        332
           65536 |@                                        325
          131072 |@@                                       431
          262144 |                                         3
          524288 |                                         2
         1048576 |                                         1
         2097152 |                                         0        

Beachten Sie, dass die Zeilen der Häufigkeitsverteilung immer Zweierpotenzen darstellen. Jede Zeile gibt die Anzahl der Elemente an, die größer gleich dem entsprechenden Wert, aber kleiner als der Wert der nächsthöheren Zeile sind. So zeigt beispielsweise die obige Ausgabe, dass iropt 4.149 Schreibzugriffe mit einer Dauer von 8.192 bis 16.383 (einschließlich) Nanosekunden hatte.

quantize() eignet sich für einen schnellen Einblick in die Daten, aber nicht so sehr, wenn Sie eine Verteilung über lineare Werte untersuchen müssen. Zum Anzeigen einer linearen Werteverteilung verwenden Sie die Aggregatfunktion lquantize. () Die Funktion lquantize() nimmt zusätzlich zu einem D-Ausdruck drei Argumente an: eine Untergrenze, eine Obergrenze und eine Schrittweite. Zur Betrachtung der Verteilung der Schreibzugriffe nach Dateibezeichner wäre eine Zweierpotenz-Quantisierung beispielsweise nicht wirkungsvoll. Gehen Sie hierzu stattdessen mit einer linearen Quantisierung und kleinem Wertebereich vor, wie im nächsten Beispiel dargestellt:

syscall::write:entry
{
	@fds[execname] = lquantize(arg0, 0, 100, 1);
}

Wenn Sie dieses Skript einige Sekunden lang ausführen, erhalten Sie eine große Menge an Informationen. Dieses Beispiel zeigt einen Ausschnitt einer typischen Ausgabe:


  mountd
           value  ------------- Distribution ------------- count
              11 |                                         0
              12 |@                                        4
              13 |                                         0
              14 |@@@@@@@@@@@@@@@@@@@@@@@@@                70
              15 |                                         0
              16 |@@@@@@@@@@@@                             34
              17 |                                         0

  xemacs-20.4
           value  ------------- Distribution ------------- count
               6 |                                         0
               7 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  521
               8 |                                         0
               9 |                                         1
              10 |                                         0

  make.bin
           value  ------------- Distribution ------------- count
               0 |                                         0
               1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  3596
               2 |                                         0
               3 |                                         0
               4 |                                         42
               5 |                                         50
               6 |                                         0

  acomp
           value  ------------- Distribution ------------- count
               0 |                                         0
               1 |@@@@@                                    1156
               2 |                                         0
               3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@         6635
               4 |@                                        297
               5 |                                         0

  iropt
           value  ------------- Distribution ------------- count
               2 |                                         0
               3 |                                         299
               4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@  20144
               5 |                                         0        

Die Funktion lquantize() ermöglicht außerdem die Zusammenfassung der Zeit ab einem bestimmten Punkt in der Vergangenheit. Mit dieser Technik können Sie Änderungen des Verhaltens im Verlauf der Zeit beobachten. Das folgende Beispiel zeigt die Änderung im Systemaufruf-Verhalten über die Lebensdauer eines Prozesses, der den Befehl date(1) ausführt:

syscall::exec:return,
syscall::exece:return
/execname == "date"/
{
	self->start = vtimestamp;
}

syscall:::entry
/self->start/
{
	/*
	 * We linearly quantize on the current virtual time minus our
	 * process's start time.  We divide by 1000 to yield microseconds
	 * rather than nanoseconds.  The range runs from 0 to 10 milliseconds
	 * in steps of 100 microseconds; we expect that no date(1) process
	 * will take longer than 10 milliseconds to complete.
	 */
	@a["system calls over time"] =
	    lquantize((vtimestamp - self->start) / 1000, 0, 10000, 100);
}

syscall::rexit:entry
/self->start/
{
	self->start = 0;
}

Je mehr date(1)-Prozesse ausgeführt werden, desto tiefer der Einblick in das Systemaufruf-Verhalten, das das obige Skript bietet. Um das Ergebnis zu sehen, führen Sie sh -c 'while true; do date >/dev/null; done' in einem Fenster und das D-Skript in einem anderen aus. Das Skript erzeugt ein Profil des Systemaufruf-Verhaltens des Befehls date(1):


# dtrace -s dateprof.d
dtrace: script './dateprof.d' matched 218 probes
^C

  system calls over time
           value  ------------- Distribution ------------- count
             < 0 |                                         0
               0 |@@                                       20530
             100 |@@@@@@                                   48814
             200 |@@@                                      28119
             300 |@                                        14646
             400 |@@@@@                                    41237
             500 |                                         1259
             600 |                                         218
             700 |                                         116
             800 |@                                        12783
             900 |@@@                                      28133
            1000 |                                         7897
            1100 |@                                        14065
            1200 |@@@                                      27549
            1300 |@@@                                      25715
            1400 |@@@@                                     35011
            1500 |@@                                       16734
            1600 |                                         498
            1700 |                                         256
            1800 |                                         369
            1900 |                                         404
            2000 |                                         320
            2100 |                                         555
            2200 |                                         54
            2300 |                                         17
            2400 |                                         5
            2500 |                                         1
            2600 |                                         7
            2700 |                                         0        

Diese Ausgabe gibt einen groben Eindruck der verschiedenen Phasen des Befehls date(1) in Bezug auf die vom Kernel benötigten Dienste. Zum besseren Verständnis dieser Phasen kann es nützlich sein, festzustellen, welche Systemaufrufe wann stattfinden. Dafür könnten wir das D-Skript so abändern, dass die Aggregation an der Variable probefunc anstatt an einer konstanten Zeichenkette erfolgt.

Anzeige von Aggregaten

Standardmäßig werden Aggregate in der Reihenfolge ihres Auftretens im D-Programm angezeigt. Mit der Funktion printa() für die Ausgabe der Aggregate können Sie dieses Verhalten außer Kraft setzen. Die Funktion printa() bietet auch die Möglichkeit, die Aggregatdaten genau zu formatieren. Hierzu setzen Sie, wie in Kapitel 12Formatierung der Ausgabe beschrieben, eine Formatzeichenkette ein.

Wenn ein Aggregat nicht durch eine printa()-Anweisung im D-Programm formatiert wird, erstellt der Befehl dtrace eine Momentaufnahme der Aggregatdaten und gibt die Ergebnisse nach Abschluss der Ablaufverfolgung im Standard-Aggregatformat aus. Bei Verwendung einer printa()-Anweisung zur Formatierung eines bestimmten Aggregats wird das Standardverhalten deaktiviert. Indem Sie die Anweisung printa(@ Aggregatname) in eine dtrace:::END-Prüfpunktklausel in Ihrem Programm einfügen, erhalten Sie äquivalente Ergebnisse. Im Standardausgabeformat für die Aggregatfunktionen avg(), count(), min(), max() und sum() wird ein ganzzahliger Dezimalwert für den Aggregatwert jedes Tupels angezeigt. Das Standardausgabeformat der Aggregatfunktionen lquantize() und quantize() zeigt die Ergebnisse in Form einer ASCII-Tabelle. Aggregat-Tupel werden so ausgegeben, als hätte man trace() auf jedes Tupelelement angewendet.

Datennormalisierung

Beim Anhäufen von Daten über einen gewissen Zeitraum kann es hilfreich sein, die Daten in Bezug auf einen konstanten Faktor zu normalisieren. Dank dieser Technik lassen sich unzusammenhängende Daten leichter vergleichen. So sollen möglicherweise beim Aggregieren von Systemaufrufen die Systemaufrufe nicht als absoluter Wert über den Verlauf der Ausführung, sondern in Häufigkeit pro Sekunde angezeigt werden. Die DTrace-Aktion normalize() ermöglicht eine derartige Normalisierung der Daten. Die Parameter für normalize() sind ein Aggregat und ein Normalisierungsfaktor. Die Ausgabe der Aggregatfunktion zeigt jeden Wert dividiert durch den Normalisierungsfaktor.

Das folgende Beispiel verdeutlicht das Aggregieren von Daten nach Systemaufruf:

#pragma D option quiet

BEGIN
{
	/*
	 * Get the start time, in nanoseconds.
	 */
	start = timestamp;
}

syscall:::entry
{
	@func[execname] = count();
}

END
{
	/*
	 * Normalize the aggregation based on the number of seconds we have
	 * been running.  (There are 1,000,000,000 nanoseconds in one second.)
	 */
	normalize(@func, (timestamp - start) / 1000000000);
}

Wenn das obige Skript kurz ausgeführt wird, erhalten wir auf einem Desktoprechner die folgende Ausgabe:


# dtrace -s ./normalize.d
 ^C
  syslogd                                                           0
  rpc.rusersd                                                       0
  utmpd                                                             0
  xbiff                                                             0
  in.routed                                                         1
  sendmail                                                          2
  echo                                                              2
  FvwmAuto                                                          2
  stty                                                              2
  cut                                                               2
  init                                                              2
  pt_chmod                                                          3
  picld                                                             3
  utmp_update                                                       3
  httpd                                                             4
  xclock                                                            5
  basename                                                          6
  tput                                                              6
  sh                                                                7
  tr                                                                7
  arch                                                              9
  expr                                                             10
  uname                                                            11
  mibiisa                                                          15
  dirname                                                          18
  dtrace                                                           40
  ksh                                                              48
  java                                                             58
  xterm                                                           100
  nscd                                                            120
  fvwm2                                                           154
  prstat                                                          180
  perfbar                                                         188
  Xsun                                                           1309
  .netscape.bin                                                  3005

normalize() legt den Normalisierungsfaktor für das angegebene Aggregat fest. Die zugrunde liegenden Daten werden dadurch jedoch nicht geändert. denormalize() übernimmt nur ein Aggregat. Wenn wir dem vorigen Beispiel die Denormalisierungsaktion hinzufügen, erhalten wir sowohl die unverarbeitete Anzahl der Systemaufrufe als auch die Frequenz pro Sekunde:

#pragma D option quiet

BEGIN
{
	start = timestamp;
}

syscall:::entry
{
	@func[execname] = count();
}

END
{
	this->seconds = (timestamp - start) / 1000000000;
	printf("Ran for %d seconds.\n", this->seconds);

	printf("Per-second rate:\n");
	normalize(@func, this->seconds);
	printa(@func);

	printf("\nRaw counts:\n");
	denormalize(@func);
	printa(@func);
}

Wenn das obige Skript kurz ausgeführt wird, erhalten wir eine ähnliche Ausgabe wie in folgendem Beispiel:


# dtrace -s ./denorm.d
^C
Ran for 14 seconds.
Per-second rate:

  syslogd                                                           0
  in.routed                                                         0
  xbiff                                                             1
  sendmail                                                          2
  elm                                                               2
  picld                                                             3
  httpd                                                             4
  xclock                                                            6
  FvwmAuto                                                          7
  mibiisa                                                          22
  dtrace                                                           42
  java                                                             55
  xterm                                                            75
  adeptedit                                                       118
  nscd                                                            127
  prstat                                                          179
  perfbar                                                         184
  fvwm2                                                           296
  Xsun                                                            829

Raw counts:

  syslogd                                                           1
  in.routed                                                         4
  xbiff                                                            21
  sendmail                                                         30
  elm                                                              36
  picld                                                            43
  httpd                                                            56
  xclock                                                           91
  FvwmAuto                                                        104
  mibiisa                                                         314
  dtrace                                                          592
  java                                                            774
  xterm                                                          1062
  adeptedit                                                      1665
  nscd                                                           1781
  prstat                                                         2506
  perfbar                                                        2581
  fvwm2                                                          4156
  Xsun                                                          11616

Aggregate können auch renormalisiert werden. Wenn normalize() mehrmals für dasselbe Aggregat aufgerufen wird, gilt der im neuesten Aufruf angegebene Normalisierungsfaktor. Das folgende Beispiel gibt die Häufigkeit pro Sekunde im Verlauf der Zeit aus:


Beispiel 9–1 renormalize.d: Renormalisierung eines Aggregats

#pragma D option quiet

BEGIN
{
	start = timestamp;
}

syscall:::entry
{
	@func[execname] = count();
}

tick-10sec
{
	normalize(@func, (timestamp - start) / 1000000000);
	printa(@func);
}

Löschen von Aggregaten

Wenn Sie mit DTrace einfache Überwachungsskripten erzeugen, können Sie die Werte in einem Aggregat mit der Funktion clear() regelmäßig löschen lassen. Diese Funktion nimmt als einzigen Parameter ein Aggregat an. Die Funktion clear() löscht nur die Werte des Aggregats; die Aggregatschlüssel bleiben erhalten. Ein Schlüssel mit dem Wert Null in einem Aggregat deutet folglich darauf hin, dass der Schlüssel zuvor einen Nicht-Nullwert besessen hatte, der bei einem clear()-Vorgang auf Null gesetzt wurde. Um sowohl die Werte als auch die Schlüssel eines Aggregats zu löschen, verwenden Sie die Funktion trunc(). Ausführliche Informationen dazu finden Sie unter·Abschneiden von Aggregaten.

Im nächsten Beispiel fügen wir clear() in Beispiel 9–1 ein:

#pragma D option quiet

BEGIN
{
	last = timestamp;
}

syscall:::entry
{
	@func[execname] = count();
}

tick-10sec
{
	normalize(@func, (timestamp - last) / 1000000000);
	printa(@func);
	clear(@func);
	last = timestamp;
}

Während Beispiel 9–1 die Frequenz der Systemaufrufe über die Lebensdauer des dtrace-Aufrufs zeigt, gibt das obige Beispiel nur die Rate für die letzten zehn Sekunden aus.

Abschneiden von Aggregaten

Bei der Betrachtung von Aggregatergebnissen interessieren häufig nur die oberen Resultate. Die zu allen anderen außer den höchsten Werten gehörenden Schlüssel und Werte sind irrelevant. Auch kann es nützlich sein, durch Entfernen der Schlüssel und der Werte ein ganzes Aggregatergebnis zu löschen. Zu beiden Zwecken dient die DTrace-Funktion trunc().

Die Parameter für trunc() sind ein Aggregat und ein optionaler Kürzungswert. Ohne Kürzungswert verwirft trunc() sowohl die Aggregatwerte als auch die Aggregatschlüssel für das gesamte Aggregat. Ist ein Kürzungswert n vorhanden, löscht trunc() die Aggregatwerte und -schlüssel mit Ausnahme der Werte und Schlüssel, die zu den n höchsten Werten gehören. Das heißt, dass trunc(@foo, 10) das Aggregat namens foo nach den zehn höchsten Werten abschneidet, während trunc(@foo) das gesamte Aggregat löscht. Es wird auch dann das gesamte Aggregat gelöscht, wenn als Kürzungswert 0 angegeben wurde.

Um anstelle der n oberen die n unteren Werte anzuzeigen, übergeben Sie der Funktion trunc() einen negativen Kürzungswert. So schneidet beispielsweise trunc(@foo, -10) das Aggregat foo hinter den zehn niedrigsten Werten ab.

Erweitern wir nun das Systemaufruf-Beispiel so, dass über einen Zeitraum von zehn Sekunden die Häufigkeit der Systemaufrufe pro Sekunde für die zehn oberen aufrufenden Anwendungen angezeigt wird:

#pragma D option quiet

BEGIN
{
	last = timestamp;
}

syscall:::entry
{
	@func[execname] = count();
}

tick-10sec
{
	trunc(@func, 10);
	normalize(@func, (timestamp - last) / 1000000000);
	printa(@func);
	clear(@func);
	last = timestamp;
}

Das nächste Beispiel zeigt die Ausgabe des obigen Skripts bei Ausführung auf einem Laptop mit geringer Systemlast:


  FvwmAuto                                                          7
  telnet                                                           13
  ping                                                             14
  dtrace                                                           27
  xclock                                                           34
  MozillaFirebird-                                                 63
  xterm                                                           133
  fvwm2                                                           146
  acroread                                                        168
  Xsun                                                            616

  telnet                                                            4
  FvwmAuto                                                          5
  ping                                                             14
  dtrace                                                           27
  xclock                                                           35
  fvwm2                                                            69
  xterm                                                            70
  acroread                                                        164
  MozillaFirebird-                                                491
  Xsun                                                           1287

Minimieren von Auslassungen

Da DTrace einige Aggregatdaten im Kernel zwischenspeichert, kann es unter Umständen vorkommen, dass nach Hinzufügen eines neuen Schlüssels für ein Aggregat nicht genügend Speicherplatz zur Verfügung steht. In diesem Fall werden die Daten ausgelassen, ein Zähler wird erhöht, und dtrace generiert eine Meldung über die Aggregatauslassung. Diese Situation ist eher unwahrscheinlich, da DTrace den Dauerstatus (bestehend aus Aggregatschlüssel und Zwischenergebnis) auf Benutzerebene hält, wo der Speicherplatz dynamisch erweitert werden kann. Sollten tatsächlich Aggregatauslassungen auftreten, können Sie die Puffergröße für Aggregate mit der Option aggsize erhöhen, um dieses Risiko herabzusetzen. Diese Option ermöglicht außerdem die Minimierung des Speicherplatzbedarfs von DTrace. Wie jede Größenoption kann auch aggsize mit jedem Größensuffix angegeben werden. Die Richtlinien zur Veränderung der Größe dieses Puffers wird von der Option bufresize vorgegeben. Weitere Informationen zur Pufferung finden Sie in Kapitel 11Puffer und Pufferung. Weitere Informationen zu Optionen finden Sie in Kapitel 16Optionen und Tunables .

Eine alternative Methode zur Vermeidung von Aggregatauslassungen besteht darin, die Häufigkeit zu erhöhen, mit der die Aggregatdaten auf Benutzerebene verbraucht werden. Standardmäßig beläuft sich diese Rate auf einmal pro Sekunde, sie kann aber mit der Option aggrate angepasst werden. Wie jede Frequenzoption kann auch aggrate mit einem beliebigen Zeitsuffix angegeben werden. Standardmäßig gilt Häufigkeit pro Sekunde. Weitere Informationen zur Option aggsize finden Sie in Kapitel 16Optionen und Tunables .