Guia de rastreamento dinâmico Solaris

Sub-rotinas copyin() e copyinstr()

A interação do DTrace com os processos é um pouco diferente da maioria dos depuradores tradicionais ou das ferramentas de observação. Muitas dessas ferramentas parecer ser executadas no escopo do processo, permitindo que os usuários cancelem diretamente a referência aos ponteiros para variáveis de programa. Em vez de serem executados em um processo ou em parte dele, os testes do DTrace são executados no kernel do Solaris. Para acessar os dados do processo, um teste precisa usar as sub-rotinas copyin() ou copyinstr() para copiar dados do processo do usuário para o espaço de endereço do kernel.

Por exemplo, considere a seguinte chamada do sistema write(2):

ssize_t write(int fd, const void *buf, size_t nbytes);

O programa em D seguinte ilustra uma tentativa incorreta de imprimir o conteúdo de uma seqüência passada para a chamada do sistema write(2):

syscall::write:entry
{
	printf("%s", stringof(arg1)); /* incorrect use of arg1 */
}

Se você tentar executar este script, o DTrace produzirá mensagens de erro semelhantes ao exemplo seguinte:


dtrace: error on enabled probe ID 1 (ID 37: syscall::write:entry): \
    invalid address (0x10038a000) in action #1

A variável arg1, que contém o valor do parâmetro buf, é um endereço que se refere à memória no processo que está executando a chamada do sistema. Para ler a seqüência nesse endereço, use a sub-rotina copyinstr() e registre seu resultado com a ação printf() :

syscall::write:entry
{
	printf("%s", copyinstr(arg1)); /* correct use of arg1 */

A saída deste script mostra todas as seqüências que estão sendo passadas para a chamada do sistema write(2). Entretanto, ocasionalmente, você talvez veja uma saída irregular semelhante ao exemplo seguinte:


  0     37                      write:entry mada���

A sub-rotina copyinstr() age como um argumento de entrada que é o endereço do usuário de uma seqüência ASCII terminada com nulo. Entretanto, os buffers passados para a chamada do sistema write(2) podem se referir a dados binários em vez de seqüências ASCII. Para imprimir somente a quantidade da seqüência que o chamador pretende, use a sub-rotina copyin(), que usa um tamanho como seu segundo argumento:

syscall::write:entry
{
	printf("%s", stringof(copyin(arg1, arg2)));
}

Observe que o operador stringof é necessário para que o DTrace converta corretamente os dados do usuário recuperados, usando copyin (), em uma seqüência. O uso de stringof não é necessário quando se usa copyinstr() porque esta função sempre retorna o tipo string.

Evitando erros

As sub-rotinas copyin() e copyinstr() não podem ler a partir de endereços do usuário que ainda não foram tocados, sendo assim, mesmo um endereço válido pode causar um erro, se a página que contém esse endereço ainda não tiver tido uma falha ao ser acessada. Considere o seguinte exemplo:


# dtrace -n syscall::open:entry'{ trace(copyinstr(arg0)); }'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU     ID                    FUNCTION:NAME
dtrace: error on enabled probe ID 2 (ID 50: syscall::open:entry): invalid address
(0x9af1b) in action #1 at DIF offset 52

Na saída do exemplo acima, o aplicativo estava funcionando corretamente, e o endereço em arg0 era válido, mas ele se referia a uma página que ainda não tinha sido acessada pelo processo correspondente. Para resolver este problema, aguarde que o kernel ou o aplicativo use os dados antes de rastreá-lo. Por exemplo, você pode esperar até que a chamada retorne para aplicar copyinstr(), como mostrado no exemplo seguinte:


# dtrace -n syscall::open:entry'{ self->file = arg0; }' \
-n syscall::open:return'{ trace(copyinstr(self->file)); self->file = 0; }'
dtrace: description 'syscall::open:entry' matched 1 probe
CPU     ID                    FUNCTION:NAME
  2     51                      open:return   /dev/null