Guia de rastreamento dinâmico Solaris

Capítulo 4 Estrutura de programa em D

Os programas em D consistem em um conjunto de cláusulas que descrevem testes a serem ativados, além de predicados e ações para vincular a esses testes. Os programas em D também podem conter declarações de variáveis, conforme descrito no Capítulo 3Variáveis, e definições de novos tipos, descritos no Capítulo 8Definições de tipo e de constante. Este capítulo descreve formalmente a estrutura geral de um programa em D e dos recursos para construir descrições de teste que correspondem a mais de um teste. Também iremos discutir o uso do pré-processador C, cpp, com programas em D.

Cláusulas e declarações de teste

Como mostrado em nossos exemplos até aqui, um arquivo-fonte de programa em D consiste em uma ou mais cláusulas de teste que descrevem a instrumentação a ser ativada por DTrace. Cada cláusula de teste possui o formato geral:

descrições de teste
/ predicado /
{
	declarações de ação
}

O predicado e a lista de declarações de ações podem ser omitidos. Quaisquer diretivas encontradas fora das cláusulas de teste são chamadas de declarações. As declarações só podem ser usadas fora das cláusulas de teste. Não são permitidas declarações entre { } e as declarações não podem ser colocadas entre os elementos da cláusula de teste mostrada acima. Pode ser usado espaço em branco para separar quaisquer elementos de programa em D e para recuar as declarações de ação.

As declarações podem ser usadas para declarar variáveis de D e símbolos de C externos, conforme discutido no Capítulo 3Variáveis, ou para definir novos tipos para uso em D, conforme descrito no Capítulo 8Definições de tipo e de constante. As diretivas de compilador de D especiais chamadas pragmas também podem aparecer em qualquer lugar em um programa em D, inclusive fora das cláusulas de teste. Os pragmas de D são especificados em linhas que começam com um caractere #. Os pragmas de D são usados, por exemplo, para definir as opções de tempo de execução do DTrace. Consulte o Capítulo 16Opções e ajustáveis para obter detalhes.

Descrições de teste

Cada cláusula do programa em D começa com uma lista de uma ou mais descrições de teste, cada uma tem o formato usual:

provedor:módulo: função:nome

Se um ou mais campos da descrição de teste forem omitidos, os campos especificados serão interpretados da direita para a esquerda pelo compilador de D. Por exemplo, a descrição de teste foo:bar corresponderia a um teste com função foo e nome bar, independentemente do valor do provedor de teste e dos campos de módulo. Portanto, uma descrição de teste é realmente exibida de forma mais correta como um padrão que pode ser usado para corresponder a um ou mais testes com base em seus nomes.

Você deve escrever suas descrições de teste de D especificando todos os quatro delimitadores de campo que podem ser especificados, para que possa especificar o provedor desejado no lado esquerdo. Se você não especificar o provedor, talvez obtenha resultados inesperados, se vários provedores publicarem testes com o mesmo nome. Semelhantemente, as versões futuras do DTrace talvez incluam provedores cujos testes correspondam não intencionalmente às suas descrições de teste parcialmente especificadas. Você pode especificar um provedor mas coincidir qualquer um dos seus respectivos testes, deixando em branco qualquer um dos campos de módulo, função e nome. Por exemplo, a descrição syscall::: pode ser usada para coincidir cada teste publicado pelo provedor syscall do DTrace.

As descrições de teste também oferecem suporte a uma sintaxe de correspondência padrão semelhante à sintaxe de correspondência padrão globbing do shell descrita em sh(1). Antes de coincidir um teste com uma descrição, o DTrace examina cada campo de descrição dos caracteres *, ? e [. Se um desses caracteres aparecer em um campo de descrição de teste e não for precedido por um \, o campo será considerado como um padrão. O padrão de descrição deve corresponder a todo o campo correspondente de um determinado teste. A descrição de teste completa deve coincidir em cada campo para que corresponda e ative um teste com êxito. Um campo de descrição de teste que não seja um padrão deve coincidir com o campo de teste correspondente. Um campo de descrição vazio corresponde a qualquer teste.

Os caracteres especiais na tabela seguinte são reconhecidos em padrões de nome de teste:

Tabela 4–1 Caracteres correspondentes do padrão de nome de teste

Símbolo 

Descrição 

*

Corresponde a qualquer seqüência, incluindo a seqüência nula. 

?

Corresponde a qualquer caractere único. 

[ ... ]

Corresponde a qualquer um dos caracteres incluídos. Um par de caracteres separados por - corresponde a qualquer caractere dentro do par, inclusive. Se o primeiro caractere após [ for !, qualquer caractere não incluído no conjunto será correspondido.

\

Interpreta o próximo caractere como ele mesmo, sem qualquer significado especial. 

Caracteres de correspondência de padrão podem ser usados em qualquer um dos quatro campos das suas descrições de teste. Você também pode usar padrões para listar testes correspondentes utilizando padrões na linha de comando com dtrace - l. Por exemplo, o comando dtrace -l -f kmem_* lista todos os testes do DTrace em funções cujos nomes começam com o prefixo kmem_.

Se você quiser especificar os mesmos predicados e ações para mais de uma descrição de teste ou padrão de descrição, coloque as descrições em uma lista separada por vírgulas. Por exemplo, o programa em D rastrearia um carimbo de data e hora toda vez que os testes associados a chamadas de entrada no sistema contendo as palavras “lwp” ou “sock” fossem acionadas:

syscall::*lwp*:entry, syscall::*sock*:entry
{
	trace(timestamp);
}

Uma descrição de teste também pode especificar um teste usando seu ID de teste de inteiro. Por exemplo, a cláusula:

12345
{
	trace(timestamp);
}

poderia ser usada para ativar o ID de teste 12345, conforme relatado por dtrace -l -i 12345. Você deve sempre escrever seus programas em D usando descrições de teste legíveis. Não há garantias de que o ID de teste de inteiro permaneça consistente, conforme os módulos de kernel do provedor do DTrace são carregados e descarregados, ou após uma reinicialização.

Predicados

Os predicados são expressões entre barras / / que são avaliadas na hora em que o teste é acionado para determinar se as ações associadas devem ser executadas. Os predicados são a construção condicional principal usada para construir um fluxo de controle mais complexo em um programa em D. Você pode omitir inteiramente a seção de predicado da cláusula de teste de qualquer teste, neste caso, as ações são sempre executadas quando o teste é acionado.

As expressões de predicado podem usar qualquer um dos operadores de D descritos anteriormente e podem fazer referência a quaisquer objetos de dados de D, tais como variáveis e constantes. A expressão de predicado deve ser avaliada como um valor inteiro ou tipo de ponteiro, para que possa ser considerada verdadeira ou falsa. Como em todas as expressões de D, um valor zero é interpretado como falso e um valor diferente de zero é interpretado como verdadeiro.

Ações

As ações de teste são descritas por uma lista de declarações separadas por ponto-e-vírgula (; ) e colocadas entre chaves { }. Se você quiser apenas anotar que um teste foi acionado em uma CPU específica sem rastrear quaisquer dados ou realizar quaisquer ações adicionais, especifique um conjunto de chaves vazio sem declarações dentro.

Uso do pré-processador C

A linguagem de programação em C usada para definir as interfaces do sistema Solaris inclui um pré-processador que realiza um conjunto de etapas iniciais na compilação do programa C. Os pré-processadores C são comumente usados para definir substituições de macro, onde um símbolo em um programa C é substituído por outro conjunto de símbolos predefinidos, ou para incluir cópias de arquivos de cabeçalho do sistema. Você pode usar o pré-processador de C junto com seus programas em D, especificando a opção -C do dtrace. Esta opção faz com que o dtrace execute primeiro o pré-processador cpp(1) no arquivo-fonte do seu programa e, em seguida, passe os resultados para o compilador de D. O pré-processador C é descrito mais detalhadamente em The C Programming Language.

O compilador de D carrega automaticamente o conjunto de descrições de tipos associadas à implementação do sistema operacional, mas você pode usar o pré-processador para incluir outras definições de tipo, tais como tipos usados em seus próprios programas em C. Você também pode usar o pré-processador para realizar outras tarefas, tais como criar macros que se expandam em blocos de código de D e outros elementos do programa. Se você usar o pré-processador com o seu programa em D, talvez só possa incluir arquivos que contêm declarações de D válidas. Os arquivos de cabeçalho C típicos incluem apenas declarações externas de tipos e símbolos, que serão interpretadas corretamente pelo compilador de D. O compilador de D não pode analisar os arquivos de cabeçalho de C que incluem elementos de programa adicionais, tais como código-fonte da função de C, e produzirá uma mensagem de erro apropriada.