Guía de seguimiento dinámico de Solaris

Estructuras

La palabra clave de D struct, forma reducida de estructura, se utiliza para introducir un nuevo tipo compuesto por un grupo de otros tipos. El nuevo tipo de estructura se puede utilizar como el tipo para las matrices y variables de D, lo que permite definir grupos de variables relacionadas con un único nombre. Las estructuras de D son iguales que la construcción correspondiente en C y C++. Si ha utilizado el lenguaje de programación Java, considere una estructura D como una clase, pero sólo con miembros de datos y sin ningún método.

Supongamos que desea crear un programa de seguimiento de llamadas del sistema más sofisticado en D que registre un número de aspectos acerca de cada llamada del sistema de read(2) y write(2) ejecutada por el comando shell, como el tiempo transcurrido, el número de llamadas y el recuento de bytes más grande transmitido como un argumento. Puede escribir una cláusula D para registrar estas propiedades en tres matrices asociativas independientes, como se muestra en el siguiente ejemplo:

syscall::read:entry, syscall::write:entry
/pid == 12345/
{
	ts[probefunc] = timestamp;
	calls[probefunc]++;
	maxbytes[probefunc] = arg2 > maxbytes[probefunc] ?
	    arg2 : maxbytes[probefunc];
}

Sin embargo, esta cláusula es ineficaz debido a que DTrace debe crear tres matrices asociativas por separado y guardar copias separadas de los valores de tupla idénticos para que probefunc busque cada uno de ellos. En su lugar, puede ahorrar espacio y facilitar la lectura y mantenimiento del programa utilizando una estructura. En primer lugar, declare un nuevo tipo de estructura en la parte superior del archivo origen del programa:

struct callinfo {
	uint64_t ts;      /* timestamp of last syscall entry */
	uint64_t elapsed; /* total elapsed time in nanoseconds */
	uint64_t calls;   /* number of calls made */
	size_t maxbytes;  /* maximum byte count argument */
};

La palabra clave struct va seguida de un identificador opcional que se utiliza para hacer referencia a nuestro nuevo tipo, que se denomina struct callinfo. A continuación, los miembros de la estructura se incluyen en un conjunto de llaves { } y toda la declaración se termina con un punto y coma (; ). Cada miembro de la estructura se define utilizando la misma sintaxis que una declaración de variables de D, con el tipo del miembro indicado en primer lugar seguido de un identificador que nombra al miembro y otro punto y coma (;).

En sí misma, la declaración de la estructura define el nuevo tipo; no crea ninguna variable ni asigna ningún almacenamiento en DTrace. Una vez declarada, puede utilizar struct callinfo como un tipo en el resto del programa D, y cada variable de tipo struct callinfo guardará una copia de las cuatro variables descritas por la plantilla de la estructura. Los miembros se ordenarán en la memoria de acuerdo con la lista de miembros, con un espacio de relleno introducido entre los miembros, ya que esto es necesario con fines de alineación de los objetos de datos.

Puede utilizar los nombres de los identificadores de miembros para acceder a los valores individuales de los miembros utilizando el operador “.” escribiendo una expresión con el siguiente formato:

variable-name.member-name

El siguiente ejemplo es un programa mejorado que utiliza el nuevo tipo de estructura. Acceda al editor y escriba el siguiente programa D y guárdelo en un archivo que se llame rwinfo.d:


Ejemplo 7–1 rwinfo.d: Recuperar estadísticas de read(2) y write(2)

struct callinfo {
	uint64_t ts;      /* timestamp of last syscall entry */
	uint64_t elapsed; /* total elapsed time in nanoseconds */
	uint64_t calls;   /* number of calls made */
	size_t maxbytes;  /* maximum byte count argument */
};

struct callinfo i[string];	/* declare i as an associative array */

syscall::read:entry, syscall::write:entry
/pid == $1/
{
	i[probefunc].ts = timestamp;
	i[probefunc].calls++;
	i[probefunc].maxbytes = arg2 > i[probefunc].maxbytes ?
		arg2 : i[probefunc].maxbytes;
}

syscall::read:return, syscall::write:return
/i[probefunc].ts != 0 && pid == $1/
{
	i[probefunc].elapsed += timestamp - i[probefunc].ts;
}

END
{
	printf("        calls  max bytes  elapsed nsecs\n");
	printf("------  -----  ---------  -------------\n");
	printf("  read  %5d  %9d  %d\n",
	    i["read"].calls, i["read"].maxbytes, i["read"].elapsed);
	printf(" write  %5d  %9d  %d\n",
	    i["write"].calls, i["write"].maxbytes, i["write"].elapsed);
}

Después de escribir el programa, ejecute dtrace -q -s rwinfo.d, especificando uno de los procesos de la shell. A continuación, escriba unos cuantos comandos en la shell y, cuando haya terminado de escribir los comandos shell, pulse Control-C en el terminal dtrace para iniciar el sondeo END e imprimir los resultados:


# dtrace -q -s rwinfo.d `pgrep -n ksh`
^C
        calls  max bytes  elapsed nsecs
------  -----  ---------  -------------
  read     36       1024  3588283144
 write     35         59  14945541
#