Guia de rastreamento dinâmico Solaris

Predicados

Uma diferença importante entre D e as outras linguagens de programação como C, C++ e Java é a ausência de construções de fluxo de controle, como instruções if e loops. As cláusulas do programa em D são escritas como listas únicas de instrução em linha reta que rastreiam uma quantidade opcional, fixa de dados. D fornece a capacidade de rastrear dados condicionalmente e modificar o fluxo de controle usando expressões lógicas chamadas predicados que podem ser usadas para prefixar cláusulas do programa. Uma expressão de predicado é avaliada quando o teste é disparado, antes da execução de quaisquer instruções associadas à cláusula correspondente. Se o predicado for avaliado como verdadeiro, representado por um valor diferente de zero, a lista de instruções será executada. Se o predicado for falso, representado por um valor zero, nenhuma das instruções será executada e o teste não será acionado.

Digite o seguinte código-fonte para o próximo exemplo e salve-o em um arquivo chamado countdown.d:

dtrace:::BEGIN
{
	i = 10;
}

profile:::tick-1sec
/i > 0/
{
	trace(i--);
}

profile:::tick-1sec
/i == 0/
{
	trace("blastoff!");
	exit(0);
}

Este programa em D implementa um temporizador de contagem regressiva de 10 segundos usando predicados. Quando executado, countdown.d começa uma contagem regressiva a partir de 10 e depois imprime uma mensagem e encerra:

# dtrace -s countdown.d
dtrace: script 'countdown.d' matched 3 probes
CPU     ID                    FUNCTION:NAME
	0  25499                       :tick-1sec        10
	0  25499                       :tick-1sec         9
	0  25499                       :tick-1sec         8
	0  25499                       :tick-1sec         7
	0  25499                       :tick-1sec         6
	0  25499                       :tick-1sec         5
	0  25499                       :tick-1sec         4
	0  25499                       :tick-1sec         3
	0  25499                       :tick-1sec         2
	0  25499                       :tick-1sec         1
	0  25499                       :tick-1sec   blastoff!
# 

Este exemplo usa o teste BEGIN para inicializar um inteiro i como 10 para iniciar a contagem regressiva. Depois, como no exemplo anterior, o programa usa o teste tick-1sec para implementar um temporizador que é acionado uma vez por segundo. Observe que em countdown.d, a descrição do teste tick-1sec é usada em duas cláusulas diferentes, cada uma com um predicado e uma lista de ações diferentes. O predicado é uma expressão lógica entre barras / / que aparece após o nome do teste e antes das chaves { } que delimitam a lista de instruções da cláusula.

O primeiro predicado testa se i é maior que zero, indicando que o temporizador ainda está sendo executado:

profile:::tick-1sec
/i > 0/
{
	trace(i--);
}

O operador relacional > significa maior que e retorna o valor de inteiro zero para falso e um para verdadeiro. Todos os operadores relacionais de C são suportados em D. A lista completa pode ser encontrada no Capítulo 2Tipos, operadores e expressões. Se i ainda não for zero, o script rastreia i e depois o diminui em um usando o operador --.

O segundo predicado usa o operador == para retornar verdadeiro quando i for exatamente igual a zero, indicando que a contagem regressiva está concluída:

profile:::tick-1sec
/i == 0/
{
	trace("blastoff!");
	exit(0);
}

Similar ao primeiro exemplo, hello.d, countdown.d usa uma seqüência de caracteres entre aspas duplas, chamada de constante de seqüências, para imprimir uma mensagem final quando a contagem regressiva estiver concluída. A função exit() é então usada para encerrar dtrace e retornar ao prompt do shell.

Se você analisar a estrutura de countdown.d, verá que ao criar duas cláusulas com a mesma descrição de teste mas predicados e ações diferentes, você criou o fluxo lógico eficientemente:

i = 10
once per second,
	if i is greater than zero
		trace(i--);
	otherwise if i is equal to zero
		trace("blastoff!");
		exit(0);

Quando você desejar escrever programas complexos usando predicados, tente primeiro visualizar seu algoritmo desta maneira, e depois transforme cada caminho de suas construções condicionais em uma cláusula e um predicado separados.

Agora, vamos combinar predicados com um novo provedor, o syscall , e criar nosso primeiro programa real de rastreio em D. O provedor syscall permite que você ative testes na entrada ou retorno de qualquer chamada do sistema Solaris. O próximo exemplo usa o DTrace para observar cada vez que o shell realiza uma chamada do sistema de read(2) ou write(2). Primeiro, abra duas janelas no terminal, uma para o DTrace e a outra contendo o processo do shell que você vai observar. Na segunda janela, digite o seguinte comando para obter o ID do processo deste shell:


# echo $$
12345

Agora, volte para a primeira janela do terminal e digite o seguinte programa em D e salve-o em um arquivo chamado rw.d. Quando você digitar o programa, substitua 12345 pelo ID do processo do shell impresso em resposta ao seu comando echo.

syscall::read:entry,
syscall::write:entry
/pid == 12345/
{

}

Observe que o corpo da cláusula do teste de rw.d é deixado em branco porque o programa destina-se somente a rastrear a notificação de disparos de teste e não a rastrear dados adicionais. Quando terminar de digitar no rw.d, use o dtrace para iniciar o seu experimento e depois vá para a segunda janela do shell e digite alguns comandos, pressionando a tecla de retorno após cada comando. Enquanto você digita, verá dtrace reportar testes acionados na primeira janela, similar ao seguinte exemplo:


# dtrace -s rw.d
dtrace: script 'rw.d' matched 2 probes
CPU     ID                    FUNCTION:NAME
	0     34                      write:entry 
	0     32                       read:entry 
	0     34                      write:entry 
	0     32                       read:entry 
	0     34                      write:entry 
	0     32                       read:entry 
	0     34                      write:entry 
	0     32                       read:entry 
...

Você agora está observando o shell realizar chamadas do sistema de read(2) e write(2) para ler um caractere da janela do terminal e retornar o resultado! Este exemplo inclui muitos dos conceitos descritos até agora e também alguns novos. Primeiro, para instrumentar read(2) e write(2) da mesma maneira, o script usa uma única cláusula de teste com várias descrições de teste separando as descrições com vírgulas, da seguinte maneira:

syscall::read:entry,
syscall::write:entry

Por questões de legibilidade, a descrição de cada teste aparece em sua própria linha. Esta organização não é obrigatória, mas facilita a leitura do script. Em seguida, o script define um predicado que corresponde somente às chamadas do sistema que são executadas pelo processo do shell:

/pid == 12345/

O predicado usa a variável predefinida do DTrace pid, que sempre tem o valor do ID do processo associado ao segmento que acionou o teste correspondente. O DTrace oferece muitas definições de variáveis internas para coisas úteis como o ID do processo. Veja a seguir uma lista de algumas variáveis do DTrace que você pode usar para escrever seus primeiros programas em D:

Nome da variável 

Tipo de dados 

Significado 

errno

int

Valor do errno atual para chamadas do sistema

execname

string

Nome do arquivo executável do processo atual 

pid

pid_t

ID do processo atual 

tid

id_t

ID do segmento atual 

probeprov

string

Campo do provedor da descrição do teste atual 

probemod

string

Campo do módulo da descrição do teste atual 

probefunc

string

Campo da função da descrição do teste atual 

probename

string

Campo do nome da descrição do teste atual 

Agora que você escreveu um programa de instrumentação real, tente experimentá-lo nos diferentes processos em execução no seu sistema, alterando o ID do processo e os testes de chamada do sistema que são instrumentados. Depois, você pode fazer mais uma simples alteração e transformar o rw.d em uma versão muito simples de uma ferramenta de rastreio de chamada do sistema como truss(1). Um campo de descrição de teste vazio atua como um curinga, correspondendo a qualquer teste, sendo assim, altere o programa para o novo código-fonte a seguir para rastrear qualquer chamada do sistema executada pelo shell:

syscall:::entry
/pid == 12345/
{

}

Tente digitar alguns comandos no shell como cd, ls e date e veja o que o programa DTrace reporta.