Guía de seguimiento dinámico de Solaris

Subrutinas copyin() y copyinstr()

La interacción de DTrace con los procesos varía un poco con respecto a los depuradores más tradicionales o las herramientas de observabilidad. Muchas de estas herramientas parece que se ejecutan dentro del ámbito de los procesos, permitiendo que los usuarios establezcan que los punteros dejen de hacer referencia directa a las variables de programa. En lugar de parecer que se ejecutan dentro de un proceso o como una parte de él, los sondeos de DTrace se ejecutan en el núcleo de Solaris. Para acceder a los datos de proceso, un sondeo debe usar las subrutinas copyin() o copyinstr() para copiar los datos de proceso de usuario en el espacio de dirección del núcleo.

Por ejemplo, considere la siguiente llamada de sistema write(2):

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

El siguiente programa escrito en D ilustra un intento incorrecto de imprimir el contenido de una cadena pasada a la llamada de sistema write(2):

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

Si trata de ejecutar esta secuencia de comandos, DTrace generará mensajes de error similares al siguiente ejemplo:


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

La variable arg1, que contiene el valor del parámetro buf, es una dirección que hace referencia a la memoria en el proceso que ejecuta la llamada de sistema. Para leer la cadena de esta dirección, use la subrutina copyinstr() y registre el resultado con la acción printf():

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

El resultado de esta secuencia de comandos muestra todas las cadenas que se están pasando a la llamada de sistema write(2). Ocasionalmente, no obstante, puede observarse una salida irregular, semejante a la del siguiente ejemplo:


  0     37                      write:entry mada���

La subrutina copyinstr() actúa en un argumento de entrada que es la dirección del usuario de una cadena ASCII con una terminación nula. Sin embargo, las memorias intermedias pasadas a la llamada de sistema write(2) pueden hacer referencia a datos binarios en lugar de a cadenas ASCII. Para imprimir sólo las cadenas que el llamador desee, use la subrutina copyin(), que considera un tamaño como su segundo argumento:

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

Observe que el operador stringof es necesario para que DTrace convierta correctamente en una cadena los datos de usuario recuperados usando copyin. () El uso de stringof no es necesario cuando se emplea copyinstr(), ya que esta función siempre devuelve el tipo string.

Cómo evitar errores

Las subrutinas copyin() y copyinstr() no se pueden leer desde direcciones de usuario que no se hayan tocado todavía, por lo que incluso una dirección válida puede provocar un error si la página que contiene esa dirección no ha experimentado errores al acceder. Considere el siguiente ejemplo:


# 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

En la salida del ejemplo anterior, la aplicación funcionaba correctamente y la dirección de arg0 era válida, pero hacía referencia a una página a la que no había accedido todavía el proceso correspondiente. Para resolver este problema, espere a que el núcleo o la aplicación usen los datos antes de comenzar el seguimiento. Por ejemplo, puede esperar a que la llamada de sistema muestre su resultado para aplicar copyinstr(), tal y como se ilustra en el siguiente ejemplo:


# 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