Manuel de suivi dynamique Solaris

Variables locales de thread

DTrace permet de déclarer le stockage des variables locales sur chaque thread du système d'exploitation, par opposition aux variables globales comme indiqué précédemment dans ce chapitre. Les variables locales de thread sont utiles lorsque vous souhaitez activer une sonde et marquer chaque thread qui déclenche la sonde avec une balise ou d'autres données. Il est aisé de créer un programme pour résoudre ce problème en langage D car les variables locales de thread partagent un nom commun dans votre code en D mais font référence à un stockage de données distinct associé à chaque thread. Les variables locales de thread sont référencées en appliquant l'opérateur -> à un identificateur self spécial :

syscall::read:entry
{
	self->read = 1;
}

Cet exemple en langage D active la sonde sur l'appel système read(2) et associe la variable locale de thread read à chaque thread qui déclenche la sonde. De la même manière que les variables globales, les variables locales de thread sont créées automatiquement sur leur première affectation et adoptent le type utilisé à droite de la première instruction d'affectation (dans cet exemple, int).

Chaque fois que la variable self->read est référencée dans votre programme en D, l'objet de données référencé correspond à l'objet associé au thread du système d'exploitation en cours d'exécution au déclenchement de la sonde de DTrace correspondante. Vous pouvez imaginer une variable locale de thread comme un tableau associatif implicitement indexé par un tuple qui décrit l'identité du thread dans le système. L'identité d'un thread est unique tout au long de la durée de vie du système : si le thread est fermé et que la même structure de données du système d'exploitation est utilisée pour créer un nouveau thread, ce dernier ne réutilise pas la même identité de stockage local de thread de DTrace.

Une fois que vous avez défini une variable locale de thread, vous pouvez la déréférencer pour n'importe quel thread dans le système même si la variable en question n'a pas été précédemment affectée à ce thread particulier. Si la copie d'un thread d'une variable locale de thread n'a pas encore été affectée, le stockage des données dédié à la copie est défini pour être rempli de zéros. De même qu'avec les éléments de tableau associatif, le stockage sous-jacent n'est pas alloué à une variable locale de thread tant qu'une valeur différente de zéro lui est affectée. De la même manière, l'affectation de zéro à une variable locale de thread entraîne la suppression de l'allocation de stockage sous-jacent au niveau de DTrace. Affectez toujours zéro aux variables locales de thread que vous n'utilisez plus. Pour découvrir les autres techniques de réglage de l'espace des variables dynamiques à partir duquel les variables locales de thread sont allouées, reportez-vous au Chapitre16Options et paramètres réglables.

Vous pouvez définir les variables locales de thread, quel que soit leur type, dans vos programmes en D, y compris les tableaux associatifs. Voici quelques exemples de définitions de variables locales de thread :

self->x = 123;              /* integer value */
self->s = "hello";	          /* string value */
self->a[123, 'a'] = 456;    /* associative array */

De même que les variables en langage D, vous ne pouvez pas déclarer explicitement les variables locales de thread avant de les utiliser. Si vous souhaitez malgré tout créer une déclaration, vous pouvez en placer une en dehors des clauses de votre programme en ajoutant initialement le mot-clé self :

self int x;    /* declare int x as a thread-local variable */

syscall::read:entry
{
	self->x = 123;
}

Les variables locales de thread sont conservées dans un espace de noms distinct des variables globales. Vous pouvez donc réutiliser les noms. N'oubliez pas que x et self->x sont deux variables différentes si vous chargez des noms dans votre programme ! L'exemple suivant présente la méthode d'utilisation des variables locales de thread. Dans un éditeur de texte, entrez le programme suivant et enregistrez-le dans le fichier rtime.d :


Exemple 3–1 rtime.d : calcul du temps passé dans read(2)

syscall::read:entry
{
	self->t = timestamp;
}

syscall::read:return
/self->t != 0/
{
	printf("%d/%d spent %d nsecs in read(2)\n",
	    pid, tid, timestamp - self->t);
	
	/*
	 * We're done with this thread-local variable; assign zero to it to
	 * allow the DTrace runtime to reclaim the underlying storage.
	 */
	self->t = 0;
}

Accédez à présent au shell et lancez le programme. Patientez quelques secondes avant de voir apparaître une sortie. En l'absence de sortie, essayez d'exécuter quelques commandes.


# dtrace -q -s rtime.d
100480/1 spent 11898 nsecs in read(2)
100441/1 spent 6742 nsecs in read(2)
100480/1 spent 4619 nsecs in read(2)
100452/1 spent 19560 nsecs in read(2)
100452/1 spent 3648 nsecs in read(2)
100441/1 spent 6645 nsecs in read(2)
100452/1 spent 5168 nsecs in read(2)
100452/1 spent 20329 nsecs in read(2)
100452/1 spent 3596 nsecs in read(2)
...
^C
#

rtime.d utilise la variable locale de thread t pour capturer un horodatage à l'entrée de read(2) par n'importe quel thread. Dans la clause de retour, le programme imprime ensuite le temps passé dans read(2) en retirant self->t de l'horodatage actuel. Les variables en D intégrées pid et tid indiquent l'ID de processus et l'ID de thread exécutant read(2). Comme self->t n'est plus requis une fois ces informations reportées, la valeur 0 lui est affectée pour permettre à DTrace de réutiliser le stockage sous-jacent associé à t pour le thread actuel.

En règle générale, vous voyez s'afficher de nombreuses lignes de sortie sans rien faire car, en coulisses, les processus serveur et les démons exécutent read(2) en permanence et ce, même lorsque vous ne faites rien. Essayez de modifier la seconde clause de rtime.d afin d'utiliser la variable execname servant à imprimer le nom du processus exécutant read(2) dans le but d'en apprendre davantage à son sujet :

printf("%s/%d spent %d nsecs in read(2)\n",
    execname, tid, timestamp - self->t);

Si vous découvrez un processus particulièrement intéressant, ajoutez un prédicat pour en découvrir davantage sur son comportement read(2) :

syscall::read:entry
/execname == "Xsun"/
{
	self->t = timestamp;
}