Guia de rastreamento dinâmico Solaris

Matrizes

D permite que você defina variáveis que sejam inteiros, assim como outros tipos para representar seqüências e tipos compostos chamados structs e uniões. Se você estiver familiarizado com a programação C, ficará feliz em saber que pode usar em D todos os tipos que pode em C. Se você não for especialista em C, não se preocupe: os tipos diferentes de dados são todos descritos no Capítulo 2Tipos, operadores e expressões. D também oferece suporte a um tipo especial de variável chamada de matriz de associação. Uma matriz de associação é similar a uma matriz normal, pois ela associa um conjunto de chaves a um conjunto de valores, mas em uma matriz de associação, as chaves não são limitadas a inteiros de um intervalo fixo.

As matrizes de associação em D podem ser indexadas por uma lista de um ou mais valores de qualquer tipo. Juntos, os valores das chaves individuais formam uma tupla que é usada para indexar na matriz e acessar ou modificar o valor correspondente a essa chave. Cada tupla usada com uma determinada matriz de associação deve estar de acordo com a assinatura de mesmo tipo; ou seja, cada chave da tupla deve ter o mesmo tamanho e ter os mesmos tipos de chave na mesma ordem. O valor associado a cada elemento de uma determinada matriz de associação é também de um tipo fixo único para toda a matriz. Por exemplo, a seguinte instrução de D define uma nova matriz de associação a do tipo de valor int com a assinatura de tupla [ string, int ] e armazena o valor de inteiro 456 na matriz:

a["hello", 123] = 456;

Quando uma matriz é definida, seus elementos podem ser acessados como qualquer outra variável de D. Por exemplo, a seguinte instrução de D modifica o elemento da matriz previamente armazenado em a aumentando o valor de 456 para 457:

a["hello", 123]++;

Os valores dos elementos de uma matriz que você ainda não tenha atribuído são definidos como zero. Vamos usar uma matriz de associação em um programa em D. Digite o seguinte programa e salve-o em um arquivo chamado rwtime.d:


Exemplo 1–3 rwtime.d: chamadas read(2) e write(2) de tempo

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]);
}

Assim como em trussrw.d, especifique o ID do processo do shell quando executar rwtime.d. Se você digitar alguns comandos do shell, verá o tempo decorrido durante cada chamada do sistema. Digite o seguinte comando e pressione a tecla de retorno algumas vezes no seu outro 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 rastrear o tempo decorrido para cada chamada do sistema, você deve instrumentar a entrada e o retorno de read(2) e write(2) e fazer amostragem do tempo em cada ponto. Em seguida, no retorno de uma determinada chamada do sistema, você deve calcular a diferença entre o primeiro e o segundo carimbo de data/hora. Você poderia usar variáveis separadas para cada chamada do sistema, mas assim seria complicado estender o programa a chamadas adicionais do sistema. Em vez disso, é mais fácil usar uma matriz de associação indexada pelo nome da função do teste. Esta é a primeira cláusula do teste:

syscall::read:entry,
syscall::write:entry
/pid == $1/
{
	ts[probefunc] = timestamp;
}

Esta cláusula define uma matriz denominada ts e atribui ao membro apropriado o valor da variável timestamp do DTrace. Essa variável retorna o valor de um contador de nanossegundos progressivo, similar à rotina de biblioteca do Solaris gethrtime(3C). Quando o carimbo de data/hora da entrada é salvo, o teste de retorno correspondente realiza a amostragem de timestamp novamente e reporta a diferença entre a hora atual e o valor salvo:

syscall::read:return,
syscall::write:return
/pid == $1 && ts[probefunc] != 0/
{
	printf("%d nsecs", timestamp - ts[probefunc]);
}

O predicado no teste de retorno requer que o DTrace esteja rastreando o processo apropriado e que o teste entry correspondente já tenha sido acionado e atribuído um valor diferente de zero a ts[probefunc]. Esse truque elimina resultados inválidos quando o DTrace é iniciado pela primeira vez. Se o shell já estiver aguardando em uma chamada do sistema read(2) por entrada quando você executar dtrace, o teste read:return será acionado sem um read:entry precedente para essa primeira read(2) e ts[probefunc] terá valor zero porque ainda não foi atribuído.