Guia de rastreamento dinâmico Solaris

Ações destrutivas

Algumas ações do DTrace são destrutivas pois alteram o estado do sistema de uma forma bem definida. As ações destrutivas não podem ser usadas a menos que tenham sido explicitamente ativadas. Ao usar o dtrace(1M), você pode ativar ações destrutivas usando a opção -w. Se for feita uma tentativa de ativar ações destrutivas no dtrace(1M) sem ativá-las explicitamente, haverá uma falha no dtrace com uma mensagem similar ao seguinte exemplo:


dtrace: failed to enable 'syscall': destructive actions not allowed

Ações destrutivas de processo

Algumas ações são destrutivas somente para um processo em particular. Essas ações estão disponíveis para usuários com os privilégios dtrace_proc ou dtrace_user. Consulte o Capítulo 35Segurança para obter detalhes sobre privilégios de segurança do DTrace.

stop()

void stop(void)

A ação stop () força o processo que aciona o teste ativado para parar quando ele deixar o kernel da próxima vez, como se fosse interrompido por uma ação proc(4). O utilitário prun(1) pode ser usado para resumir um processo que foi interrompido pela ação stop(). A ação stop() pode ser usada para interromper um processo em qualquer ponto do teste de DTrace. Essa ação pode ser usada para capturar um programa em um estado particular que seria difícil de alcançar com um simples ponto de interrupção e depois anexar um depurador tradicional como o mdb(1) ao processo. Você também pode usar o utilitário gcore(1) para salvar o estado de um processo interrompido em um arquivo de núcleo para análise posterior.

raise()

void raise(int signal)

A ação raise () envia o sinal especificado para o processo em execução no momento. Essa ação é similar ao uso do comando kill(1) para enviar um sinal a um processo. A ação raise() pode ser usada para enviar um sinal em um ponto preciso na execução de um processo.

copyout()

void copyout(void *buf, uintptr_t addr, size_t nbytes)

A ação copyout() copia nbytes do buffer especificado por buf para o endereço especificado por addr no espaço de endereço do processo associado ao segmento atual. Se o endereço do espaço do usuário não corresponder a uma página válida, com falhas, no espaço de endereço atual, um erro será gerado.

copyoutstr()

void copyoutstr(string str, uintptr_t addr, size_t maxlen)

A ação copyoutstr() copia a seqüência especificada por str para o endereço especificado por addr no espaço de endereço do processo associado ao segmento atual. Se o endereço do espaço do usuário não corresponder a uma página válida, com falhas, no espaço de endereço atual, um erro será gerado. O tamanho da seqüência é limitado ao valor definido pela opção strsize. Consulte o Capítulo 16Opções e ajustáveis para obter detalhes.

system()

void system(string program, ...) 

A ação system () faz com que o programa especificado por programa seja executado como se fosse fornecido ao shell como entrada. A seqüência de programa pode conter qualquer uma das conversões de formato de printf()/ printa. () Os argumentos que correspondam às conversões de formato devem ser especificados. Consulte o Capítulo 12Formatação de saída para obter detalhes sobre as conversões de formato válidas.

O exemplo a seguir executa o comando date(1) uma vez por segundo:


# dtrace -wqn tick-1sec'{system("date")}'
 Tue Jul 20 11:56:26 CDT 2004
 Tue Jul 20 11:56:27 CDT 2004
 Tue Jul 20 11:56:28 CDT 2004
 Tue Jul 20 11:56:29 CDT 2004
 Tue Jul 20 11:56:30 CDT 2004

O exemplo a seguir mostra um uso mais elaborado da ação, usando conversões de printf() na seqüência de programa junto com ferramentas de filtragem tradicionais como pipes:

#pragma D option destructive
#pragma D option quiet

proc:::signal-send
/args[2] == SIGINT/
{
	printf("SIGINT sent to %s by ", args[1]->pr_fname);
	system("getent passwd %d | cut -d: -f5", uid);
}

Executar o script acima resultará numa saída semelhante ao exemplo seguinte:


# ./whosend.d
SIGINT sent to MozillaFirebird- by Bryan Cantrill
SIGINT sent to run-mozilla.sh by Bryan Cantrill
^C
SIGINT sent to dtrace by Bryan Cantrill

A execução do comando especificado não ocorre no contexto do teste acionado – ela ocorre quando o buffer que contém os detalhes da ação system() é processado no nível do usuário. Como e quando esse processamento ocorre depende da política de buffer, descrita no Capítulo 11Buffers e armazenamento em buffer. Com a política de buffer padrão, a taxa de processamento do buffer é especificada pela opção switchrate . Você poderá ver o atraso inerente em system() se ajustar explicitamente switchrate com um valor mais alto que o padrão de um segundo, conforme mostrado no exemplo a seguir:

#pragma D option quiet
#pragma D option destructive
#pragma D option switchrate=5sec

tick-1sec
/n++ < 5/
{
	printf("walltime  : %Y\n", walltimestamp);
	printf("date      : ");
	system("date");
	printf("\n");
}

tick-1sec
/n == 5/
{
	exit(0);
}

Executar o script acima resultará numa saída semelhante ao exemplo seguinte:


# dtrace -s ./time.d
 walltime  : 2004 Jul 20 13:26:30
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:31
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:32
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:33
date      : Tue Jul 20 13:26:35 CDT 2004

walltime  : 2004 Jul 20 13:26:34
date      : Tue Jul 20 13:26:35 CDT 2004

Observe que os valores de walltime diferem, mas os valores de date são idênticos. Esse resultado reflete o fato de que a execução do comando date(1) ocorreu somente quando o buffer foi processado, não quando a ação system() foi registrada.

Ações destrutivas do kernel

Algumas ações são destrutivas para todo o sistema. Essas ações obviamente devem ser usadas com muito cuidado, pois elas irão afetar todos os processos no sistema e qualquer outro sistema que dependa implicita ou explicitamente dos serviços de rede do sistema afetado.

breakpoint()

void breakpoint(void)

A ação breakpoint() induz a um ponto de interrupção do kernel, fazendo com que o sistema pare e transfira o controle para o depurador do kernel. O depurador do kernel emitirá uma seqüência indicando o teste do DTrace que acionou a ação. Por exemplo, se a seguinte ação fosse realizada:


# dtrace -w -n clock:entry'{breakpoint()}'
dtrace: allowing destructive actions
dtrace: description 'clock:entry' matched 1 probe

No Solaris em execução no SPARC, a seguinte mensagem poderia aparecer no console:


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb 30002765700)
Type  'go' to resume
ok

No Solaris em execução no x86, a seguinte mensagem poderia aparecer no console:


dtrace: breakpoint action at probe fbt:genunix:clock:entry (ecb d2b97060)
stopped at      int20+0xb:      ret
kmdb[0]:

O endereço após a descrição do teste é o endereço do bloco de controle de ativação (ECB) no DTrace. Você pode usar esse endereço para determinar mais detalhes sobre a ativação do teste que induziu a ação de ponto de interrupção.

Um erro na ação breakpoint() pode fazer com que ela seja chamada mais vezes do que o pretendido. Esse comportamento pode, por sua vez, impedir até mesmo que você encerre o consumidor do DTrace que está acionando as ações de ponto de interrupção. Nessa situação, defina a variável de inteiro do kernel dtrace_destructive_disallow como 1. Essa configuração irá impedir todas as ações destrutivas na máquina. Aplique essa configuração somente nessa situação em particular.

O método exato para a definição de dtrace_destructive_disallow dependerá do depurador do kernel que você está usando. Se estiver usando o PROM de OpenBoot em um sistema SPARC, use w!:


ok 1 dtrace_destructive_disallow w!
ok

Confirme que a variável foi definida usando-se w?:


ok dtrace_destructive_disallow w?
1
ok

Continue digitando go:


ok go

Se estiver usando kmdb(1) em sistemas x86 ou SPARC, use o modificador de gravação de 4 bytes (W) com o dcmd de formatação /:


kmdb[0]: dtrace_destructive_disallow/W 1
dtrace_destructive_disallow:    0x0             =       0x1
kmdb[0]:

Continue usando :c:


kadb[0]: :c

Para reativar ações destrutivas após prosseguir, você precisará redefinir explicitamente dtrace_destructive_disallow de volta para 0 usando mdb(1):


# echo "dtrace_destructive_disallow/W 0" | mdb -kw
dtrace_destructive_disallow:    0x1             =       0x0
#

panic()

void panic(void)

A ação panic () causa um aviso grave no kernel quando acionada. Essa ação deve ser usada para forçar um despejo de memória do sistema quando for necessário. Você pode usar essa ação junto com o buffer de anel e a análise post mortem para compreender um problema. Para obter mais informações, consulte o Capítulo 11Buffers e armazenamento em buffer e o Capítulo 37Rastreio post-mortem respectivamente. Quando a ação panic é usada, uma mensagem é exibida indicando o teste que causou o aviso grave. Por exemplo:


  panic[cpu0]/thread=30001830b80: dtrace: panic action at probe
  syscall::mmap:entry (ecb 300000acfc8)

  000002a10050b840 dtrace:dtrace_probe+518 (fffe, 0, 1830f88, 1830f88,
    30002fb8040, 300000acfc8)
    %l0-3: 0000000000000000 00000300030e4d80 0000030003418000 00000300018c0800
    %l4-7: 000002a10050b980 0000000000000500 0000000000000000 0000000000000502
  000002a10050ba30 genunix:dtrace_systrace_syscall32+44 (0, 2000, 5,
    80000002, 3, 1898400)
    %l0-3: 00000300030de730 0000000002200008 00000000000000e0 000000000184d928
    %l4-7: 00000300030de000 0000000000000730 0000000000000073 0000000000000010

  syncing file systems... 2 done
  dumping to /dev/dsk/c0t0d0s1, offset 214827008, content: kernel
  100% done: 11837 pages dumped, compression ratio 4.66, dump
  succeeded
  rebooting...

syslogd(1M) também emitirá uma mensagem na reinicialização:


  Jun 10 16:56:31 machine1 savecore: [ID 570001 auth.error] reboot after panic:
  dtrace: panic action at probe syscall::mmap:entry (ecb 300000acfc8)

O buffer de mensagem do despejo de memória também contém o teste e o ECB responsável pela ação panic().

chill()

void chill(int nanoseconds)

A ação chill () faz com que o DTrace gire para o número especificado de nanossegundos. chill() é principalmente útil para explorar problemas que possam estar relacionados ao tempo. Por exemplo, você pode usar essa ação para abrir janelas de condição de corrida ou para colocar ou tirar eventos periódicos de fase entre si. Como as interrupções são desativadas durante o contexto de teste do DTrace, qualquer uso de chill() irá induzir à latência de interrupção, latência de agendamento e latência de distribuição. Portanto, chill() pode causar efeitos sistêmicos não esperados e não deve ser usada indiscriminadamente. Como a atividade do sistema depende de uma manipulação periódica de interrupção, o DTrace irá se recusar a executar a ação chill() por mais de 500 milissegundos de cada intervalo de um segundo em uma determinada CPU. Se o intervalo máximo de chill() for excedido, o DTrace irá relatar um erro de operação ilegal, conforme mostrado no seguinte exemplo:


# dtrace -w -n syscall::open:entry'{chill(500000001)}'
dtrace: allowing destructive actions
dtrace: description 'syscall::open:entry' matched 1 probe
dtrace: 57 errors
CPU     ID                    FUNCTION:NAME
dtrace: error on enabled probe ID 1 (ID 14: syscall::open:entry): \
  illegal operation in action #1

Esse limite é aplicado mesmo que o tempo seja distribuído entre várias chamadas para chill() ou vários consumidores de DTrace de um único teste. Por exemplo, o mesmo erro seria gerado pelo seguinte comando:


# dtrace -w -n syscall::open:entry'{chill(250000000); chill(250000001);}'