Manuel de suivi dynamique Solaris

Chapitre 10 Actions et sous-routines

Vous pouvez utiliser des appels de fonction D tels que trace() et printf()pour appeler deux types de service différents fournis par DTrace : lesactions d'une part, qui suivent les données ou modifient des états externes à DTrace et les sous-routines d'autre part, qui affectent uniquement des états internes à DTrace. Ce chapitre définit les actions et sous-routines et décrit leur syntaxe et sémantique.

Actions

Les actions permettent à vos programmes DTrace d'interagir avec le système à l'extérieur de DTrace. Les actions les plus courantes enregistrent des données vers un tampon de DTrace. D'autres actions sont disponibles, telles que l'arrêt du processus en cours, l'augmentation d'un signal spécifique sur le processus en cours ou encore l'interruption totale du suivi. Certaines de ces actions sont dites destructrices dans la mesure où elles modifient radicalement le système. Il n'est possible de les utiliser que si elles ont été activées de façon explicite. Par défaut, les actions d'enregistrement de données enregistrent des données dans le tampon principal. Pour de plus amples informations sur le tampon principal et les stratégies de tampon, reportez-vous au Chapitre11Tampons et mise en tampon.

Action par défaut

Une clause peut contenir un nombre quelconque d'actions et de manipulations de variables. Si une clause reste vide, l'action par défaut s'applique. L'action par défaut consiste à suivre l'identificateur de la sonde activée (EPID) sur le tampon principal. L'EPID identifie l'activation d'une sonde donnée avec un prédicat et des actions spécifiques. À partir de l'EPID, les consommateurs DTrace peuvent identifier la sonde à l'origine de l'action. En effet, lorsque des données font l'objet d'un suivi, elles doivent être accompagnées de l'EPID pour que le consommateur puisse les interpréter. L'action par défaut consiste donc à procéder exclusivement au suivi de l'EPID.

L'utilisation de l'action par défaut permet une utilisation simple de dtrace(1M). Par exemple, la commande suivante, donnée à titre d'exemple, permet d'activer toutes les sondes du module de planification de temps partagé TS avec l'action par défaut :


# dtrace -m TS

La commande précédente donne une sortie similaire à ce qui suit :


# dtrace -m TS
dtrace: description 'TS' matched 80 probes
CPU     ID                    FUNCTION:NAME
  0  12077                 ts_trapret:entry 
  0  12078                ts_trapret:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12081                  ts_wakeup:entry 
  0  12082                 ts_wakeup:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12033                  ts_setrun:entry 
  0  12034                 ts_setrun:return 
  0  12069                   ts_sleep:entry 
  0  12070                  ts_sleep:return 
  0  12023                  ts_update:entry 
  0  12079             ts_update_list:entry 
  0  12080            ts_update_list:return 
  0  12079             ts_update_list:entry 
...

Actions d'enregistrement de données

Les actions d'enregistrement de données constituent les actions principales de DTrace. Chacune de ces actions enregistre des données dans le tampon principal par défaut mais elles peuvent également en enregistrer dans des tampons spéculatifs. Pour de plus amples informations sur le tampon principal, reportez-vous au Chapitre11Tampons et mise en tampon. Pour de plus amples informations sur les tampons spéculatifs, reportez-vous au Chapitre13Suivi spéculatif. Les descriptions données dans cette section se rapportent uniquement au tampon spécifié. Elles indiquent si les données sont enregistrées dans le tampon principal ou dans un tampon spéculatif, si l'action fait suite à une action speculate().

trace()

void trace(expression)

L'action la plus élémentaire est l'action trace(), qui prend comme argument une expression D et procède au suivi du résultat dans le tampon spécifié. Les instructions suivantes sont des exemples d'actions trace() :

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

tracemem()

void tracemem(address, size_t nbytes)

L'action tracemem() prend comme premier argument une expression D, address, et comme second argument une constante, nbytes. tracemem() copie la mémoire à partir de l'adresse addr dans le tampon approprié en respectant la longueur nbytes.

printf()

void printf(string format, ...) 

De la même manière que l'action trace(), l'action printf() procède au suivi d'expressions D. Toutefois, printf() autorise un formatage de style printf(3C). Comme printf(3C), les paramètres se composent d'une chaîne format suivie d'un nombre variable d'arguments. Par défaut, les arguments font l'objet d'un suivi dans le tampon spécifié. Les arguments sont ensuite formatés pour la sortie dans dtrace(1M) en fonction de la chaîne de format spécifiée. Par exemple, les deux premiers exemples de trace() de la section trace() peuvent être combinés dans une seule action printf() :

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

Pour plus d'informations sur printf(), reportez-vous au Chapitre12Format de sortie.

printa()

void printa(aggregation)
void printa(string format, aggregation)

L'action printa() permet d'afficher des groupements et de les formater. Pour de plus amples informations sur les groupements, reportez-vous au Chapitre9Groupements. Si aucun format n'est fourni, printa() procède uniquement au suivi, dans le consommateur DTrace, d'une directive en vertu de laquelle le groupement spécifié doit être traité et affiché avec le format par défaut. Si aucun format n'est fourni, le groupement sera formaté tel que spécifié. Pour plus d'informations sur la chaîne de format Chapitre12Format de sortie, reportez-vous au Chapter 12, Output Formatting().

printa() effectue uniquement le suivi d'une directive en vertu de laquelle le groupement doit être traité par le consommateur DTrace. Cette action ne traite pas le groupement dans le noyau. Par conséquent, le temps écoulé entre le suivi de la directive printa() et le traitement effectif de la directive dépend des facteurs affectant le traitement du tampon. Parmi ces facteurs figurent le taux de groupement, la stratégie de mise en tampon et, en cas de sélection de la stratégie switching, la vitesse de commutation des tampons. Pour plus d'informations sur ces facteurs, reportez-vous au Chapitre9Groupements et au Chapitre11Tampons et mise en tampon.

stack()

void stack(int nframes)
void stack(void)

L'action stack() enregistre un suivi de pile de noyau dans le tampon spécifié. La pile de noyau aura une profondeur de nframes. Si nframes n'est pas fourni, le nombre de cadres de pile enregistrés correspond au nombre spécifié par l'option stackframes. Exemple :


# 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
   ...

L'action stack() diffère légèrement des autres actions dans la mesure où elle peut également être utilisée en tant que clé de groupement.


# 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 nframes, int strsize)
void ustack(int nframes)
void ustack(void)

L'action ustack() enregistre une trace de pile utilisateur dans le tampon spécifié. La pile utilisateur aura une profondeur de nframes. Si nframes n'est pas fourni, le nombre de cadres de pile enregistrés correspond au nombre spécifié par l'option ustackframes. Tant que ustack() est capable de déterminer l'adresse de cadres d'appel lorsque la sonde se déclenche, les cadres de pile ne seront convertis en symbole qu'au moment où le consommateur DTrace traite l'action ustack() au niveau utilisateur. Si strsize est spécifié et que sa valeur est différente de zéro, ustack() alloue l'espace de chaîne spécifié pour effectuer une conversion d'adresse en symbole directement depuis le noyau. Cette conversion directe en symbole utilisateur est actuellement disponible uniquement sur les machines virtuelles Java 1.5 ou une version supérieure. La conversion Java d'adresse en symbole annote les piles utilisateur contenant des cadres Java avec un nom de méthode et une classe Java. Si ces cadres ne peuvent pas être convertis, ils apparaîtront uniquement en tant qu'adresses hexadécimales.

L'exemple suivant effectue le suivi d'une pile sans espace de chaîne, et donc sans conversion Java d'adresse en symbole.


# 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

Vous remarquerez que les cadres de pile C et C++ de la machine virtuelle Java sont présentés symboliquement, en utilisant des noms symboliques “mutilés” C++ tandis que les cadres de pile Java sont présentés uniquement sous forme d'adresses hexadécimales. L'exemple suivant illustre un appel à ustack() avec un espace de chaîne dont la valeur est différente de zéro :


# 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

Le résultat de l'exemple ci-dessus affiche des informations symboliques sur les cadres de pile Java. Des cadres hexadécimaux restent affichés dans cette sortie car certaines fonctions sont statiques et n'ont pas d'entrée dans le tableau des symboles d'application. La conversion est impossible pour ces cadres.

La conversion du symbole ustack() pour les cadres autres que Java se produit après l'enregistrement des données de pile. Par conséquent, le processus utilisateur correspondant risque de se terminer avant la réalisation de la conversion du symbole, rendant la conversion des cadres de pile impossible. Si le processus utilisateur se termine avant la réalisation de la conversion du symbole, dtrace émet un message d'avertissement, suivi des cadres de pile hexadécimaux, comme illustré dans l'exemple suivant :


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

Vous trouverez davantage d'informations sur les techniques pour restreindre ce problème au Chapitre33Suivi des processus utilisateur.

Enfin, étant donné que les commandes du débogueur DTrace post-mortem ne peuvent pas exécuter la conversion des cadres, l'utilisation de ustack() avec une stratégie de tampon ring donnera des données ustack() brutes.

Le programme D suivant montre un exemple de ustack() laissant strsize non spécifié :

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

Pour exécuter cet exemple destiné au navigateur Web Netscape .netscape.bin dans les installations Solaris par défaut, utilisez la commande suivante :


# 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 nframes, int strsize)
void jstack(int nframes)
void jstack(void)

jstack() est un alias de ustack() qui utilise l'option jstackframes en fonction du nombre de cadres de pile et de la taille de l'espace de chaîne, jstackstrsize. Par défaut, jstacksize prend une valeur différente de zéro. Cela signifie que l'utilisation de jstack() produit une pile avec une conversion des cadres Java.

Actions destructrices

Certaines actions DTrace sont destructrices dans la mesure où elles modifient radicalement l'état du système. Il n'est possible de les utiliser que si elles ont été activées de manière explicite. Lors de l'utilisation de dtrace(1M), vous pouvez activer des actions destructrices en utilisant l'option -w. En cas de tentative d'activation non explicite d'actions destructrices dans dtrace(1M), dtrace échouera, affichant un message similaire à ce qui suit :


dtrace: failed to enable 'syscall': destructive actions not allowed

Actions destructrices de processus

Certaines actions destructrices ne le sont que dans un processus particulier. Ces actions sont disponibles pour les utilisateurs disposant de privilèges dtrace_proc ou dtrace_user. Pour plus d'informations sur les privilèges de sécurité DTrace, reportez-vous au Chapitre35Sécurité.

stop()

void stop(void)

L'action stop() force le processus qui déclenche l'arrêt de la sonde activée lorsque celle-ci quitte le noyau, comme si elle était arrêtée par une action proc(4). L'utilitaire prun(1) peut être utilisé pour la reprise d'un processus ayant été arrêté par l'action stop(). L'action stop() peut être utilisée pour arrêter un processus à un point de sonde DTrace quelconque. Cette action peut être utilisée pour capturer un programme dans un état particulier, qu'il serait difficile d'obtenir avec un point d'interruption simple puis pour joindre au processus un débogueur classique tel que mdb(1). Vous pouvez également utiliser l'utilitaire gcore(1) pour enregistrer l'état d'un processus arrêté dans un fichier Core à des fins d'analyses ultérieures.

raise()

void raise(int signal)

L'action raise() envoie le signal spécifié au processus en cours d'exécution. Recourir à cette action revient à utiliser la commande kill(1) permettant d'envoyer un signal à un processus. Il est possible d'utiliser l'action raise() pour envoyer un signal à un point précis de l'exécution d'un processus.

copyout()

void copyout(void *buf, uintptr_t addr, size_t nbytes)

L'action copyout() copie nbytes à partir du tampon buf à l'adresse addr dans l'espace d'adressage du processus associé au thread actuel. Si l'adresse de l'espace utilisateur ne correspond pas à une page valide, par défaut, dans l'espace d'adressage, une erreur est générée.

copyoutstr()

void copyoutstr(string str, uintptr_t addr, size_t maxlen)

L'action copyoutstr() copie la chaîne str vers l'adresse addr dans l'espace d'adressage du processus associé au thread actuel. Si l'adresse de l'espace utilisateur ne correspond pas à une page valide, par défaut, dans l'espace d'adressage, une erreur est générée. La longueur de chaîne est limitée à la valeur définie par l'option strsize. Pour de plus amples informations, reportez-vous au Chapitre16Options et paramètres réglables.

system()

void system(string program, ...) 

L'action system() provoque l'exécution du programme program comme s'il était saisi dans le shell. La chaîne program peut contenir n'importe quelle conversion de format printf()/printa. () Des arguments correspondant aux conversions de format doivent être spécifiés. Pour plus d'informations sur les conversions dans des formats valides, reportez-vous au Chapitre12Format de sortie.

L'exemple suivant exécute la commande date(1) une fois par seconde :


# dtrace -wqn tick-1sec'{system("date")}'
 Tue Jul 20 11:56:26 CDT 2004
 Tue Jul 20 11:56:27 CDT 2004
 Tue Jul 20 11:56:28 CDT 2004
 Tue Jul 20 11:56:29 CDT 2004
 Tue Jul 20 11:56:30 CDT 2004

L'exemple suivant illustre une utilisation élaborée de l'action, avec des conversions printf() dans la chaîne program et des outils de filtre traditionnels comme des tubes :

#pragma D option destructive
#pragma D option quiet

proc:::signal-send
/args[2] == SIGINT/
{
	printf("SIGINT sent to %s by ", args[1]->pr_fname);
	system("getent passwd %d | cut -d: -f5", uid);
}

Exécuter le script ci-dessus engendre une sortie similaire à l'exemple suivant :


# ./whosend.d
SIGINT sent to MozillaFirebird- by Bryan Cantrill
SIGINT sent to run-mozilla.sh by Bryan Cantrill
^C
SIGINT sent to dtrace by Bryan Cantrill

L'exécution de la commande spécifiée n'a pas lieu dans le cadre du déclenchement d'une sonde, mais lorsque le tampon contenant les détails sur l'action system() est traité au niveau utilisateur. La manière dont ce traitement a lieu et le moment auquel il a lieu dépendent de la stratégie de mise en tampon, décrite dans le Chapitre11Tampons et mise en tampon Avec la stratégie de mise en tampon par défaut, la vitesse de traitement du tampon est spécifiée par l'option switchrate. Vous pouvez voir le délai inhérent au system() si vous réglez de façon explicite switchrate sur une vitesse supérieure à la valeur par défaut de une seconde, comme illustré dans l'exemple suivant :

#pragma D option quiet
#pragma D option destructive
#pragma D option switchrate=5sec

tick-1sec
/n++ < 5/
{
	printf("walltime  : %Y\n", walltimestamp);
	printf("date      : ");
	system("date");
	printf("\n");
}

tick-1sec
/n == 5/
{
	exit(0);
}

Exécuter le script ci-dessus engendre une sortie similaire à l'exemple suivant :


# dtrace -s ./time.d
 walltime  : 2004 Jul 20 13:26:30
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:31
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:32
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:33
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:34
date      : Tue Jul 20 13:26:35 CDT 2004

Vous remarquerez que les valeurs walltime diffèrent mais que les valeurs date sont identiques. Ce résultat reflète le fait que l'exécution de la commande date(1) a eu lieu uniquement lors du traitement du tampon et non lors de l'enregistrement de l'action system().

Actions destructrices de noyau

Certaines actions destructrices le sont dans l'intégralité du système. Ces actions doivent évidemment être utilisées avec un très grand soin car elles affectent tous les processus du système ainsi que ceux de tous les autres systèmes dépendant de manière implicite ou explicite des services réseau du système affecté.

breakpoint()

void breakpoint(void)

L'action breakpoint() provoque un point d'interruption dans le noyau, engendrant l'arrêt et le transfert de la commande vers le débogueur du noyau. Le débogueur du noyau émet une chaîne dénotant la sonde DTrace ayant engendré l'action. Par exemple, si l'on exécute le programme suivant :


# dtrace -w -n clock:entry'{breakpoint()}'
dtrace: allowing destructive actions
dtrace: description 'clock:entry' matched 1 probe

Avec Solaris fonctionnant sous SPARC, le message suivant risque de s'afficher sur la console :


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb 30002765700)
Type  'go' to resume
ok

Avec Solaris fonctionnant sous x86, le message suivant risque de s'afficher sur la console :


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb d2b97060)
stopped at      int20+0xb:      ret
kmdb[0]:

L'adresse suivant la description de sonde est l'adresse du bloc de contrôle d'activation (ECB) au sein de DTrace. Vous pouvez utiliser cette adresse pour obtenir plus d'informations sur l'activation de sonde ayant provoqué l'action de point d'interruption.

En cas d'erreur relative à l'action breakpoint(), le nombre d'appels de cette action risque d'être largement supérieur au nombre prévu. Ce comportement peut par ailleurs vous empêcher de mettre fin au consommateur DTrace engendrant les actions de point d'interruption. Dans ce cas, définissez la variable de nombre entier du noyau, dtrace_destructive_disallow sur 1. Ce paramétrage interdira toutes les actions destructives sur la machine. Appliquez ce réglage uniquement dans ce cas de figure précis.

La méthode exacte permettant de régler dtrace_destructive_disallow dépend du débogueur du noyau utilisé. Si vous utilisez le programme OpenBoot PROM sur un système SPARC, utilisez w!:


ok 1 dtrace_destructive_disallow w!
ok

Confirmez que la variable a été définie en utilisant w? :


ok dtrace_destructive_disallow w?
1
ok

Continuez en tapant go :


ok go

En cas d'utilisation de kmdb(1) sur des systèmes x86 ou SPARC, utilisez le modificateur d'écriture à 4 octets (W) avec le formatage dcmd / :


kmdb[0]: dtrace_destructive_disallow/W 1
dtrace_destructive_disallow:    0x0             =       0x1
kmdb[0]:

Continuez en utilisant :c :


kadb[0]: :c

Pour réactiver des actions destructrices après avoir continué, vous devez réinitialiser de façon explicite dtrace_destructive_disallow en lui réattribuant la valeur 0 avec mdb(1) :


# echo "dtrace_destructive_disallow/W 0" | mdb -kw
dtrace_destructive_disallow:    0x1             =       0x0
#

panic()

void panic(void)

Lorsqu'elle est déclenchée, l'action panic() provoque une erreur grave de noyau. Cette action doit être utilisée pour forcer un vidage mémoire sur incident sur un système à un moment stratégique. Vous pouvez utiliser conjointement à cette action des analyses postmortem et de mise en tampon circulaire pour mieux comprendre le problème. Pour plus d'informations, reportez-vous au Chapitre11Tampons et mise en tampon et au Chapitre37Suivi post-mortem. Lorsque l'action d'erreur grave est utilisée, un message d'erreur grave s'affiche, dénotant la sonde qui en est à l'origine. Exemple :


  panic[cpu0]/thread=30001830b80: dtrace: panic action at probe
  syscall::mmap:entry (ecb 300000acfc8)

  000002a10050b840 dtrace:dtrace_probe+518 (fffe, 0, 1830f88, 1830f88,
    30002fb8040, 300000acfc8)
    %l0-3: 0000000000000000 00000300030e4d80 0000030003418000 00000300018c0800
    %l4-7: 000002a10050b980 0000000000000500 0000000000000000 0000000000000502
  000002a10050ba30 genunix:dtrace_systrace_syscall32+44 (0, 2000, 5,
    80000002, 3, 1898400)
    %l0-3: 00000300030de730 0000000002200008 00000000000000e0 000000000184d928
    %l4-7: 00000300030de000 0000000000000730 0000000000000073 0000000000000010

  syncing file systems... 2 done
  dumping to /dev/dsk/c0t0d0s1, offset 214827008, content: kernel
  100% done: 11837 pages dumped, compression ratio 4.66, dump
  succeeded
  rebooting...

syslogd(1M) émet également un message au moment de la réinitialisation :


  Jun 10 16:56:31 machine1 savecore: [ID 570001 auth.error] reboot after panic:
  dtrace: panic action at probe syscall::mmap:entry (ecb 300000acfc8)

Le tampon du message de vidage mémoire sur incident contient également la sonde et l'ECB responsable de l'action panic().

chill()

void chill(int nanoseconds)

L'action chill() provoque la rotation de DTrace pendant un nombre de nanosecondes spécifié. chill() est essentiellement utilisé pour l'exploration de problèmes de synchronisation. Par exemple, vous pouvez utiliser cette action pour ouvrir des fenêtres de condition de compétitivité ou pour mettre des événements périodiques en phase ou hors phase les uns avec les autres. Étant donné que les interruptions sont désactivées lorsqu'elles sont dans un contexte de sonde DTrace, toute utilisation de chill() provoquera une latence d'interruption, de planification et de répartition. Par conséquent, chill() peut provoquer des effets inattendus sur le système. Il convient donc de l'utiliser à bon escient. Étant donné que l'activité du système repose sur la gestion des interruptions périodiques, DTrace refusera d'exécuter l'action chill() pendant plus de 500 millisecondes par intervalle d'une seconde sur une CPU donnée. En cas de dépassement de l'intervalle chill(), l'erreur d'opération illégale fera l'objet d'un rapport dans DTrace, comme illustré dans l'exemple suivant :


# dtrace -w -n syscall::open:entry'{chill(500000001)}'
dtrace: allowing destructive actions
dtrace: description 'syscall::open:entry' matched 1 probe
dtrace: 57 errors
CPU     ID                    FUNCTION:NAME
dtrace: error on enabled probe ID 1 (ID 14: syscall::open:entry): \
  illegal operation in action #1

Cette limite s'applique même si le temps est réparti sur plusieurs appels à chill() ou sur plusieurs consommateurs DTrace d'une sonde unique. Par exemple, la même erreur peut être générée par la commande suivante :


# dtrace -w -n syscall::open:entry'{chill(250000000); chill(250000001);}'

Actions spéciales

Cette section décrit les actions qui ne sont ni des actions d'enregistrement de données ni des actions destructrices.

Actions spéculatives

Les actions associées à un suivi spéculatif sont les actions speculate(), commit()et discard(). Vous trouverez plus d'informations sur ces actions au Chapitre13Suivi spéculatif.

exit()

void exit(int status)

L'action exit() est utilisée pour interrompre immédiatement le suivi et pour informer le consommateur DTrace qu'il doit interrompre le suivi, effectuer tout traitement final et appeler exit(3C) avec l'état spécifié. Étant donné que exit() renvoie un état au niveau utilisateur, il s'agit d'une action d'enregistrement de données. Toutefois, contrairement aux autres actions de stockage de données, exit() ne peut pas faire l'objet d'un suivi spéculatif. exit() provoquera la fermeture du consommateur DTrace, quelle que soit la stratégie de tampon. Étant donné que exit() est une action d'enregistrement de données, il est possible de l'abandonner.

Lorsque exit() est appelé, seules les actions DTrace déjà en cours d'exécution sur d'autres CPU seront terminées. Aucune nouvelle action ne se produira sur aucune CPU. La seule exception à cette règle est le traitement de la sonde END qui est appelée après que le consommateur DTrace a traité l'action exit() et indiqué que le suivi devait être arrêté.

Sous-routines

Les sous-routines diffèrent des actions car généralement, elles n'affectent que l'état interne de DTrace. Par conséquent, il n'existe pas de sous-routines destructrices et les sous-routines ne procèdent jamais au suivi de données dans des tampons. De nombreuses sous-routines ont des analogues dans les sections 9F ou 3C. Pour plus d'informations sur les sous-routines correspondantes, reportez-vous aux sections Intro(9F) et Intro(3).

alloca()

void *alloca(size_t size)

alloca() alloue size octets depuis un espace de travail, et renvoie un pointeur vers la mémoire allouée. Le pointeur renvoyé présente systématiquement un alignement de 8 octets. L'espace de travail n'est valide que pendant la durée d'une clause. La mémoire allouée avec alloca()est libérée lors de l'achèvement de la clause. Si l'espace de travail disponible n'est pas suffisant, aucune mémoire n'est allouée et une erreur est générée.

basename()

string basename(char *str)

basename() est un analogue D de basename(1). Cette sous-routine crée une chaîne qui comprend une copie de la chaîne spécifiée, mais sans le préfixe se terminant par /. La chaîne renvoyée est allouée à l'extérieur de la mémoire de travail. Sa durée de validité correspond donc à la durée de la clause. Si l'espace de travail disponible est insuffisant, basename ne s'exécute pas et une erreur est générée.

bcopy()

void bcopy(void *src, void *dest, size_t size)

bcopy() copie size octets de la mémoire src dans la mémoire dest. L'ensemble de la mémoire source doit résider à l'extérieur de la mémoire de travail tandis que l'ensemble de la mémoire de destination doit résider à l'intérieur. Si ces conditions ne sont pas remplies, aucune copie n'est effectuée et une erreur est générée.

cleanpath()

string cleanpath(char *str)

cleanpath() crée une chaîne qui consiste en une copie du chemin str, mais dont certains éléments redondants sont éliminés. En particulier, les éléments “/./” du chemin sont supprimés et les éléments “/../” réduits. La réduction des éléments /../ est effectuée indépendamment des liens symboliques. Il est donc possible que cleanpath() copie un chemin valide et renvoie un chemin plus court, non valide.

Par exemple, si str correspond à “ /foo/../bar” et que /foo est un lien symbolique vers /net/foo/export cleanpath() renvoie la chaîne “/bar” même si bar risque de ne figurer que dans /net/foo et non dans /. Cette limitation est due au fait que cleanpath() est appelé dans le contexte d'un déclenchement de sonde, où la résolution de liens symboliques ou de noms arbitraires est impossible. La chaîne renvoyée est allouée à l'extérieur de la mémoire de travail. Sa durée de validité correspond donc à la durée de la clause. Si l'espace de travail disponible est insuffisant, cleanpath ne s'exécute pas et une erreur est générée.

copyin()

void *copyin(uintptr_t addr, size_t size)

copyin() copie le nombre d'octets spécifié depuis l'adresse de l'utilisateur spécifiée dans un tampon de travail DTrace et renvoie l'adresse de ce tampon. L'adresse de l'utilisateur est interprétée en tant qu'adresse de l'espace du processus associé au thread actuel. Le pointeur de tampon qui en résulte comporte obligatoirement un alignement à 8 octets. L'adresse en question doit correspondre à une page par défaut dans le processus en cours. Si l'adresse ne correspond pas à une page par défaut, ou si l'espace de travail disponible est insuffisant, la valeur NULL est renvoyée et une erreur est générée. Pour connaître les techniques visant à réduire le risque d'apparition d'erreurs copyin, reportez-vous au Chapitre33Suivi des processus utilisateur.

copyinstr()

string copyinstr(uintptr_t addr)

copyinstr() copie une chaîne C terminée par un octet nul à partir de l'adresse de l'utilisateur spécifiée dans un tampon de travail DTrace et renvoie l'adresse de ce tampon. L'adresse de l'utilisateur est interprétée en tant qu'adresse de l'espace du processus associé au thread actuel. La longueur de la chaîne est limitée à la valeur définie par l'option strsize ; pour plus d'informations, consultez le Chapitre16Options et paramètres réglables. Comme pour copyin, l'adresse spécifiée doit correspondre à une page par défaut dans le processus en cours. Si l'adresse ne correspond pas à une page par défaut, ou si l'espace de travail disponible est insuffisant, la valeur NULL est renvoyée et une erreur est générée. Pour connaître les techniques visant à réduire le risque d'apparition d'erreurs Chapitre33Suivi des processus utilisateur, reportez-vous au Chapter 33, User Process Tracing.

copyinto()

void copyinto(uintptr_t addr, size_t size, void *dest)

copyin() copie le nombre d'octets spécifié depuis l'adresse de l'utilisateur spécifiée dans un tampon de travail DTrace spécifié par dest. L'adresse de l'utilisateur est interprétée en tant qu'adresse de l'espace du processus associé au thread actuel. L'adresse en question doit correspondre à une page par défaut dans le processus en cours. Si l'adresse ne correspond pas à une page par défaut ou qu'aucune mémoire de destination ne réside à l'extérieur de l'espace de travail, aucune copie n'est effectuée et une erreur est générée. Pour connaître les techniques visant à réduire le risque d'apparition d'erreurs Chapitre33Suivi des processus utilisateur, reportez-vous au Chapter 33, User Process Tracing.

dirname()

string dirname(char *str)

dirname() est un analogue D de dirname(1). Cette sous-routine crée une chaîne comprenant le nom du chemin complet à l'exception du dernier niveau, str . La chaîne renvoyée est allouée à l'extérieur de la mémoire de travail. Sa durée de validité correspond donc à la durée de la clause. Si l'espace de travail disponible est insuffisant, dirname ne s'exécute pas et une erreur est générée.

msgdsize()

size_t msgdsize(mblk_t *mp)

msgdsize() renvoie le nombre d'octets dans le message de données vers lequel pointe mp. Pour plus d'informations, consultez msgdsize(9F). msgdsize() ne prend en compte dans le total que les blocs de données de type M_DATA.

msgsize()

size_t msgsize(mblk_t *mp)

msgsize() renvoie le nombre d'octets dans le message vers lequel pointe mp. Contrairement à msgdsize(), qui renvoie uniquement le nombre d'octets des données, msgsize() renvoie le nombre total d'octets du message.

mutex_owned()

int mutex_owned(kmutex_t *mutex)

mutex_owned() est une implémentation de mutex_owned(9F). mutex_owned() renvoie une valeur différente de zéro si le thread d'appel est le propriétaire du mutex de noyau, ou une valeur égale à zéro si le mutex adaptatif n'a pas de propriétaire.

mutex_owner()

kthread_t *mutex_owner(kmutex_t *mutex)

mutex_owner() renvoie le pointeur de thread du propriétaire actuel du mutex de noyau adaptatif. mutex_owner() renvoie NULL si le mutex adaptatif spécifié n'a pas de propriétaire ou s'il s'agit d'un spin mutex. Consultez mutex_owned(9F).

mutex_type_adaptive()

int mutex_type_adaptive(kmutex_t *mutex)

mutex_type_adaptive() renvoie une valeur différente de zéro si le mutex de noyau spécifié est de type MUTEX_ADAPTIVE, ou une valeur égale à zéro dans le cas contraire. Les mutex sont adaptatifs s'ils remplissent au moins une des conditions suivantes :

Pour plus d'informations sur les mutex, consultez mutex_init(9F). La majorité des mutex du noyau Solaris sont adaptatifs.

progenyof()

int progenyof(pid_t pid)

progenyof() renvoie une valeur différente de zéro si le processus d'appel (le processus associé au thread qui déclenche actuellement la sonde correspondant aux critères) fait partie du progeny de l'ID de processus spécifié.

rand()

int rand(void)

rand() renvoie un nombre entier pseudo-aléatoire. Le nombre renvoyé est un nombre pseudo-aléatoire faible. Il ne doit pas être utilisé dans le cadre d'une application cryptographique.

rw_iswriter()

int rw_iswriter(krwlock_t *rwlock)

rw_iswriter() renvoie une valeur différente de zéro si le verrou de lecture-écriture est possédé ou requis par un programme d'écriture. Si le verrou est possédé par des programmes de lecture et qu'aucun programme d'écriture n'est bloqué, ou que le verrou n'est pas possédé, rw_iswriter() renvoie une valeur égale à zéro. Consultez rw_init(9F).

rw_write_held()

int rw_write_held(krwlock_t *rwlock)

rw_write_held() renvoie une valeur différente de zéro si le verrouillage en lecture-écriture spécifié est actuellement possédé par un programme d'écriture. Si le verrou est possédé uniquement par des programmes de lecture ou qu'il n'est pas possédé, rw_write_held() renvoie une valeur égale à zéro. Consultez rw_init(9F).

speculation()

int speculation(void)

speculation() réserve un tampon de suivi spéculatif à utiliser avec speculate() et renvoie un identificateur correspondant à ce tampon. Pour plus d'informations, reportez-vous au Chapitre13Suivi spéculatif.

strjoin()

string strjoin(char *str1, char *str2)

strjoin() crée une chaîne composée de l'élément str1 concaténé avec l'élément str2. La chaîne renvoyée est allouée à l'extérieur de la mémoire de travail. Sa durée de validité correspond donc à la durée de la clause. Si l'espace de travail disponible est insuffisant, strjoin ne s'exécute pas et une erreur est générée.

strlen()

size_t strlen(string str)

strlen() renvoie la longueur de la chaîne spécifiée en octets, à l'exception de l'octet nul de fin.