Handbuch zur dynamischen Ablaufverfolgung in Solaris

Daten aufzeichnende Aktionen

Die Daten aufzeichnenden Aktionen stellen die zentralen Aktionen von DTrace dar. Sie alle zeichnen standardmäßig Daten im Hauptpuffer auf, ermöglichen aber auch das Aufzeichnen von Daten in spekulativen Puffern. Ausführliche Informationen zum Hauptpuffer finden Sie in Kapitel 11Puffer und Pufferung. Ausführliche Informationen zu spekulativen Puffern finden Sie in Kapitel 13Spekulative Ablaufverfolgung. Die Beschreibungen in diesem Abschnitt beziehen sich lediglich auf den Zielpuffer, wobei angegeben wird, ob die Daten im Hauptpuffer oder, wenn auf die Aktion die Funktion speculate() folgt, in einem spekulativen Puffer aufgezeichnet wird.

trace()

void trace(Ausdruck)

Die grundlegende Aktion ist trace(), die einen D-Ausdruck als Argument übernimmt und das Ergebnis im Zielpuffer aufzeichnet. Die folgenden Anweisungen sind Beispiele für trace()-Aktionen:

trace(execname);
trace(curlwpsinfo->pr_pri);
trace(timestamp / 1000);
trace(`lbolt);
trace("somehow managed to get here");

tracemem()

void tracemem(Adresse, size_t Anzahl-Byte)

Die Aktion tracemem() übernimmt als erstes Argument einen D-Ausdruck, Adresse, und als zweites Argument eine Konstante, Anzahl-Byte. tracemem() kopiert den Speicherinhalt aus der mit Adresse angegebenen Adresse über die mit Anzahl_Byte angegebene Länge in den Zielpuffer.

printf()

void printf(string Format, ...) 

Wie trace() verfolgt auch die Aktion printf() D-Ausdrücke. printf() ermöglicht jedoch eine gezielte Formatierung im printf(3C)-Stil. Wie bei printf(3C) bestehen die Parameter aus einer Format-Zeichenkette gefolgt von einer variablen Anzahl von Argumenten. Die Argumente werden standardmäßig im Zielpuffer aufgezeichnet. Anschließend werden die Argumente für die Ausgabe durch dtrace(1M) gemäß der angegebenen Format-Zeichenkette formatiert. So ließen sich etwa die ersten zwei Beispiele für trace() unter trace() in einer einzigen printf()-Aktion kombinieren:

printf("execname is %s; priority is %d", execname, curlwpsinfo->pr_pri);

Weitere Informationen zu printf() finden Sie in Kapitel 12Formatierung der Ausgabe.

printa()

void printa(Aggregation)
void printa(string Format, Aggregation)

Die Aktion printa() dient zum Anzeigen und Formatieren von Aggregaten. Weitere Informationen zu Aggregationen finden Sie in Kapitel 9Aggregate. Wenn kein Format angegeben wird, verfolgt printa() lediglich eine Direktive, eine Anweisung für den DTrace-Verbraucher, die besagt, dass das angegebene Aggregat verarbeitet und im Standardformat angezeigt werden soll. Wenn ein Format angegeben wurde, wird das Aggregat gemäß der Angabe formatiert. Kapitel 12Formatierung der Ausgabe enthält eine ausführlichere Beschreibung der printa()-Formatzeichenkette.

printa() zeichnet nur eine Direktive auf, die besagt, dass das Aggregat vom DTrace-Verbraucher verarbeitet werden soll. Das Aggregat im Kernel wird von der Aktion nicht verarbeitet. Deshalb hängt die Dauer zwischen der Ablaufverfolgung der printa()-Direktive und der tatsächlichen Verarbeitung der Direktive von den die Pufferverarbeitung beeinflussenden Faktoren ab. Bei diesen Faktoren handelt es sich um die Aggregationsfrequenz, die Pufferungsregel und, wenn letztere auf switching gesetzt ist, die Frequenz der Pufferumschaltung. Ausführliche Beschreibungen dieser Faktoren finden Sie in Kapitel 9Aggregate und·Kapitel 11Puffer und Pufferung.

stack()

void stack(int Anzahl_Frames)
void stack(void)

Die Aktion stack() zeichnet ein Kernel-Stackprotokoll im Zielpuffer auf. Dabei ist die Tiefe des Kernel-Stacks durch Anzahl_Frames vorgegeben. Wenn Anzahl_Frames nicht angegeben ist, werden so viele Stack-Frames aufgezeichnet, wie mit der Option stackframes angegeben wurden. Beispiel:


# dtrace -n uiomove:entry'{stack()}'
  CPU     ID                    FUNCTION:NAME
    0   9153                    uiomove:entry
                genunix`fop_write+0x1b
                namefs`nm_write+0x1d
                genunix`fop_write+0x1b
                genunix`write+0x1f7

    0   9153                    uiomove:entry
                genunix`fop_read+0x1b
                genunix`read+0x1d4

    0   9153                    uiomove:entry
                genunix`strread+0x394
                specfs`spec_read+0x65
                genunix`fop_read+0x1b
                genunix`read+0x1d4
   ...

Die Aktion stack() unterscheidet sich insofern leicht von anderen Aktionen, als sie auch als Schlüssel für Aggregate eingesetzt werden kann:


# dtrace -n kmem_alloc:entry'{@[stack()] = count()}'
dtrace: description 'kmem_alloc:entry' matched 1 probe
^C

                rpcmod`endpnt_get+0x47c
                rpcmod`clnt_clts_kcallit_addr+0x26f
                rpcmod`clnt_clts_kcallit+0x22
                nfs`rfscall+0x350
                nfs`rfs2call+0x60
                nfs`nfs_getattr_otw+0x9e
                nfs`nfsgetattr+0x26
                nfs`nfs_getattr+0xb8
                genunix`fop_getattr+0x18
                genunix`cstat64+0x30
                genunix`cstatat64+0x4a
                genunix`lstat64+0x1c
                  1

                genunix`vfs_rlock_wait+0xc
                genunix`lookuppnvp+0x19d
                genunix`lookuppnat+0xe7
                genunix`lookupnameat+0x87
                genunix`lookupname+0x19
                genunix`chdir+0x18
                  1

                rpcmod`endpnt_get+0x6b1
                rpcmod`clnt_clts_kcallit_addr+0x26f
                rpcmod`clnt_clts_kcallit+0x22
                nfs`rfscall+0x350
                nfs`rfs2call+0x60
                nfs`nfs_getattr_otw+0x9e
                nfs`nfsgetattr+0x26
                nfs`nfs_getattr+0xb8
                genunix`fop_getattr+0x18
                genunix`cstat64+0x30
                genunix`cstatat64+0x4a
                genunix`lstat64+0x1c
                  1
    ...

ustack()

void ustack(int Anzahl_Frames, int strsize)
void ustack(int Anzahl_Frames)
void ustack(void)

Die Aktion ustack() zeichnet ein Benutzer-Stackprotokoll im Zielpuffer auf. Dabei ist die Tiefe des Benutzer-Stacks durch Anzahl_Frames vorgegeben. Wenn Anzahl_Frames nicht angegeben ist, werden so viele Stack-Frames aufgezeichnet, wie mit der Option ustackframes angegeben wurden. Während ustack() in der Lage ist, die Adresse der aufrufenden Frames zum Zeitpunkt der Prüfpunktauslösung zu ermitteln, werden die Stack-Frames erst in Symbole übersetzt, wenn die Aktion ustack() auf Benutzerebene vom DTrace-Verbraucher verarbeitet wurde. Wenn Größe_Zeichenkette angegeben und nicht Null ist, reserviert ustack() den angegebenen Speicherplatz für die Zeichenkette und nutzt ihn dazu, die Adresse direkt aus dem Kernel in ein Symbol zu übersetzen. Diese direkte Benutzersymbol-Übersetzung ist derzeit nur für Java Virtual Machines der Version 1.5 und höher verfügbar. Die Adress/Symbol-Übersetzung in Java kennzeichnet Benutzer-Stacks, die Java-Frames enthalten, mit der Java-Klasse und dem Methodennamen. Wenn diese Frames nicht übersetzt werden können, erscheinen diese nur als hexadezimale Adressen.

Im nächsten Beispiel wird ein Stack ohne Speicherplatz für die Zeichenkette und folglich ohne Java-Adress/Symbol-Übersetzung verfolgt:


# dtrace -n syscall::write:entry'/pid == $target/{ustack(50, 0);
    exit(0)}' -c "java -version"
dtrace: description 'syscall::write:entry' matched 1 probe
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b58)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b58, mixed mode)
dtrace: pid 5312 has exited
CPU     ID                    FUNCTION:NAME
  0     35                      write:entry
              libc.so.1`_write+0x15
              libjvm.so`__1cDhpiFwrite6FipkvI_I_+0xa8
              libjvm.so`JVM_Write+0x2f
              d0c5c946
              libjava.so`Java_java_io_FileOutputStream_writeBytes+0x2c
              cb007fcd
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb002a7b
              cb000152
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_
                          pnGThread__v_+0x187
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_
                          pnGThread__v2468_v_+0x14
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_
                          pnRJavaCallArguments_pnGThread __v_+0x28
              libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_
                          pnI_jobject_nLJNICallType_pnK_jmethodID_pnSJNI_
                          ArgumentPusher_pnGThread__v_+0x180
              libjvm.so`jni_CallStaticVoidMethod+0x10f
              java`main+0x53d

Beachten Sie, dass die C- und C++-Stack-Frames aus der Java Virtual Machine symbolisch anhand von „verstümmelten“ C++-Symbolnamen und die Java-Stack-Frames nur als hexadezimale Adressen dargestellt werden. Das nächste Beispiel zeigt einen ustack()-Aufruf mit einer Zeichenkettengröße ungleich Null:


# dtrace -n syscall::write:entry'/pid == $target/{ustack(50, 500); exit(0)}'
      -c "java -version"
dtrace: description 'syscall::write:entry' matched 1 probe
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b58)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b58, mixed mode)
dtrace: pid 5308 has exited
CPU     ID                    FUNCTION:NAME
  0     35                      write:entry
              libc.so.1`_write+0x15
              libjvm.so`__1cDhpiFwrite6FipkvI_I_+0xa8
              libjvm.so`JVM_Write+0x2f
              d0c5c946
              libjava.so`Java_java_io_FileOutputStream_writeBytes+0x2c
              java/io/FileOutputStream.writeBytes
              java/io/FileOutputStream.write
              java/io/BufferedOutputStream.flushBuffer
              java/io/BufferedOutputStream.flush
              java/io/PrintStream.write
              sun/nio/cs/StreamEncoder$CharsetSE.writeBytes
              sun/nio/cs/StreamEncoder$CharsetSE.implFlushBuffer
              sun/nio/cs/StreamEncoder.flushBuffer
              java/io/OutputStreamWriter.flushBuffer
              java/io/PrintStream.write
              java/io/PrintStream.print
              java/io/PrintStream.println
              sun/misc/Version.print
              sun/misc/Version.print
              StubRoutines (1)
              libjvm.so`__1cJJavaCallsLcall_helper6FpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_pnGThread
                          __v_+0x187
              libjvm.so`__1cCosUos_exception_wrapper6FpFpnJJavaValue_
                          pnMmethodHandle_pnRJavaCallArguments_pnGThread
                          __v2468_v_+0x14
              libjvm.so`__1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle
                          _pnRJavaCallArguments_pnGThread__v_+0x28
              libjvm.so`__1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI
                          _jobject_nLJNICallType_pnK_jmethodID_pnSJNI
                          _ArgumentPusher_pnGThread__v_+0x180
              libjvm.so`jni_CallStaticVoidMethod+0x10f
              java`main+0x53d
              8051b9a

Die obige Beispielausgabe veranschaulicht die symbolischen Stack-Frame-Informationen zu Java-Stack-Frames. Dass weiterhin hexadezimale Frames in der Ausgabe enthalten sind, ist darauf zurückzuführen, dass einige Funktionen statisch sind und keine Einträge in der Anwendungssymboltabelle für sie vorliegen. Diese Frames können nicht übersetzt werden.

Die ustack()-Symbolübersetzung für Java-fremde Frames erfolgt nach der Aufzeichnung der Stackdaten. Folglich wird der entsprechende Benutzerprozess möglicherweise beendet, noch bevor die Symbolübersetzung durchgeführt werden kann. Dadurch wird die Stack-Frame-Übersetzung unmöglich. Wenn der Benutzerprozess vor der Symbolübersetzung beendet wird, gibt dtrace eine Warnmeldung wie im nächsten Beispiel, gefolgt von den hexadezimalen Stack-Frames aus:


  dtrace: failed to grab process 100941: no such process
                c7b834d4
                c7bca85d
                c7bca1a4
                c7bd4374
                c7bc2628
                8047efc

Verfahren zur Linderung dieses Problems werden in Kapitel 33Ablaufverfolgung von Benutzerprozessen beschrieben.

Da schließlich die DTrace-Befehle zur nachträglichen Fehleranalyse (Post-Mortem-Debugging) die Frame-Übersetzung nicht durchführen können, ergibt die Verwendung von ustack() mit der ring-Pufferregel stets unverarbeitete ustack()-Daten.

Das folgende D-Programm zeigt ein Beispiel für eine ustack()-Aktion ohne Angabe von Größe_Zeichenkette:

syscall::brk:entry
/execname == $$1/
{
	@[ustack(40)] = count();
}

Um dieses Beispiel auf den Webbrowser Netscape, .netscape.bin, in Solaris-Standardinstallationen anzuwenden, geben Sie folgenden Befehl ein:


# dtrace -s brk.d .netscape.bin
dtrace: description 'syscall::brk:entry' matched 1 probe
^C
                libc.so.1`_brk_unlocked+0xc
                88143f6
                88146cd
                .netscape.bin`unlocked_malloc+0x3e
                .netscape.bin`unlocked_calloc+0x22
                .netscape.bin`calloc+0x26
                .netscape.bin`_IMGCB_NewPixmap+0x149
                .netscape.bin`il_size+0x2f7
                .netscape.bin`il_jpeg_write+0xde
                8440c19
                .netscape.bin`il_first_write+0x16b
                8394670
                83928e5
                .netscape.bin`NET_ProcessHTTP+0xa6
                .netscape.bin`NET_ProcessNet+0x49a
                827b323
                libXt.so.4`XtAppProcessEvent+0x38f
                .netscape.bin`fe_EventLoop+0x190
                .netscape.bin`main+0x1875
                   1

                libc.so.1`_brk_unlocked+0xc
                libc.so.1`sbrk+0x29
                88143df
                88146cd
                .netscape.bin`unlocked_malloc+0x3e
                .netscape.bin`unlocked_calloc+0x22
                .netscape.bin`calloc+0x26
                .netscape.bin`_IMGCB_NewPixmap+0x149
                .netscape.bin`il_size+0x2f7
                .netscape.bin`il_jpeg_write+0xde
                8440c19
                .netscape.bin`il_first_write+0x16b
                8394670
                83928e5
                .netscape.bin`NET_ProcessHTTP+0xa6
                .netscape.bin`NET_ProcessNet+0x49a
                827b323
                libXt.so.4`XtAppProcessEvent+0x38f
                .netscape.bin`fe_EventLoop+0x190
                .netscape.bin`main+0x1875
                  1
    ...

jstack()

void jstack(int Anzahl_Frames, int Größe)
void jstack(int Anzahl_Frames)
void jstack(void)

jstack() ist ein Aliasname für ustack(). Dabei gilt als Anzahl der Stack-Frames der mit der Option jstackframes angegebene Wert und als Speicherplatz für die Zeichenkette der mit der Option jstackstrsize angegebene Wert. jstacksize nimmt standardmäßig einen Wert ungleich Null an. Das bedeutet, dass die Verwendung von jstack() einen Stack mit erfolgter Java-Frame-Übersetzung ergibt.