O DTrace o ajuda a declarar o armazenamento de variável local para cada segmento do segmento do sistema operacional, ao contrário das variáveis globais demonstradas anteriormente neste capítulo. As variáveis de segmento locais são úteis em situações em que você deseja ativar um este e marcar cada segmento que aciona o teste com alguma marca ou outros dados. Criar um programa para resolver esse problema é fácil em D porque as variáveis de segmento locais compartilham um nome comum em seu código D, mas se referem a um armazenamento de dados separado associado a cada segmento. As variáveis de segmento locais são referenciadas por meio da aplicação do operador -> ao identificador especial self:
syscall::read:entry { self->read = 1; }
Este exemplo de fragmento de D ativa o teste na chamada do sistema read(2) e associa uma variável de segmento local denominada read a cada segmento que aciona o teste. Semelhantemente às variáveis globais, as variáveis de segmento globais são criadas automaticamente na primeira atribuição e deduzem o tipo usado no lado direito da primeira declaração de atribuição (neste exemplo, int).
Cada vez que a variável self->read é referenciada em seu programa em D, o objeto de dados referenciado é aquele associado ao que o segmento do sistema operacional estava executando quando o teste do DTrace correspondente foi acionado. Pense em uma variável de segmento local como uma matriz de associação que é indexada explicitamente por uma tupla que descreve a identidade do segmento no sistema. Uma identidade de segmento é exclusiva durante o ciclo de vida do sistema: se o segmento sair e a mesma estrutura de dados do sistema operacional for usada para criar um novo segmento, esse segmento não reutilizará a mesma identidade de armazenamento de segmentos local do DTrace.
Quando você tiver definido uma variável de segmento local, é possível referenciá-la de qualquer segmento do sistema, mesmo que a variável em questão não tenha sido atribuída anteriormente para esse segmento específico. Se uma cópia do segmento da variável de segmento local ainda não tiver sido atribuída, o armazenamento de dados da cópia será definido para que seja preenchido com zeros. Como nos elementos de matriz de associação, o armazenamento subjacente não é alocado para uma variável de segmento local até que um valor diferente de zero se atribuído a ele. Como também acontece nos elementos de matriz de associação, atribuir zero a uma variável de segmento local faz com que o DTrace desaloque o armazenamento subjacente. Sempre atribua zero a variáveis de segmento locais que não estão mais sendo usada. Consulte o Capítulo 16Opções e ajustáveis para obter outras técnicas de ajustar o espaço de variável dinâmica do qual as variáveis de segmento locais são alocadas.
As variáveis de segmento locais de qualquer tipo podem ser definidas em seu programa em D, incluindo matrizes de associação. Alguns exemplos de definições de variável de segmento local são:
self->x = 123; /* integer value */ self->s = "hello"; /* string value */ self->a[123, 'a'] = 456; /* associative array */
Como com qualquer variável de D, você não precisa declarar variáveis de segmento locais explicitamente antes de usá-las. Se você quiser criar uma declaração mesmo assim, coloque uma fora das cláusulas do seu programa, antecedendo-a da palavra-chave self:
self int x; /* declare int x as a thread-local variable */ syscall::read:entry { self->x = 123; }
As variáveis de segmento globais são mantidas em um espaço de nome separado das variáveis globais, para que você possa reutilizar os nomes. Lembre-se de que x e self->x não serão a mesma variável, se você sobrecarregar os nomes em seu programa! O exemplo seguinte mostra como usar as variáveis de segmento locais. Em um editor de texto, digite o seguinte programa e salve-o em um arquivo chamado rtime.d:
syscall::read:entry { self->t = timestamp; } syscall::read:return /self->t != 0/ { printf("%d/%d spent %d nsecs in read(2)\n", pid, tid, timestamp - self->t); /* * We're done with this thread-local variable; assign zero to it to * allow the DTrace runtime to reclaim the underlying storage. */ self->t = 0; }
Agora vá para o shell e comece a executar o programa. Espere alguns segundos e você deverá ver alguma saída. Se nenhuma saída for exibida, tente executar alguns comandos.
# dtrace -q -s rtime.d 100480/1 spent 11898 nsecs in read(2) 100441/1 spent 6742 nsecs in read(2) 100480/1 spent 4619 nsecs in read(2) 100452/1 spent 19560 nsecs in read(2) 100452/1 spent 3648 nsecs in read(2) 100441/1 spent 6645 nsecs in read(2) 100452/1 spent 5168 nsecs in read(2) 100452/1 spent 20329 nsecs in read(2) 100452/1 spent 3596 nsecs in read(2) ... ^C # |
rtime.d usa uma variável de segmento local denominada t para capturar um carimbo de data e hora na entrada a read(2) por qualquer segmento. Em seguida, na cláusula de retorno, o programa imprime o tempo total gasto em read(2) subtraindo self->t do carimbo de data e hora atual. As variáveis internas de D pid e tid informam o ID do processo e o ID do segmento que está realizando o read(2). Como self->t não é mais necessária já que essa informação está relatada, 0 é atribuído a ela para permitir que o DTrace reutilize o armazenamento subjacente associado a t no segmento atual.
Geralmente, você verá muitas linhas de saída mesmo que não realize nenhuma porque, em segundo plano, os processos e daemons do servidor estão executando o read(2) todo o tempo, mesmo que você não esteja fazendo nada. Tente alterar a segunda cláusula de rtime.d para usar a variável execname para imprimir o nome do processo que está realizando um read(2) para aprender mais:
printf("%s/%d spent %d nsecs in read(2)\n", execname, tid, timestamp - self->t);
Se você encontrar um processo que seja de particular interesse, adicione um predicado para aprender mais sobre o se comportamento de read(2):
syscall::read:entry /execname == "Xsun"/ { self->t = timestamp; }