D permite definir variables en forma de enteros, así como otros tipos para representar cadenas y tipos compuestos denominados estructuras y uniones. Si está familiarizado con el lenguaje de programación C, le agradará saber que puede utilizar también los tipos de C en el lenguaje D. Si, por el contrario, no es un experto en C, no se preocupe: las diferentes clases de tipos de datos se describen en el Capítulo 2Tipos, operadores y expresiones. D también admite un tipo especial de variable denominada matriz asociativa. Una matriz asociativa es parecida a una matriz normal, en el sentido en que se asocia un conjunto de claves a un conjunto de valores, pero, en la matriz asociativa, las claves no se limitan a los enteros de un intervalo fijo.
Las matrices asociativas del lenguaje D pueden indexarse mediante una lista de uno o varios valores de cualquier tipo. Los valores de claves individuales conforman una tupla, que se utiliza para realizar la indexación en la matriz, y acceder o modificar el valor correspondiente a esa clave. Cada tupla utilizada en una determinada matriz asociativa debe ajustarse a la misma firma de tipo; es decir, cada clave de la tupla debe tener la misma longitud y los mismos tipos de claves en el mismo orden. El valor asociado a cada elemento de una determinada matriz asociativa debe ser también de un único tipo fijo para toda la matriz. Por ejemplo, la siguiente instrucción del lenguaje D define una matriz asociativa a con el tipo de valor int y la firma de tupla [ string, int ], y almacena el valor de entero 456 en la matriz:
a["hello", 123] = 456;
Una vez definida una matriz, se puede acceder a sus elementos del mismo modo que en cualquier otra variable del lenguaje D. Por ejemplo, la siguiente instrucción del lenguaje D modifica el elemento de la matriz almacenada anteriormente en a, aumentando el valor de 456 a 457:
a["hello", 123]++;
Los valores de los elementos de la matriz que aún no se hayan asignado se establecerán en cero. Ahora utilicemos una matriz asociativa en un programa D. Escriba el siguiente programa y guárdelo en un archivo con el nombre rwtime.d:
syscall::read:entry, syscall::write:entry /pid == $1/ { ts[probefunc] = timestamp; } syscall::read:return, syscall::write:return /pid == $1 && ts[probefunc] != 0/ { printf("%d nsecs", timestamp - ts[probefunc]); }
Al igual que con trussrw.d, especifique el Id. del proceso de la shell al ejecutar rwtime.d. Si escribe varios comandos de la shell, verá el tiempo transcurrido durante cada llamada del sistema. Escriba el siguiente comando y, a continuación, pulse Intro varias veces en la otra shell:
# dtrace -s rwtime.d `pgrep -n ksh` dtrace: script 'rwtime.d' matched 4 probes CPU ID FUNCTION:NAME 0 33 read:return 22644 nsecs 0 33 read:return 3382 nsecs 0 35 write:return 25952 nsecs 0 33 read:return 916875239 nsecs 0 35 write:return 27320 nsecs 0 33 read:return 9022 nsecs 0 33 read:return 3776 nsecs 0 35 write:return 17164 nsecs ... ^C # |
Para realizar un seguimiento del tiempo transcurrido para cada llamada del sistema, debe instrumentar la entrada a read(2) y write(2) y la devolución, y obtener muestras del tiempo en cada punto. A continuación, en la devolución de una determinada llamada del sistema, debe calcular la diferencia entre la primera y la segunda marca de tiempo. Puede utilizar diferentes variables para cada llamada del sistema, pero esto dificultaría la ampliación a llamadas del sistema adicionales en el programa. En su lugar, es más sencillo usar una matriz asociativa indexada por el nombre de función del sondeo. A continuación se muestra la primera cláusula del sondeo:
syscall::read:entry, syscall::write:entry /pid == $1/ { ts[probefunc] = timestamp; }
Esta cláusula define una matriz con el nombre ts y asigna al miembro adecuado el valor de la variable timestamp de DTrace. Esta variable devuelve el valor de un contador de nanosegundos siempre incremental, parecido a la rutina de biblioteca gethrtime(3C) de Solaris. Una vez guardada la marca del tiempo de la entrada, el sondeo de devolución correspondiente obtiene de nuevo muestras de timestamp e informa de la diferencia entre el tiempo actual y el valor guardado:
syscall::read:return, syscall::write:return /pid == $1 && ts[probefunc] != 0/ { printf("%d nsecs", timestamp - ts[probefunc]); }
Es necesario que DTrace realice un seguimiento del proceso adecuado y que el sondeo entry correspondiente se haya activado y haya asignado un valor diferente a cero a ts[probefunc]. Este truco permite eliminar la salida no válida cuando se inicia por primera vez DTrace. Si la shell ya está esperando la entrada en una llamada del sistema read(2) al ejecutar dtrace, el sondeo read:return se activará sin necesidad de una entrada read:entry precedente, ya que esta primera acción read(2) y ts[probefunc] se evaluará en cero debido a que aún no se ha asignado.