Guia de rastreamento dinâmico Solaris

Structs

A palavra-chave de D struct, abreviação de structure (estrutura) é usada para introduzir um novo tipo composto de um grupo de outros tipos. O novo tipo struct pode ser usado como o tipo de variáveis e matrizes de D, permitindo que você defina grupos de variáveis relacionadas sob um único nome. As structs de D são o mesmo que a construção correspondente em C e C++. Caso você tenha programado na linguagem de programação Java, pense em uma struct de D como uma classe, mas uma classe que tenha apenas membros de dados, não métodos.

Vamos supor que você queira criar um programa de rastreio de chamada do sistema mais sofisticado em D, que registre inúmeras informações sobre cada chamada do sistema a read(2) e write(2) executada pelo shell, tal como o tempo decorrido, o número de chamadas e a maior contagem de bytes passada como um argumento. Você poderia escrever uma cláusula de D para registrar essas propriedades em três matrizes de associação separadas, como mostrado no exemplo seguinte:

syscall::read:entry, syscall::write:entry
/pid == 12345/
{
	ts[probefunc] = timestamp;
	calls[probefunc]++;
	maxbytes[probefunc] = arg2 > maxbytes[probefunc] ?
	    arg2 : maxbytes[probefunc];
}

Entretanto, esta cláusula é ineficiente porque o DTrace deve criar três matrizes de associação separadas e armazenar cópias separadas dos valores de tupla idênticos correspondentes a probefunc para cada uma. Em vez disso, você pode conservar espaço e facilitar a leitura e a manutenção do seu programa usando uma struct. Primeiro, declare um novo tipo de struct no início do arquivo-fonte do programa:

struct callinfo {
	uint64_t ts;      /* timestamp of last syscall entry */
	uint64_t elapsed; /* total elapsed time in nanoseconds */
	uint64_t calls;   /* number of calls made */
	size_t maxbytes;  /* maximum byte count argument */
};

A palavra-chave struct é seguida por um identificador opcional usado para fazer referência ao nosso novo tipo, que agora é conhecido como struct callinfo. Os membros de struct são colocados entre um conjunto de chaves { } e a declaração inteira é terminada por um ponto-e-vírgula (; ). Cada membro de struct é definido através da mesma sintaxe que uma declaração de variável de D, com o tipo do membro listado primeiro, seguido por um identificador que nomeia o membro e outro ponto-e-vírgula (;).

A própria declaração struct simplesmente define o novo tipo; ela não cria quaisquer variáveis ou aloca qualquer armazenamento no DTrace. Quando declarada, você pode usar struct callinfo como um tipo no restante do seu programa em D, e cada variável de tipo struct callinfo armazenará uma cópia das quatro variáveis descritas por nosso modelo de estrutura. Os membros serão organizados na memória na ordem da lista de membros, com espaço de preenchimento introduzido entre membros, conforme necessário, para fins de alinhamento de objeto de dados.

Você pode usar os nomes de identificador de membro para acessar valores de membro individual usando o operador “.” escrevendo uma expressão do formato:

nome da variável. nome do membro

O exemplo seguinte é um programa aprimorado que usa o novo tipo de estrutura. Em um editor, digite o seguinte programa em D e salve-o em um arquivo chamado rwinfo.d:


Exemplo 7–1 rwinfo.d: coletar estatísticas de read(2) e write(2)

struct callinfo {
	uint64_t ts;      /* timestamp of last syscall entry */
	uint64_t elapsed; /* total elapsed time in nanoseconds */
	uint64_t calls;   /* number of calls made */
	size_t maxbytes;  /* maximum byte count argument */
};

struct callinfo i[string];	/* declare i as an associative array */

syscall::read:entry, syscall::write:entry
/pid == $1/
{
	i[probefunc].ts = timestamp;
	i[probefunc].calls++;
	i[probefunc].maxbytes = arg2 > i[probefunc].maxbytes ?
		arg2 : i[probefunc].maxbytes;
}

syscall::read:return, syscall::write:return
/i[probefunc].ts != 0 && pid == $1/
{
	i[probefunc].elapsed += timestamp - i[probefunc].ts;
}

END
{
	printf("        calls  max bytes  elapsed nsecs\n");
	printf("------  -----  ---------  -------------\n");
	printf("  read  %5d  %9d  %d\n",
	    i["read"].calls, i["read"].maxbytes, i["read"].elapsed);
	printf(" write  %5d  %9d  %d\n",
	    i["write"].calls, i["write"].maxbytes, i["write"].elapsed);
}

Depois que você digitar o programa, execute o dtrace -q -s rwinfo.d, especificando um dos processos do shell. Em seguida, digite alguns comandos no shell e, quando terminar de inserir os comandos do shell, digite Control-C no terminal do dtrace para acionar o teste END e imprimir os resultados:


# dtrace -q -s rwinfo.d `pgrep -n ksh`
^C
        calls  max bytes  elapsed nsecs
------  -----  ---------  -------------
  read     36       1024  3588283144
 write     35         59  14945541
#