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.
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.
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 ... |
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().
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");
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.
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.
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.
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 ... |
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 ... |
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.
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 |
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é.
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.
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.
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.
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.
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().
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é.
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 # |
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().
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);}' |
Cette section décrit les actions qui ne sont ni des actions d'enregistrement de données ni des actions destructrices.
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.
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é.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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 :
Le mutex est déclaré de façon statique.
Le mutex est créé avec un cookie de bloc d'interruption NULL.
Le mutex est créé avec un cookie de bloc d'interruption qui ne correspond pas à une interruption de haut niveau.
Pour plus d'informations sur les mutex, consultez mutex_init(9F). La majorité des mutex du noyau Solaris sont adaptatifs.
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é.
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.
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).
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).
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.
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.
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.