Manuel de suivi dynamique Solaris

Sous-routines copyin() et copyinstr()

L'interaction de DTrace avec les processus diffère légèrement de la plupart des débogueurs ou des outils d'observation classiques. La plupart de ces outils s'exécutent dans l'étendue du processus, ce qui permet aux utilisateurs de déréférencer les pointeurs pour programmer directement les variables. Plutôt que de s'exécuter dans le cadre ou au sein du processus lui-même, les sondes de DTrace s'exécutent dans le noyau de Solaris. Pour accéder aux données du processus, une sonde doit utiliser les sous-routines copyin() ou copyinstr() pour copier les données de processus utilisateur dans l'espace d'adressage du noyau.

Étudiez, par exemple, l'appel système write(2) suivant :

ssize_t write(int fd, const void *buf, size_t nbytes);

Le programme en D suivant illustre une tentative erronée d'impression du contenu d'une chaîne transmise à l'appel système write(2).

syscall::write:entry
{
	printf("%s", stringof(arg1)); /* incorrect use of arg1 */
}

Si vous essayez d'exécuter ce script, DTrace crée des messages d'erreur similaires à l'exemple suivant :


dtrace: error on enabled probe ID 1 (ID 37: syscall::write:entry): \
    invalid address (0x10038a000) in action #1

La variable arg1, contenant la valeur du paramètre buf, correspond à une adresse renvoyant à la mémoire dans le processus exécutant le système d'appel. Pour lire la chaîne à cette adresse, utilisez la sous-routine copyinstr() et enregistrez son résultat avec l'action printf() :

syscall::write:entry
{
	printf("%s", copyinstr(arg1)); /* correct use of arg1 */

La sortie de ce script présente toutes les chaînes dont la transmission à l'appel système write(2) est en cours. Il se peut, cependant, que vous constatiez occasionnellement des sorties irrégulières similaires à l'exemple suivant :


  0     37                      write:entry mada���

La sous-routine copyinstr() prend en charge un argument d'entrée correspondant à l'adresse utilisateur d'une chaîne ASCII nulle terminée. Cependant, les tampons transmis à l'appel système write(2) peuvent renvoyer à des données binaires plutôt qu'à des chaînes ASCII. Pour n'imprimer que le nombre de chaînes prévu par le programme appelant, utilisez la sous-routine copyin() dont le second argument correspond à une taille :

syscall::write:entry
{
	printf("%s", stringof(copyin(arg1, arg2)));
}

Notez l'utilisation obligatoire de l'opérateur stringof de sorte que DTrace convertisse correctement les données utilisateur récupérées à l'aide de copyin() vers une chaîne. L'utilisation de stringof ne s'impose pas si vous utilisez copyinstr() car cette fonction retourne toujours le type string.

Évitement des erreurs

Les sous-routines copyin() et copyinstr() ne peuvent pas lire les adresses utilisateur qui n'ont pas encore été touchées. Par conséquent, même une adresse valide peut provoquer une erreur si la page contenant cette adresse n'a pas déjà subi de défaillance lors de l'accès. Examinez l'exemple suivant :


# dtrace -n syscall::open:entry'{ trace(copyinstr(arg0)); }'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU     ID                    FUNCTION:NAME
dtrace: error on enabled probe ID 2 (ID 50: syscall::open:entry): invalid address
(0x9af1b) in action #1 at DIF offset 52

Dans la sortie de l'exemple ci-dessus, l'application fonctionnait correctement et l'adresse dans arg0 était valide mais renvoyait à une page à laquelle le processus correspondant n'avait pas encore accédé. Pour résoudre ce problème, attendez que le noyau ou l'application utilisent les données avant de procéder au suivi. Attendez, par exemple, le retour de l'appel système pour appliquer copyinstr(), comme illustré dans l'exemple suivant :


# dtrace -n syscall::open:entry'{ self->file = arg0; }' \
-n syscall::open:return'{ trace(copyinstr(self->file)); self->file = 0; }'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU     ID                    FUNCTION:NAME
  2     51                      open:return   /dev/null