DTrace almacena los resultados de las funciones de adición en objetos denominados adiciones. Los resultados de la adición se indexan mediante una tupla de expresiones similares a las utilizadas para las matrices asociativas. En D, la sintaxis de una adición es:
@name[ keys ] = aggfunc ( args );
donde name es el nombre de la adición, keys es una lista separada por comas de expresiones del lenguaje D, aggfunc es una de las funciones de adición de DTrace y args es una lista separada por comas de los argumentos adecuados para la función de adición. La variable de adición name es un identificador de D que incluye, al principio, el carácter especial @. Todas las adiciones con nombre de los programas D son variables globales; no existe ninguna adición local de cláusula o subproceso. Los nombres de las adiciones se mantienen en espacios de nombres de identificadores distintos de las otras variables globales de D. Recuerde que a y @a no son la misma variable si vuelve a utilizar los nombres. El nombre de adición especial @ puede utilizarse para asignar un nombre a una adición anónima en programas D sencillos. El compilador del lenguaje D considera este nombre un alias del nombre de adición @_.
Las funciones de adición de DTrace se muestran en la siguiente tabla. La mayoría de las funciones de adición utilizan un solo argumento que representa el nuevo dato.
Tabla 9–1 Funciones de adición de DTrace
Nombre de la función |
Argumentos |
Resultado |
---|---|---|
count |
ninguno |
El número de veces que se ha realizado una llamada a una función. |
sum |
expresión escalable |
El valor total de las expresiones especificadas. |
avg |
expresión escalable |
La media aritmética de las expresiones especificadas. |
min |
expresión escalable |
El valor inferior entre las expresiones especificadas. |
max |
expresión escalable |
El valor superior entre las expresiones especificadas. |
lquantize |
expresión escalable, límite inferior, límite superior, valor del paso |
Una distribución de frecuencia lineal, clasificada según el tamaño en el intervalo indicado, de los valores de las expresiones especificadas. Incrementa el valor del compartimento superior, que es inferior a la expresión especificada. |
quantize |
expresión escalable |
Una distribución de frecuencia de potencias de dos de los valores de las expresiones especificadas. Incrementa el valor del compartimento superior de potencias de dos, que es inferior a la expresión especificada. |
Por ejemplo, para realizar el recuento de las llamadas de sistema write(2) en el mismo, puede utilizar una cadena de carácter informativo como clave y la función de adición count():
syscall::write:entry { @counts["write system calls"] = count(); }
El comando dtrace imprime de forma predeterminada los resultados de la adición cuando finaliza el proceso, ya sea como resultado de una acción END específica o cuando el usuario pulsa Control-C. La siguiente salida de ejemplo muestra el resultado de la ejecución de este comando, al esperar unos segundos y pulsar Control-C:
# dtrace -s writes.d dtrace: script './writes.d' matched 1 probe ^C write system calls 179 # |
Puede realizar un recuento de las llamadas del sistema por nombres de procesos utilizando la variable execname como clave en una adición:
syscall::write:entry { @counts[execname] = count(); }
En la siguiente salida de ejemplo, se muestra el resultado de la ejecución de este comando, al esperar varios segundos y pulsar Control-C:
# dtrace -s writesbycmd.d dtrace: script './writesbycmd.d' matched 1 probe ^C dtrace 1 cat 4 sed 9 head 9 grep 14 find 15 tail 25 mountd 28 expr 72 sh 291 tee 814 def.dir.flp 1996 make.bin 2010 # |
También es posible que desee examinar más detalladamente las acciones de escritura organizadas por el nombre del archivo ejecutable y el descriptor de archivo. El descriptor del archivo es el primer argumento de write(2), por lo que, en el siguiente ejemplo, se utiliza una clave formada por execname y arg0:
syscall::write:entry { @counts[execname, arg0] = count(); }
Al ejecutar este comando, se genera una tabla con el nombre del archivo ejecutable y el descriptor de archivo, como se muestra en el siguiente ejemplo:
# dtrace -s writesbycmdfd.d dtrace: script './writesbycmdfd.d' matched 1 probe ^C cat 1 58 sed 1 60 grep 1 89 tee 1 156 tee 3 156 make.bin 5 164 acomp 1 263 macrogen 4 286 cg 1 397 acomp 3 736 make.bin 1 880 iropt 4 1731 # |
En el siguiente ejemplo, se muestra el tiempo medio que ha durado la llamada del sistema de escritura, organizado por el nombre de proceso. En este ejemplo se utiliza la función de adición avg(), especificando la expresión que permite calcular el promedio como argumento. En este ejemplo, se calcula el promedio del tiempo cronometrado que ha durado la llamada del sistema:
syscall::write:entry { self->ts = timestamp; } syscall::write:return /self->ts/ { @time[execname] = avg(timestamp - self->ts); self->ts = 0; }
En la siguiente salida de ejemplo, se muestra el resultado de la ejecución de este comando, al esperar varios segundos y pulsar Control-C:
# dtrace -s writetime.d dtrace: script './writetime.d' matched 2 probes ^C iropt 31315 acomp 37037 make.bin 63736 tee 68702 date 84020 sh 91632 dtrace 159200 ctfmerge 321560 install 343300 mcs 394400 get 413695 ctfconvert 594400 bringover 1332465 tail 1335260 # |
El promedio puede resultar de utilidad, pero, a menudo, no ofrece suficiente información para conocer la distribución de los puntos de datos. Para conocer detalladamente la distribución, utilice la función de adición quantize(), como se muestra en el siguiente ejemplo:
syscall::write:entry { self->ts = timestamp; } syscall::write:return /self->ts/ { @time[execname] = quantize(timestamp - self->ts); self->ts = 0; }
Como cada línea de salida se convierte en un diagrama de distribución de frecuencia, el resultado de esta secuencia de comandos es considerablemente más largo que los anteriores. En el siguiente ejemplo, se muestra una selección de la salida de muestra:
lint value ------------- Distribution ------------- count 8192 | 0 16384 | 2 32768 | 0 65536 |@@@@@@@@@@@@@@@@@@@ 74 131072 |@@@@@@@@@@@@@@@ 59 262144 |@@@ 14 524288 | 0 acomp value ------------- Distribution ------------- count 4096 | 0 8192 |@@@@@@@@@@@@ 840 16384 |@@@@@@@@@@@ 750 32768 |@@ 165 65536 |@@@@@@ 460 131072 |@@@@@@ 446 262144 | 16 524288 | 0 1048576 | 1 2097152 | 0 iropt value ------------- Distribution ------------- count 4096 | 0 8192 |@@@@@@@@@@@@@@@@@@@@@@@ 4149 16384 |@@@@@@@@@@ 1798 32768 |@ 332 65536 |@ 325 131072 |@@ 431 262144 | 3 524288 | 2 1048576 | 1 2097152 | 0 |
Tenga en cuenta que las filas de la distribución de frecuencia están formadas siempre por valores de potencias de dos. Cada fila indica el recuento del número de elementos superiores o iguales al valor correspondiente, pero inferiores al valor superior de la fila siguiente. Por ejemplo, el resultado anterior muestra que iropt presentaba 4.149 operaciones de escritura que han tardado en realizarse entre 8.192 y 16.383 nanosegundos, incluidos estos dos valores.
Mientras que quantize() es útil para obtener una perspectiva rápida de los datos, es posible que desee examinar, en su lugar, una distribución entre valores lineales. Para mostrar una distribución de valores lineales, utilice la función de adición lquantize. () La función lquantize() utiliza tres argumentos, además de una expresión de D: un límite inferior, un límite superior y un paso. Por ejemplo, si desea consultar la distribución de operaciones de escritura por descriptor de archivo, no resultaría eficaz realizar una cuantificación de potencias de dos. En su lugar, utilice una cuantificación lineal con un intervalo reducido, como se muestra en el siguiente ejemplo:
syscall::write:entry { @fds[execname] = lquantize(arg0, 0, 100, 1); }
Al ejecutar esta secuencia de comandos durante varios segundos, se obtiene como resultado una gran cantidad de información. En el siguiente ejemplo, se muestra una selección de la salida habitual:
mountd value ------------- Distribution ------------- count 11 | 0 12 |@ 4 13 | 0 14 |@@@@@@@@@@@@@@@@@@@@@@@@@ 70 15 | 0 16 |@@@@@@@@@@@@ 34 17 | 0 xemacs-20.4 value ------------- Distribution ------------- count 6 | 0 7 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 521 8 | 0 9 | 1 10 | 0 make.bin value ------------- Distribution ------------- count 0 | 0 1 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 3596 2 | 0 3 | 0 4 | 42 5 | 50 6 | 0 acomp value ------------- Distribution ------------- count 0 | 0 1 |@@@@@ 1156 2 | 0 3 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 6635 4 |@ 297 5 | 0 iropt value ------------- Distribution ------------- count 2 | 0 3 | 299 4 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 20144 5 | 0 |
También puede utilizar la función de adición lquantize() para realizar una adición en el tiempo a partir de un punto del pasado. Esta técnica permite supervisar un cambio en el comportamiento a lo largo del tiempo. En el ejemplo siguiente, se muestra un cambio en el comportamiento de las llamadas del sistema durante la duración de un proceso que está ejecutando el comando date(1):
syscall::exec:return, syscall::exece:return /execname == "date"/ { self->start = vtimestamp; } syscall:::entry /self->start/ { /* * We linearly quantize on the current virtual time minus our * process's start time. We divide by 1000 to yield microseconds * rather than nanoseconds. The range runs from 0 to 10 milliseconds * in steps of 100 microseconds; we expect that no date(1) process * will take longer than 10 milliseconds to complete. */ @a["system calls over time"] = lquantize((vtimestamp - self->start) / 1000, 0, 10000, 100); } syscall::rexit:entry /self->start/ { self->start = 0; }
La secuencia de comandos anterior proporciona información detallada sobre el comportamiento de las llamadas del sistema cuando se ejecutan varios procesos date(1). Para ver este resultado, ejecute sh -c 'while true; do date >/dev/null; done' en una ventana, mientras ejecuta la secuencia de comandos de D en otra. Esta secuencia de comandos genera un perfil del comportamiento de las llamadas del sistema del comando date(1):
# dtrace -s dateprof.d dtrace: script './dateprof.d' matched 218 probes ^C system calls over time value ------------- Distribution ------------- count < 0 | 0 0 |@@ 20530 100 |@@@@@@ 48814 200 |@@@ 28119 300 |@ 14646 400 |@@@@@ 41237 500 | 1259 600 | 218 700 | 116 800 |@ 12783 900 |@@@ 28133 1000 | 7897 1100 |@ 14065 1200 |@@@ 27549 1300 |@@@ 25715 1400 |@@@@ 35011 1500 |@@ 16734 1600 | 498 1700 | 256 1800 | 369 1900 | 404 2000 | 320 2100 | 555 2200 | 54 2300 | 17 2400 | 5 2500 | 1 2600 | 7 2700 | 0 |
Este resultado proporciona una idea general de las diferentes fases del comando date(1) en relación con los comandos necesarios del núcleo. Para conocer mejor estas fases, es posible que desee conocer las llamadas del sistema que se están realizando en cada momento. Si es así, puede cambiar la secuencia de comandos de D para realizar la adición en la variable probefunc en lugar de en una cadena constante.