Guia de rastreamento dinâmico Solaris

Agregações

O DTrace armazena os resultados das funções de agregação em objetos chamados agregações. Os resultados da agregação são indexados usando-se uma tupla de expressões similar às usadas nas matrizes de associação. Em D, a sintaxe de uma agregação é:

@name[ keys ] = aggfunc ( args );

onde nome é o nome da agregação, chaves é uma lista separada por vírgulas de expressões de D, funçagr é uma das funções de agregação do DTrace e args é uma lista de argumentos separados por vírgulas apropriada para a função de agregação. O nome da agregação é um identificador de D que possui como prefixo o caractere especial @. Todas as agregações nomeadas nos programas em D são variáveis globais; não há agregações locais de cláusula ou segmento. Os nomes de agregação são mantidos em um espaço de nome de identificador separado de outras variáveis globais de D. Lembre-se que a e @a não são a mesma variável se você reutilizar nomes. O nome de agregação especial @ pode ser usado para nomear uma agregação anônima em programas em D simples. O compilador de D trata esse nome como um alias do nome de agregação @_.

As funções de agregação de DTrace são mostradas na tabela a seguir. A maioria das funções de agregação utilizam apenas um único argumento que representa os novos dados.

Tabela 9–1 Funções de agregação do DTrace

Nome da função 

Argumentos 

Resultado 

count

nenhum 

O número de vezes chamada. 

sum

expressão escalar 

O valor total das expressões especificadas. 

avg

expressão escalar 

A média aritmética das expressões especificadas. 

min

expressão escalar 

O menor valor entre as expressões especificadas. 

max

expressão escalar 

O maior valor entre as expressões especificadas. 

lquantize

expressão escalar, limite inferior, limite superior, valor da etapa 

Uma distribuição de freqüência linear, dimensionada pelo intervalo especificado, dos valores das expressões especificadas. Incrementa o valor no depósito mais alto que é menor que a expressão especificada.

quantize

expressão escalar 

Uma distribuição de freqüência em potência de dois dos valores das expressões especificadas. Incrementa o valor no depósito mais alto em potência de dois que é menor que a expressão especificada.

Por exemplo, para contar o número de chamadas do sistema write(2) no sistema, você poderia usar uma seqüência informativa como uma chave e a função de agregação count():

syscall::write:entry
{
	@counts["write system calls"] = count();
}

O comando dtrace imprime resultados de agregação por padrão quando o processo é encerrado, seja como o resultado de uma ação END explícita ou quando o usuário pressiona Control-C. O seguinte exemplo mostra o resultado da execução desse comando, aguardando-se alguns segundos e pressionando-se Control-C:


# dtrace -s writes.d
dtrace: script './writes.d' matched 1 probe
^C

  write system calls                                              179
#

Você pode contar chamadas do sistema por nome de processo usando a variável execname como chave de uma agregação:

syscall::write:entry
{
	@counts[execname] = count();
}

O seguinte exemplo mostra o resultado da execução deste comando, aguardando-se alguns segundos e pressionando-se 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
#

Como alternativa, você pode querer examinar em detalhes as gravações organizadas pelo nome do executável e o descritor de arquivos. O descritor de arquivos é o primeiro argumento de write(2), sendo assim, o seguinte exemplo usa uma chave que consiste em execname e arg0:

syscall::write:entry
{
	@counts[execname, arg0] = count();
}

A execução deste comando resulta em uma tabela com o nome do executável e o descritor de arquivos, conforme mostrado no exemplo a seguir:


# 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
#

O exemplo a seguir exibe o tempo médio gasto na chamada do sistema write, organizado por nome de processo. Este exemplo usa a função de agregação avg(), especificando a expressão para realizar média como o argumento. O exemplo faz a média do tempo gasto na chamada do sistema:

syscall::write:entry
{
	self->ts = timestamp;
}

syscall::write:return
/self->ts/
{
	@time[execname] = avg(timestamp - self->ts);
	self->ts = 0;
}

O seguinte exemplo mostra o resultado da execução deste comando, aguardando-se alguns segundos e pressionando-se 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
#

A média pode ser útil, mas geralmente não fornece detalhes suficientes para compreender a distribuição dos pontos de dados. Para compreender a distribuição com mais detalhes, use a função de agregação quantize() conforme mostrado no exemplo a seguir:

syscall::write:entry
{
	self->ts = timestamp;
}

syscall::write:return
/self->ts/
{
	@time[execname] = quantize(timestamp - self->ts);
	self->ts = 0;
}

Como cada linha de resultado torna-se um diagrama de distribuição de freqüência, o resultado deste script é substancialmente maior que os anteriores. O exemplo a seguir mostra uma seleção de resultado de amostra:


  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        

Observe que as linhas da distribuição de freqüência são sempre valores de potência de dois. Cada linha indica a contagem do número de elementos maiores que ou iguais ao valor correspondente, mas menores que o próximo valor maior de linha. Por exemplo, o resultado acima mostra que iropt tinha 4.149 gravações ocorrendo entre 8.192 nanossegundos e 16.383 nanossegundos, inclusive.

Embora quantize() seja útil para obter uma rápida visualização dos dados, talvez você queira examinar uma distribuição em valores lineares. Para exibir uma distribuição de valor linear, use a função de agregação lquantize (). A função lquantize() possui três argumentos além de uma expressão de D. um limite inferior, um limite superior e uma etapa. Por exemplo, se você quisesse visualizar a distribuição de gravações por descritor de arquivo, uma quantização em potência de dois não seria eficiente. Em vez disso, use uma quantização linear com um pequeno intervalo, conforme mostrado no exemplo a seguir:

syscall::write:entry
{
	@fds[execname] = lquantize(arg0, 0, 100, 1);
}

A execução deste script por alguns segundos resulta em uma grande quantidade de informação. O exemplo a seguir mostra uma seleção de resultado típico:


  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        

Você também pode usar a função de agregação lquantize() para agregar no momento a partir de um ponto no passado. Essa técnica permite que você observe uma alteração no comportamento ao longo do tempo. O exemplo a seguir exibe a alteração no comportamento da chamada do sistema durante um processo executando o 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;
}

O script anterior fornece uma melhor avaliação do comportamento da chamada do sistema quando muitos processos date(1) são executados. Para ver esse resultado, execute sh -c 'while true; do date >/dev/null; done' em uma janela, enquanto executa o script de D em outra. O script produz um perfil do comportamento da chamada do sistema do 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 fornece uma breve idéia das diferentes fases do comando date(1)com relação aos serviços solicitados do kernel. Para entender melhor essas fases, talvez você queira compreender quando quais chamadas do sistema estão sendo chamadas. Se esse for o caso, você poderá alterar o script de D para agregar na variável probefunc em vez de uma seqüência de constante.