Compartilhar via


Exemplos de código do DTrace

O DTrace dá suporte à linguagem de programação D. Este tópico fornece exemplos de código D.

Para obter informações gerais sobre o DTrace no Windows, consulte DTrace.

Para obter informações detalhadas sobre o DTrace, consulte a Especificação do OpenDTrace versão 1.0 na Universidade de Cambridge.

Observação

O DTrace tem suporte nos builds do Insider do Windows após a versão 18980 e o Windows Server Build 18975.

Scripts de exemplo adicionais

Scripts D adicionais aplicáveis a cenários do Windows estão disponíveis no diretório de exemplos do código-fonte DTrace.

https://github.com/microsoft/DTrace-on-Windows/tree/windows/samples/windows

Um conjunto de scripts úteis do kit de ferramentas opentrace está disponível em https://github.com/opendtrace/toolkit.

Uso de disco por nome

Esse script fornece os contadores de disco para um determinado nome executável. O nome executável diferencia maiúsculas de minúsculas e é fornecido na linha de comando quando o script é iniciado.


#pragma D option quiet
#pragma D option destructive


intptr_t curptr;
struct nt`_EPROCESS *eprocess_ptr;
int found;
int firstpass;
BEGIN
{
	curptr = (intptr_t) ((struct nt`_LIST_ENTRY *) (void*)&nt`PsActiveProcessHead)->Flink;	
	found = 0;
	firstpass = 1;
	bytesread = 0;
	byteswrite = 0;
	readcount = 0;
	writecount = 0;
	flushcount = 0;
}

tick-1ms

/found == 0/

{
/* use this for pointer parsing */
	if (found == 0)
	{
		eprocess_ptr = (struct nt`_EPROCESS *)(curptr - offsetof(nt`_EPROCESS, ActiveProcessLinks));
		processid = (string) eprocess_ptr->ImageFileName;

		if ($1 == processid)
		{
			found = 1;
		}

		else 
		{
			curptr = (intptr_t) ((struct nt`_LIST_ENTRY *) (void*)curptr)->Flink;
		}
	}		
}

tick-2s

{
	system ("cls");
	if (found == 1)
	{
		if (firstpass)
		{
			firstpass = 0;
			bytesread = (unsigned long long) eprocess_ptr->DiskCounters->BytesRead;
			byteswrite = (unsigned long long) eprocess_ptr->DiskCounters->BytesWritten;
			readcount = eprocess_ptr->DiskCounters->ReadOperationCount;
			writecount = eprocess_ptr->DiskCounters->WriteOperationCount;
			flushcount =  eprocess_ptr->DiskCounters->FlushOperationCount;
		}

		else
		{
			bytesread = (unsigned long long)  (eprocess_ptr->DiskCounters->BytesRead - bytesread);
			byteswrite = (unsigned long long)  (eprocess_ptr->DiskCounters->BytesWritten - byteswrite);
			readcount = eprocess_ptr->DiskCounters->ReadOperationCount - readcount;
			writecount = eprocess_ptr->DiskCounters->WriteOperationCount - writecount;		
			flushcount = eprocess_ptr->DiskCounters->FlushOperationCount - flushcount;

			printf("*** Reports disk read/write every second *** \n");
			printf("Process name: %s\n", eprocess_ptr->ImageFileName);
			printf("Process Id: %d\n", (int) eprocess_ptr->UniqueProcessId);
			printf("Bytes Read %llu \n",  (unsigned long long) bytesread);
			printf("Bytes Written %llu \n", (unsigned long long) byteswrite);
			printf("Read Operation Count %d \n", readcount);
			printf("Write Operation Count %d \n",writecount);
			printf("Flush Operation Count %d \n", flushcount);

			bytesread = (unsigned long long) eprocess_ptr->DiskCounters->BytesRead;
			byteswrite = (unsigned long long) eprocess_ptr->DiskCounters->BytesWritten;
			readcount = eprocess_ptr->DiskCounters->ReadOperationCount;
			writecount = eprocess_ptr->DiskCounters->WriteOperationCount;
			flushcount =  eprocess_ptr->DiskCounters->FlushOperationCount;		
		}		
	}

	else
	{
		printf("No matching process found for %s \n", $1);
		exit(0);
	}
}

Salve o arquivo como diskuagebyname.d e use a opção -s para executar o script de teste. Forneça um nome que diferencia maiúsculas de minúsculas do exe desejado, como Notepad.exe


C:\>dtrace -s diskusagebyname.d Notepad.exe

*** Reports disk read/write every second *** 
Process name: Notepad.exe
Process Id: 6012
Bytes Read 0 
Bytes Written 0 
Read Operation Count 0 
Write Operation Count 0 
Flush Operation Count 0 
*** Reports disk read/write every second *** 
Process name: cmd.exe
Process Id: 4428
Bytes Read 18446744073709480960 
Bytes Written 18446744073709522944 
Read Operation Count -5 
Write Operation Count -7 
Flush Operation Count 0 

Para manter um log em execução da atividade de disco, redirecione o comando para um arquivo de texto, conforme mostrado aqui.

C:\>dtrace -s diskusagebyname.d Notepad.exe>>diskstats.txt

Despejo de Uso de Memória

Em alguns casos de diagnóstico, é necessário despejar estruturas de kernel para entender a situação. Esse código mostra um exemplo para despejar o uso da memória do sistema. O script D abaixo dispara uma sonda de temporizador a cada 3 segundos e atualiza o uso de memória do sistema.

#pragma D option quiet
#pragma D option destructive

tick-3s
{
	system ("cls");

	this->pp = ((struct nt`_MI_PARTITION *) &nt`MiSystemPartition);

	printf("***** Printing System wide page information ******* \n");
	printf( "Total Pages Entire Node: %u MB \n", this->pp->Core.NodeInformation->TotalPagesEntireNode*4096/(1024*1024));
	printf("Total Available Pages: %u Mb \n", this->pp->Vp.AvailablePages*4096/(1024*1024));
	printf("Total ResAvail Pages: %u  Mb \n",  this->pp->Vp.ResidentAvailablePages*4096/(1024*1024));
	printf("Total Shared Commit: %u  Mb \n",  this->pp->Vp.SharedCommit*4096/(1024*1024));
	printf("Total Pages for PagingFile: %u  Mb \n",  this->pp->Vp.TotalPagesForPagingFile*4096/(1024*1024));
	printf("Modified Pages : %u  Mb \n",  this->pp->Vp.ModifiedPageListHead.Total*4096/(1024*1024));
	printf("Modified No Write Page Count : %u  Mb \n",  this->pp->Vp.ModifiedNoWritePageListHead.Total*4096/(1024*1024));
	printf("Bad Page Count : %d  Mb \n",  this->pp->PageLists.BadPageListHead.Total*4096/(1024*1024));
	printf("Zero Page Count : %d  Mb \n",  this->pp->PageLists.ZeroedPageListHead.Total*4096/(1024*1024));
	printf("Free Page Count : %d  Mb \n",  this->pp->PageLists.FreePageListHead.Total*4096/(1024*1024));


/********** Printing Commit info ******************/ 
	
	this->commit = ((struct nt`_MI_PARTITION_COMMIT ) ((struct nt`_MI_PARTITION *) &nt`MiSystemPartition)->Commit);

	printf("***** Printing Commit Info ******* \n");
	printf("Total Committed Pages: %u  Mb \n", this->pp->Vp.TotalCommittedPages*4096/(1024*1024));
	printf("Total Commit limit: %u  Mb  \n", this->pp->Vp.TotalCommitLimit*4096/(1024*1024));
	printf("Peak Commitment: %u Mb \n", this->commit.PeakCommitment*4096/(1024*1024));
	printf("Low Commit Threshold: %u Mb \n", this->commit.LowCommitThreshold*4096/(1024*1024));
	printf("High Commit Threshold: %u Mb \n", this->commit.HighCommitThreshold*4096/(1024*1024));
	printf("System Commit Reserve: %u Mb \n", this->commit.SystemCommitReserve*4096/(1024*1024));
	

}

Salve o arquivo como dumpmemoryusage.d.

Este exemplo usa símbolos, portanto, defina o caminho do símbolo conforme discutido na instalação.

set _NT_SYMBOL_PATH=srv*C:\symbols*https://msdl.microsoft.com/download/symbols 

Use a opção -s para executar o script de teste.

C:\>dtrace -s dumpmemoryusage.d
***** Printing System wide page information ******* 
Total Pages Entire Node: 2823 MB 
Total Available Pages: 622 Mb 
Total ResAvail Pages: 2369  Mb 
Total Shared Commit: 166  Mb 
Total Pages for PagingFile: 30  Mb 
Modified Pages : 30  Mb 
Modified No Write Page Count : 0  Mb 
Bad Page Count : 0  Mb 
Zero Page Count : 7  Mb 
Free Page Count : 0  Mb 
***** Printing Commit Info ******* 
Total Committed Pages: 2401  Mb 
Total Commit limit: 4551  Mb  
Peak Commitment: 2989 Mb 
Low Commit Threshold: 3413 Mb 
High Commit Threshold: 4295 Mb 
System Commit Reserve: 5 Mb 

Este programa foi projetado para continuar monitorando o uso de memória. Pressione CTRL+C para sair.

Identificando a divisão de tempo livre do heap para uma atividade

Este exemplo fornece um detalhamento da função RtlFreeHeap em sub-funções e exibe o tempo máximo necessário para que essas funções executem.

/* Mark script destructive as we call "cls" in the tick-1sec provider*/
#pragma D option destructive

/* Program to calculate time taken in RtlFreeHeap function. Hit Ctrl-C to break. */ 

dtrace:::BEGIN 
{
	counter1 = 0;

}

tick-1sec
{
	system ("cls");
	printf("count of function hit = %d << Press Ctrl-C to exit >> \n", counter1);

}

/* Instrument entry of RtlFreeHeap*/

pid$1:ntdll:RtlFreeHeap:entry
{ 
	/* self is thread local */
	self->ts = timestamp;
	self->depth = 0;
	counter1++;
} 

pid$1:ntdll:RtlFreeHeap:return
/self->ts/
{
	this->ts = timestamp - self->ts;
	@disttime = quantize(this->ts); 
	@funcName[probemod,probefunc] = max(this->ts);	
	self->ts = 0;
}


pid$1:::entry
/ self->ts /
{
	self->functime[self->depth] = timestamp;
	self->depth++;
}

pid$1:::return
/ self->ts /
{
	if (self->depth > 0)
	{
		self->depth--;
		/* this is a local scope */
		this->ts = timestamp - self->functime[self->depth];
		@funcName[probemod,probefunc] = max(this->ts);
	}
}

END

{
	system ("cls");
	/* Print overall time distribution for RtlFreeHeap */
	printa (@disttime);
	/* Print top 15 functions along with average time spent executing them */
	trunc (@funcName, 15);
	
	printa( @funcName);
	
}

Salve o arquivo como heapfree.d.

Esse script usa uma ID de processo ou PID. Para testar o script, execute a lista de tarefas no prompt de comando e localize o PID desejado.

C:\>tasklist

Image Name                     PID Session Name        Session#    Mem Usage
========================= ======== ================ =========== ============
System Idle Process              0 Services                   0          8 K
System                           4 Services                   0        108 K
Registry                        72 Services                   0     35,576 K

...

Notepad.exe                   7944 31C5CE94259D4006           2     20,736 K

Especifique o PID como um parâmetro e use a opção -s para executar o script de teste, conforme mostrado.

C:\>dtrace -s heapfree.d 7944

count of function hit = 28 <<Press Ctrl-C to exit>>

Execute qualquer tarefa de interesse no programa associado ao PID, como, por exemplo, digitar no Bloco de Notas ou alterar a fonte.

Pressione CRTL-C e o uso do heap será exibido junto com informações de stack.

           value  ------------- Distribution ------------- count
            4096 |                                         0
            8192 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@        23
           16384 |@@@@@@                                   4
           32768 |                                         0
           65536 |                                         0
          131072 |@                                        1
          262144 |                                         0


  ntdll                                               RtlTryEnterCriticalSection                                     9444
  ntdll                                               RtlGetCurrentServiceSessionId                                 12302
  ntdll                                               RtlFreeHeap                                                  151354

Acompanhamento do pool de memória

Observação: execute este script com conjunto de variáveis aggsortkeypos. Essa variável informa o D-Trace para ordenar a saída com base no primeiro índice (tamanho).

Uso:

dtrace -s <script.d> <Time> <Tag> -x aggsortkey -x aggsortkeypos=1

Exemplo para monitorar vazamentos do KSec: dtrace -s PoolTrackingSummary.d "120s" 0x6365734b -x aggsortkey -x aggsortkeypos=1

O valor <Tag> é um valor ASCII codificado do identificador do pool de memória. Por exemplo, para codificar o valor da tag para o sistema de arquivos nt – NtFf, primeiro inverta os dois conjuntos de letras, resultando em fFtN. Converter os caracteres ASCII em hex será 46 66 74 4e ou 0x4666744e como um parâmetro para o nosso programa.

Outro exemplo seria a conversão de Ksec.

Primeiro inverter a ordem das letras: cesK

Em seguida, converta esses caracteres em hex, 63 65 73 4b ou 0x6365734b como um parâmetro para o nosso programa.

Observe que maiúsculas e minúsculas são importantes para corresponder à marca do pool.

Saída: o script é executado por 120 segundos e gera o resumo de alocação/liberação KSec. Você pode definir isso para qualquer hora necessária.

#pragma D option destructive
#pragma D option quiet
#pragma D option dynvarsize=240m 
#pragma D option bufsize=120m
#pragma D option aggsize=120m 


fbt:nt:ExAllocatePoolWithTag:entry
{	
    /* This is E100 in reserve. Convert ASCII to Hex => Hex('001E').*/
    if (arg2 == $2) 
    { 
		self->size = (unsigned int) arg1;
		@allocstack[stack(), self->size] = count();
    }
}

fbt:nt:ExAllocatePoolWithTag:return
/self->size/
{
    @mem[(uintptr_t) arg1] = sum(1);
    addr[(uintptr_t) arg1] = 1;
    /* printf("%Y: Execname %s allocated size %d bytes return ptr %x", walltimestamp, execname, (uint64_t) self->size, (uintptr_t) arg1 );*/

    size[(uintptr_t) arg1] = self->size;
    @sizealloc[self->size] = count();
    @delta[self->size] = sum(1);
    
    self->size = 0;
}

fbt:nt:ExFreePoolWithTag:entry
/addr[(uintptr_t) arg0]/
{
    @mem[(uintptr_t) arg0] = sum (-1);
    addr[(uintptr_t) arg0] -= 1;

    @sizefree[size[(uintptr_t) arg0]] = count();
    @delta[size[(uintptr_t) arg0]] = sum(-1);
}

tick-$1
{	
    exit(0);
}

END 
{

   printf("%10s %10s %10s %10s\n", "SIZE", "ALLOC", "FREE", "DELTA");
   printa("%10d %@10d %@10d %@10d\n", @sizealloc, @sizefree, @delta);

   printf("Printing stacks \n");
   printa (@allocstack);
    
   printf("== REPORT ==\n\n");
   printa("0x%x => %@u\n",@mem);
}

Salve o arquivo como pooltrackingsummary.d e use a opção -s para executar o script de teste, fornecendo um valor de marca e outros parâmetros discutidos acima.

A opção 120s, o programa é executado por dois minutos. Durante esse tempo, use o Windows para manipular o pool de memória monitorado. Por exemplo, carregue e descarregue um navegador da Web ou outros programas.

C:\>dtrace -s pooltrackingsummary.d "120s" 0x4666744e -x aggsortkey -x aggsortkeypos=1
      SIZE      ALLOC       FREE      DELTA
      1552         16          0         16
Printing stacks

              Ntfs.sys`NtfsCreateFcb+0x388
              Ntfs.sys`NtfsCreateNewFile+0xaa8
              Ntfs.sys`NtfsCommonCreate+0x2303
              Ntfs.sys`NtfsFsdCreate+0x284
              nt`IofCallDriver+0x55
              FLTMGR.SYS`FltpLegacyProcessingAfterPreCallbacksCompleted+0x1b9
              FLTMGR.SYS`FltpCreate+0x324
              nt`IofCallDriver+0x55
              nt`IoCallDriverWithTracing+0x34
              nt`IopParseDevice+0x6ac
              nt`ObpLookupObjectName+0x3fe
              nt`ObOpenObjectByNameEx+0x1fa
              nt`IopCreateFile+0x40f
              nt`NtCreateFile+0x79
              nt`KiSystemServiceCopyEnd+0x38
     1552                3

              Ntfs.sys`NtfsCreateFcb+0x388
              Ntfs.sys`NtfsOpenFile+0x2d7
              Ntfs.sys`NtfsCommonCreate+0x25a8
              Ntfs.sys`NtfsFsdCreate+0x284
              nt`IofCallDriver+0x55
              FLTMGR.SYS`FltpLegacyProcessingAfterPreCallbacksCompleted+0x1b9
              FLTMGR.SYS`FltpCreate+0x324
              nt`IofCallDriver+0x55
              nt`IoCallDriverWithTracing+0x34
              nt`IopParseDevice+0x6ac
              nt`ObpLookupObjectName+0x3fe
              nt`ObOpenObjectByNameEx+0x1fa
              nt`IopCreateFile+0x40f
              nt`NtCreateFile+0x79
              nt`KiSystemServiceCopyEnd+0x38
     1552                4

              Ntfs.sys`NtfsCreateFcb+0x388
              Ntfs.sys`NtfsOpenFile+0x2d7
              Ntfs.sys`NtfsCommonCreate+0x25a8
              Ntfs.sys`NtfsFsdCreate+0x284
              nt`IofCallDriver+0x55
              FLTMGR.SYS`FltpLegacyProcessingAfterPreCallbacksCompleted+0x1b9
              FLTMGR.SYS`FltpCreate+0x324
              nt`IofCallDriver+0x55
              nt`IoCallDriverWithTracing+0x34
              nt`IopParseDevice+0x6ac
              nt`ObpLookupObjectName+0x3fe
              nt`ObOpenObjectByNameEx+0x1fa
              nt`IopCreateFile+0x40f
              nt`NtOpenFile+0x58
              nt`KiSystemServiceCopyEnd+0x38
     1552                3

...

== REPORT ==

0xffffd304c98dd380 => 1
0xffffd304cc4655a0 => 1
0xffffd304cccf15a0 => 1
0xffffd304ccdeb990 => 1
0xffffd304ce048760 => 1
0xffffd304cf1ee990 => 1
0xffffd304d0473010 => 1
0xffffd304d12075a0 => 1
0xffffd304d14135a0 => 1
0xffffd304d1674010 => 1
0xffffd304d33b3660 => 1
0xffffd304d3b29010 => 1
0xffffd304d42c6010 => 1
0xffffd304d48b2010 => 1
0xffffd304de1fa5f0 => 1
0xffffd304e1ad56a0 => 1

Comparando GUIDs

Embora o DTrace não dê suporte ao GUID nativamente, você pode criar struct conforme mostrado neste código de exemplo para trabalhar com eles. Aqui está um exemplo de como você pode comparar GUIDs.

nt`_GUID guidcmp;


/* Sleep After GUID: 29f6c1db-86da-48c5-9fdb-f2b67b1f44da */
dtrace:::BEGIN
{
    printf("Begin\n");
    guidcmp.Data1 = 0x29f6c1db;
    guidcmp.Data2 = 0x86da;
    guidcmp.Data3 = 0x48c5;
    guidcmp.Data4[0] = 0x9f;
    guidcmp.Data4[1]  = 0xdb;
    guidcmp.Data4[2]  = 0xf2;
    guidcmp.Data4[3]  = 0xb6;
    guidcmp.Data4[4]  = 0x7b;
    guidcmp.Data4[5]  = 0x1f;
    guidcmp.Data4[6]  = 0x44;
    guidcmp.Data4[7]  = 0xda;
}

pid$target:PowrProf:PowerReadACValueIndexEx:entry 
{ 
	cidstr = (nt`_GUID *) (copyin(arg2, sizeof(nt`_GUID))); 

	printf("\nPrinting GUID to compare\n");
	print(guidcmp);

	printf("\nPrinting GUID received \n");
	print(*cidstr);

	if ( 	(cidstr->Data1 == guidcmp.Data1) &&
		(cidstr->Data2 == guidcmp.Data2) &&
		(cidstr->Data3 == guidcmp.Data3) &&
		(cidstr->Data4[0] == guidcmp.Data4[0]) &&
		(cidstr->Data4[1] == guidcmp.Data4[1]) &&
		(cidstr->Data4[2] == guidcmp.Data4[2]) &&
		(cidstr->Data4[3] == guidcmp.Data4[3]) &&
		(cidstr->Data4[4] == guidcmp.Data4[4]) &&
		(cidstr->Data4[5] == guidcmp.Data4[5]) &&
		(cidstr->Data4[6] == guidcmp.Data4[6]) &&
		(cidstr->Data4[7] == guidcmp.Data4[7])	)
	{
		printf("GUID matched \n");
	}
	else
	{
		printf("No match");
	}
}

dtrace:::END
{
    printf("End\n");
}

Salve o arquivo como comparequid.d e use a opção -s para executar o script de teste, fornecendo o parâmetro mostrado abaixo.

C:\Windows\system32>dtrace -s compareguid.d -c "powercfg /qh scheme_current sub_sleep standbyidle"
dtrace: script 'compareguid.d' matched 9 probes
CPU     ID                    FUNCTION:NAME
  0      1                           :BEGIN Begin
 
Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e  (Balanced)
  GUID Alias: SCHEME_BALANCED
  Subgroup GUID: 238c9fa8-0aad-41ed-83f4-97be242c8f20  (Sleep)
    GUID Alias: SUB_SLEEP
    Power Setting GUID: 29f6c1db-86da-48c5-9fdb-f2b67b1f44da  (Sleep after)
      GUID Alias: STANDBYIDLE
      Minimum Possible Setting: 0x00000000
      Maximum Possible Setting: 0xffffffff
      Possible Settings increment: 0x00000001
      Possible Settings units: Seconds
    Current AC Power Setting Index: 0x00000708
   Current DC Power Setting Index: 0x00000384
 
dtrace: pid 7560 has exited
  0  42695    PowerReadACValueIndexEx:entry
Printing GUID to compare
struct _GUID {
    UInt32 Data1 = 0x29f6c1db
    UInt16 Data2 = 0x86da
    UInt16 Data3 = 0x48c5
    UInt8 [8] Data4 = [ 0x9f, 0xdb, 0xf2, 0xb6, 0x7b, 0x1f, 0x44, 0xda ]
}
Printing GUID received
struct _GUID {
    UInt32 Data1 = 0x29f6c1db
    UInt16 Data2 = 0x86da
    UInt16 Data3 = 0x48c5
    UInt8 [8] Data4 = [ 0x9f, 0xdb, 0xf2, 0xb6, 0x7b, 0x1f, 0x44, 0xda ]
}
  0  42695    PowerReadACValueIndexEx:entry GUID matched

Consulte Também

DTrace no Windows

Programação do Windows DTrace

DTrace ETW

Despejo ao vivo do DTrace