Guia de rastreamento dinâmico Solaris

Capítulo 3 Variáveis

D fornece dois tipos básicos de variáveis a serem usadas em seus programas de rastreamento: variáveis escalares e matrizes de associação. Demonstramos brevemente o uso dessas variáveis nos exemplos do Capítulo 1. Este capítulo explora mais detalhadamente as regras de variáveis de D e como as variáveis podem ser associadas a escopos diferentes. Um tipo especial de variável de matriz, chamada agregação, é discutido no Capítulo 9Agregações.

Variáveis escalares

As variáveis escalares são usadas para representar objetos de dados de tamanho fixo individuais, tais como inteiros e ponteiros. As variáveis escalares também podem ser usadas para objetos de tamanho fixo que são compostos de um ou mais tipos primitivos ou compostos. D fornece o recurso para criar matrizes de objeto, assim como estruturas compostas. DTrace também representa seqüências como escalares de tamanho fixo, permitindo que elas cresçam para um tamanho máximo predefinido. O controle sobre o tamanho da seqüência em seu programa em D é discutido com mais detalhes no Capítulo 6Seqüências.

As variáveis escalares são criadas automaticamente na primeira vez em que você atribui um valor a um identificador não definido anteriormente em seu programa em D. Por exemplo, para criar uma variável escalar chamada x de tipo int , você pode simplesmente atribuir a ela um valor do tipo int em qualquer cláusula de teste:

BEGIN
{
	x = 123;
}

Variáveis escalares criadas dessa forma são variáveis globais : seu nome e local de armazenamento de dados são definidos uma vez e ficam visíveis em cada cláusula de seu programa em D. Toda vez que você referenciar o identificador x, estará fazendo referência a um único local de armazenamento associado a essa variável.

Ao contrário de ANSI-C, D não requerer declarações explícitas de variáveis. Se você quiser declarar uma variável global para atribuir seu nome e tipo explicitamente antes de usá-la, coloque uma declaração fora das cláusulas de teste em seu programa, como mostrado no exemplo seguinte. Declarações de variáveis explícitas não são necessárias na maioria dos programas em D, mas às vezes são úteis quando você quiser controlar cuidadosamente os tipos de variáveis ou quando quiser começar o seu programa com um conjunto de declarações e comentários documentando as variáveis do seu programa e seus respectivos significados.

int x; /* declare an integer x for later use */

BEGIN
{
	x = 123;
	...
}

Ao contrário das declarações ANSI-C, as declarações de variáveis D podem não atribuir valores iniciais. Você deve usar uma cláusula de teste BEGIN para atribuir quaisquer valores iniciais. O armazenamento de todas as variáveis globais é preenchido com zeros pelo DTrace antes de você referenciar a variável.

A definição de linguagem D não limita o tamanho e o número de variáveis D, mas os limites são definidos pela implementação de DTrace e pela memória disponível no sistema. O compilador D reforçará quaisquer limitações que possam ser aplicadas no momento em que você compila o seu programa. Você pode aprender mais sobre como ajustar as opções relacionadas aos limites do programa no Capítulo 16Opções e ajustáveis.

Matrizes de associação

As matrizes de associação são usadas para representar as coleções de elementos de dados que podem ser recuperados pela especificação de um nome chamado chave. As chaves de matriz de associação de D são formadas por uma lista de valores de expressão escalar chamado tupla. Você pode pensar na tupla de matriz como uma lista de parâmetros imaginários de uma função que é chamada para recuperar o valor da matriz correspondente quando você faz referência à matriz. Cada matriz de associação de D possui uma assinatura de chave fixa que consiste em um número fixo de elementos de tupla, onde cada elemento possui um determinado tipo fixo. Você pode definir assinaturas de chave diferentes para cada matriz em seu programa em D.

As matrizes de associação são diferentes das matrizes de tamanho fixo normais, pois não possuem limite predefinido no número de elementos. Os elementos podem ser indexados por qualquer tupla, ao contrário de usar apenas inteiros como chaves, além disso, os elementos não são armazenados em locais de armazenamento consecutivos pré-alocados. As matrizes de associação são úteis em situações em que você usaria uma tabela hash ou outra estrutura de dados de dicionário simples em um programa de linguagem C, C++ ou Java. TM As matrizes de associação o ajudam a criar um histórico dinâmico de eventos e estado, capturado em seu programa em D, que você pode usar para criar fluxos de controle mais complexos.

Para definir uma matriz de associação, escreva uma expressão de atribuição no formato:

nome [ chave ] = expressão ;

onde nome é qualquer identificador D válido e chave é uma lista separada por vírgula de uma ou mais expressões. Por exemplo, a declaração seguinte define uma matriz de associação a com assinatura de chave [ int, string ] e armazena o valor inteiro 456 em um local nomeado pela tupla [ 123, "olá" ]:

a[123, "hello"] = 456;

O tipo de cada objeto contido na matriz de todos os elementos de uma determinada matriz. Como a foi atribuída primeiro por meio do inteiro 456, cada valor subseqüente armazenado na matriz também será do tipo int. Você pode usar qualquer um dos operadores de atribuição definidos no Capítulo 2 para modificar elementos de matriz de associação, sujeitos às regras de operando definidas para cada operador. O compilador D produzirá uma mensagem de erro apropriada, se você tentar uma atribuição incompatível. Você pode usar qualquer tipo com uma chave de matriz de associação ou valor que você pode usar com uma variável escalar. Você não pode aninhar uma matriz de associação em outra matriz de associação como uma chave ou valor.

Você pode fazer referência a uma matriz de associação por meio de qualquer tupla que seja compatível com a assinatura de chave de matriz. As regras de compatibilidade de tupla são semelhantes às de chamadas de função e atribuições de variável: a tupla deve ser do mesmo tamanho e cada tipo da lista de parâmetros atuais deve ser compatível com o tipo correspondente na assinatura de chave formal. Por exemplo, se uma matriz de associação x for definida da seguinte forma:

x[123ull] = 0;

então, a assinatura de chave é do tipo unsigned long long e os valores são do tipo int. Essa matriz também pode ser referenciada por meio da expressão x['a'] porque a tupla consiste na constante de caractere 'a' do tipo int e o tamanho um é compatível com a assinatura de chave unsigned long long, de acordo com as regras de conversão aritmética descritas em Tipos de conversões.

Se você precisar declarar explicitamente uma matriz de associação de D antes de usá-la, crie uma declaração do nome da matriz e da assinatura de chave fora das cláusulas de teste no código-fonte do seu programa:

int x[unsigned long long, char];

BEGIN
{
	x[123ull, 'a'] = 456;
}

Quando uma matriz de associação é definida, as referências a qualquer tupla ou assinatura de chave compatível são permitidas, mesmo que a tupla em questão não tenha sido atribuída anteriormente. O acesso ao elemento de matriz de associação não atribuída é definido para retornar um objeto preenchido com zeros. Uma conseqüência dessa definição é que o armazenamento subjacente não é alocado para um elemento de matriz de associação até que um valor diferente de zero seja atribuído a esse elemento. Contrariamente, atribuir um elemento de matriz de associação a zero faz com que o DTrace desaloque o armazenamento subjacente. Esse comportamento é importante porque o espaço de variável dinâmica, do qual os elementos de matriz de associação são alocados, é finito; caso o espaço termine durante uma tentativa de alocação, a alocação falhará e será gerada uma mensagem de erro indicando uma queda de variável dinâmica. Sempre atribua zero a elementos de matriz de associação que não estão mais sendo usados. Consulte o Capítulo 16Opções e ajustáveis para obter outras técnicas de eliminar interrupções de variável dinâmica.

Variáveis de segmento locais

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:


Exemplo 3–1 rtime.d: tempo de cálculo gasto em read(2)

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

Variáveis de cláusula locais

Você também pode definir as variáveis de D cujo armazenamento é reutilizado em cada cláusula do programa em D. As variáveis de cláusula locais são semelhantes às variáveis automáticas em um programa da linguagem C, C++ ou Java que estão ativas durante cada chamada de uma função. Como todas as variáveis de programa em D, as variáveis de cláusula locais são criadas na primeira atribuição. Essas variáveis podem ser referenciadas e atribuídas por meio da aplicação do operador -> ao identificador especial this:

BEGIN
{
	this->secs = timestamp / 1000000000;
	...
}

Se você quiser declarar explicitamente uma variável de cláusula local antes de usá-la, use a palavra-chave this:

this int x;   /* an integer clause-local variable */
this char c;  /* a character clause-local variable */

BEGIN
{
	this->x = 123;
	this->c = 'D';
}

As variáveis de cláusula locais só ficam ativas durante um ciclo de vida de uma determinada cláusula de teste. Depois que o DTrace realiza as ações associadas às suas cláusulas em um determinado teste, o armazenamento de todas as variáveis de cláusula locais é recuperado e reutilizado pela próxima cláusula. Por isso, as variáveis de cláusula locais são as únicas variáveis D que não são inicialmente preenchidas com zeros. Observe que se o seu programa contiver várias cláusulas de um único teste, quaisquer variáveis de cláusula locais permanecerão intactas quando as cláusulas forem executadas, como mostrado no exemplo seguinte:


Exemplo 3–2 clause.d: variáveis de cláusula locais

int me;			/* an integer global variable */
this int foo;		/* an integer clause-local variable */

tick-1sec
{
	/*
	 * Set foo to be 10 if and only if this is the first clause executed.
	 */
	this->foo = (me % 3 == 0) ? 10 : this->foo;
	printf("Clause 1 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

tick-1sec
{
	/*
	 * Set foo to be 20 if and only if this is the first clause executed. 
	 */
	this->foo = (me % 3 == 0) ? 20 : this->foo;
	printf("Clause 2 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

tick-1sec
{
	/*
	 * Set foo to be 30 if and only if this is the first clause executed.
	 */
	this->foo = (me % 3 == 0) ? 30 : this->foo;
	printf("Clause 3 is number %d; foo is %d\n", me++ % 3, this->foo++);
}

Como as cláusulas são sempre executadas na ordem do programa, e já que as variáveis de cláusula locais persistem entre cláusulas diferentes, ativando o mesmo teste, executar o programa acima sempre produzirá a mesma saída:


# dtrace -q -s clause.d
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
Clause 1 is number 0; foo is 10
Clause 2 is number 1; foo is 11
Clause 3 is number 2; foo is 12
^C

Enquanto as variáveis de cláusula locais são persistentes entre as cláusulas, ativando o mesmo teste, seus valores são indefinidos na primeira cláusula executada em um determinado teste. Esteja certo de atribuir um valor apropriado a cada variável de cláusula local antes de usá-la, ou seu programa poderá ter resultados inesperados.

As variáveis de cláusula locais podem ser definidas por meio de qualquer tipo de variável escalar, mas as matrizes de associação não podem ser definidas através de escopo de cláusula local. O escopo de variáveis de cláusula locais só se aplica aos dados da variável correspondente, não ao nome e à identidade de tipo definidos para a variável. Quando uma variável de cláusula local é definida, este nome e assinatura de tipo podem ser usados em qualquer cláusula do programa em D subseqüente. Você não pode contar que o local de armazenamento seja o mesmo entre cláusulas diferentes.

Você pode usar variáveis de cláusula globais para acumular resultados intermediários de cálculos ou como cópia de outras variáveis. O acesso a uma variável de cláusula global é muito mais rápido do que o acesso a uma matriz de associação. Portanto, se você precisar fazer referência a um valor de matriz de associação várias vezes na mesma cláusula do programa em D, é mais eficaz copiá-la primeiro para uma variável de cláusula local e, em seguida, referenciar a variável locai repetidamente.

Variáveis internas

A tabela seguinte fornece uma lista de variáveis internas de D. Todas essas variáveis são variáveis globais escalares; nenhuma variável de segmento local, ou variável de cláusula local ou matrizes de associação internas, estão definidas no momento por D.

Tabela 3–1 Variáveis internas do DTrace

Tipo e nome 

Descrição 

int64_t arg0, ..., arg9

Os dez primeiros argumentos de entrada para um teste representado como inteiros brutos de 64 bits. Se menos de dez argumentos forem passados para o teste atual, as variáveis restantes retornarão zero. 

args[]

Os argumentos digitados para o teste atual, se houver. A matriz args[] é acessada por meio de um índice de inteiro, mas cada elemento é definido como o tipo correspondente ao argumento do teste determinado. Por exemplo, se args[] for referenciado por um teste de chamada do sistema read(2), args[0] é do tipo int, args[1] é do tipo void * e args[2] é do tipo size_t.

uintptr_t caller

O local do contador do programa do segmento atual um pouco antes de entrar no teste atual. 

chipid_t chip

O identificador de chipe da CPU do chipe físico atual. Para obter mais informações, consulte o Capítulo 26Provedor sched.

processorid_t cpu

O identificador de CPU da CPU atual. Para obter mais informações, consulte o Capítulo 26Provedor sched.

cpuinfo_t *curcpu

As informações de CPU da CPU atual. Para obter mais informações, consulte o Capítulo 26Provedor sched.

lwpsinfo_t *curlwpsinfo

O estado de processo leve (LWP) do LWP associado ao segmento atual. Esta estrutura é descrita mais detalhadamente na página do manual proc(4).

psinfo_t *curpsinfo

O estado de processo do processo associado ao segmento atual. Esta estrutura é descrita mais detalhadamente na página do manual proc(4).

kthread_t *curthread

O endereço da estrutura de dados interna do kernel do sistema operacional do segmento atual, kthread_t. kthread_t é definido em <sys/thread.h>. Consulte Solaris Internals para obter mais informações sobre esta variável e outras estruturas de dados do sistema operacional.

string cwd

O nome do diretório de trabalho atual do processo associado ao segmento atual. 

uint_t epid

O ID de teste ativado (EPID) do teste atual. Este inteiro identifica exclusivamente um teste em particular com um predicado específico e um conjunto de ações. 

int errno

O valor de erro retornado pela última chamada do sistema executada por este segmento. 

string execname

O nome que foi passado a exec(2) para que execute o processo atual.

gid_t gid

O ID de grupo real do processo atual. 

uint_t id

O ID de teste do teste atual. Este ID é o identificador de teste exclusivo do sistema conforme publicado por DTrace e listado na saída de dtrace -l.

uint_t ipl

O nível de prioridade de saída (IPL) da CPU atual na hora do acionamento do teste. Consulte Solaris Internals para obter mais informações sobre os níveis de interrupção e manipulação de interrupção no kernel do sistema operacional.

lgrp_id_t lgrp

O ID de grupo de latência do grupo de latência do qual a CPU é um membro. Para obter mais informações, consulte o Capítulo 26Provedor sched.

pid_t pid

O ID de processo do processo atual. 

pid_t ppid

O ID de processo pai do processo atual. 

string probefunc

A parte do nome de função da descrição do teste atual. 

string probemod

A parte do nome de módulo da descrição do teste atual. 

string probename

A parte do nome da descrição do teste atual. 

string probeprov

A parte do nome do provedor da descrição do teste atual. 

psetid_t pset

O ID de conjunto de processadores do conjunto de processadores que contém a CPU atual. Para obter mais informações, consulte o Capítulo 26Provedor sched.

string root

O nome do diretório raiz do processo associado ao segmento atual. 

uint_t stackdepth

A profundidade da moldura da porta na hora de acionamento do teste. 

id_t tid

O ID de segmento do segmento atual. Para segmentos associados aos processos do usuário, este valor é igual ao resultado de uma chamada ao pthread_self(3C).

uint64_t timestamp

O valor atual de um contador de carimbo de data e hora em nanossegundos. Este contador é incrementado a partir de um ponto arbitrário no passado e deve ser usado somente para cálculos relativos. 

uid_t uid

O ID de usuário real do processo atual. 

uint64_t uregs[]

O segmento atual salvou os valores de registro de modo do usuário na hora de acionamento do teste. O uso da matriz uregs[] é discutido no Capítulo 33Rastreio de processo do usuário.

uint64_t vtimestamp

O valor atual de um contador de carimbo de data e hora em nanossegundos que é virtualizado no total de tempo que o segmento atual está sendo executado em uma CPU, menos o tempo gasto nos predicados e ações do DTrace. Este contador é incrementado a partir de um ponto arbitrário no passado e deve ser usado somente para cálculos relativos de tempo. 

uint64_t walltimestamp

O número atual de nanossegundos desde 00:00 (Horário Coordenado Universal), 1 de janeiro de 1970. 

As funções internas da linguagem D, tal como trace(), são discutidas no Capítulo 10Ações e sub-rotinas.

Variáveis externas

D usa o caractere de aspa invertida (`) como um operador de escopo especial para acessar variáveis que são definidas no sistema operacional e não em seu programa em D. Por exemplo, o kernel do Solaris contém uma declaração C de um sistema ajustável chamado kmem_flags para ativar os recursos de depuração do alocador de memória. Consulte o Solaris Tunable Parameters Reference Manual para obter informações sobre kmem_flags. Este ajuste é declarado como uma variável C no código-fonte do kernel da seguinte forma:

int kmem_flags;

Para acessar o valor dessa variável em um programa em D, use a notação D:

`kmem_flags

O DTrace associa cada símbolo de kernel ao tipo usado para o símbolo no código C do sistema operacional correspondente, fornecendo acesso fácil baseado na origem às estruturas de dados do sistema operacional nativo. Para usar as variáveis externas do sistema operacional, você precisará acessar o código-fonte do sistema operacional correspondente.

Quando acessa as variáveis externas a partir de um programa em D, você está acessando os detalhes de implementação interna de outro programa, tal como o kernel do sistema operacional ou seus drivers de dispositivo. Esses detalhes de implementação não formam uma interface estável com a qual você possa contar! Quaisquer programas em D que você escreva que dependam desses detalhes podem parar de funcionar quando você fizer a próxima atualização do software correspondente. Por esse motivo, as variáveis externas são geralmente usadas pelo kernel, desenvolvedores de driver de dispositivo e pessoal de serviço, a fim de depurar o desempenho ou problemas de funcionalidade, usando o DTrace. Para aprender mais sobre a estabilidade dos seus programas em D, consulte o Capítulo 39Estabilidade.

Os nomes de símbolo de kernel são mantidos em um espaço de nome separado da variável D e dos identificadores de função, para que você nunca tenha que se preocupar que esses nomes entrem em conflito com as suas variáveis D. Quando você coloca um prefixo em uma variável com uma aspa invertida, o compilador D procura os símbolos de kernel conhecidos para usar a lista de módulos carregados a fim de encontrar uma definição de variável correspondente. Como o kernel do Solaris aceita módulos carregados dinamicamente com espaços de nome de símbolo separados, o mesmo nome de variável deve ser usado mais de uma vez no kernel do sistema operacional ativo. Você pode resolver esses conflitos de nome especificando o nome do módulo de kernel cuja variável deve ser acessada antes da aspa invertida no nome do símbolo. Por exemplo, cada módulo de kernel carregável geralmente fornece uma função _fini(9E), portanto, para fazer referência ao endereço da função _fini fornecida por um módulo de kernel chamado foo, você deve escrever:

foo`_fini

Você pode aplicar qualquer um dos operadores D a variáveis externas, exceto aqueles que modificam valores, sujeitos às regras usuais de tipos de operando. Quando você inicia o DTrace, o compilador D carrega o conjunto de nomes de variáveis correspondentes aos módulos do kernel ativo, portanto, as declarações dessas variáveis não são necessárias. Você não pode aplicar qualquer operador a uma variável externa que modifique seus valores, tais como = ou +=. Por motivos de segurança, o DTrace evita que você danifique ou corrompa o estado do software que está observando.