Compartilhar via


ETW DTrace

Use o DTrace para Windows para processar eventos ETW existentes e adicionar novos eventos ETW.

O ETW (Rastreamento de Eventos para Windows) é um recurso de rastreamento no nível do kernel que permite registrar eventos definidos por kernel ou aplicativo em um arquivo de log. Você pode consumir os eventos em tempo real ou em um arquivo de log e usá-los para depurar um aplicativo ou determinar onde os problemas de desempenho estão ocorrendo no aplicativo. Para obter informações gerais sobre o ETW, consulte Sobre o Rastreamento de Eventos.

Observação

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

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

Provedor de DTrace do Windows ETW

Você pode usar o DTrace para capturar e relatar eventos ETW registrados e baseados em manifesto. Para sondar palavras-chave/níveis/eventIDs específicos, as sondagens ETW funcionarão de forma muito mais confiável se você não usar curingas. Em vez disso, especifique totalmente sua investigação com base nessas regras:

Probename = etw

Modname = GUID do provedor no formato xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, usando todos os caracteres minúsculos.

Funcname = Level_Keyword no formato 0x00_0x0000000000000000. Para corresponder a tudo, este valor deve ser ajustado para 0xff_0xffffffffffffffff.

Probename = ID de evento inteiro ou "generic_event" para corresponder a todas as IDs de evento.

A filtragem com base em Probename só funciona para eventos manifestos. Utilize coringa (*) para eventos rastreados.

O payload do ETW é acessado por meio do arg0. Isso é composto por nt'_EVENT_HEADER seguido pela data específica do evento.

Determinando provedores ETW disponíveis

Use o comando logman para exibir provedores ETW ativos e seus GUIDs de provedor.

C:\>logman query providers
...
Microsoft-Windows-Kernel-Memory {D1D93EF7-E1F2-4F45-9943-03D245FE6C00}
Microsoft-Windows-Kernel-Network {7DD42A49-5329-4832-8DFD-43D979153A88}
Microsoft-Windows-Kernel-PnP {9C205A39-1250-487D-ABD7-E831C6290539}
Microsoft-Windows-Kernel-Power {331C3B3A-2005-44C2-AC5E-77220C37D6B4}
Microsoft-Windows-Kernel-Prefetch {5322D61A-9EFA-4BC3-A3F9-14BE95C144F8}
Microsoft-Windows-Kernel-Process {22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}
...

Exibindo informações existentes do provedor ETW

O DTrace tem a capacidade de gerar eventos ETW. Isso é útil para cenários em que existe um pipeline ETW para relatar, coletar e analisar.

Use este exemplo de comando DTrace para reportar eventos do provedor Microsoft-Windows-Kernel-Memory.

C:\>dtrace -n "etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12"
dtrace: description 'etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12' matched 1 probe
CPU     ID                    FUNCTION:NAME
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12
  0   3271       0xff_0xffffffffffffffff:12

Adicionando novos eventos ETW

Eventos de rastreamento etw podem ser criados chamando a macro etw_trace. Os eventos só serão registrados se houver um ouvinte ativo para o provedor de rastreamento especificado, caso contrário, eles serão ignorados.

A macro etw_trace dá suporte a tipos de dados básicos como int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 e string. Consulte a tabela de tipos de dados ETW com suporte abaixo para obter mais detalhes.

Exemplo ETW_TRACE macro:

Este script gera um evento ETW personalizado quando a rotina de chamada de sistema retorna 0xc0000001 – STATUS_UNSUCCESSFUL.

Você pode alterar o this->status valor para usar valores NTSTATUS diferentes para registrar valores de retorno de syscall diferentes.

syscall:::return 
{ 
	this->status = (uint32_t) arg0;

	if (this->status == 0xc0000001UL) 
	{ 
		etw_trace
		(
    		"Tools.DTrace.Platform", /* Provider Name */
   	 		"AAD330CC-4BB9-588A-B252-08276853AF02", /* Provider GUID */
    		"My custom event from DTrace", /* Event Name */
    		1, /* Event Level (0 - 5) */
    		0x0000000000000020, /* Flag */
    		"etw_int32", /* Field_1 Name */
    		"PID",/* Field_1 Type */
     		(int32_t)pid, /* Field_1 Value  */
     		"etw_string", /* Field_2 Name */
     		"Execname", /* Field_2 type */
      		execname, /* Field_2 Value */
     		"etw_string", /* Field_3 Name */
     		"Probefunc", /* Field_3 type */
      		probefunc /* Field_3 Value */   
			);
	}
}
C:\> dtrace -s addnewetwevent.d
dtrace: script 'addnewetwevent.d' matched 1881 probes
CPU     ID                    FUNCTION:NAME
  0     93 NtAlpcSendWaitReceivePort:return
  0     93 NtAlpcSendWaitReceivePort:return
  0     93 NtAlpcSendWaitReceivePort:return

Código de exemplo ETW NUMA MEM STATS

Este script de exemplo usa o provedor ETWKernel-Memory Microsoft-Windows para despejar memória de nó NUMA. O tamanho da página pode ser convertido em tamanho em KB multiplicando por 4. Para obter informações gerais sobre NUMA, consulte o suporte a NUMA.

Esse código também está localizado em https://github.com/microsoft/DTrace-on-Windows/blob/windows/samples/windows/etw/numamemstats.d

typedef struct KernelMemInfoEvent
{
        struct nt`_EVENT_HEADER _EH;
	uint32_t PartitionId;
	uint32_t Count;
	uint32_t NodeNumber;
}kmi;

typedef struct MemoryNodeInfo
{
	uint64_t TotalPageCount;
	uint64_t SmallFreePageCount;
	uint64_t SmallZeroPageCount;
	uint64_t MediumFreePageCount;
	uint64_t MediumZeroPageCount;
	uint64_t LargeFreePageCount;
	uint64_t LargeZeroPageCount;
	uint64_t HugeFreePageCount;
	uint64_t HugeZeroPageCount;
}m_nodeinfo;

int printcounter;

BEGIN
{
	printcounter = 0;
}

/* MemNodeInfo */
etw:d1d93ef7-e1f2-4f45-9943-03d245fe6c00:0xff_0xffffffffffffffff:12
{
	if (printcounter%10 == 0)
	{
		printf ("\n \n");
		printf("Partition ID: %d \n",((kmi *)arg0)->PartitionId);
		printf("Count: %d \n", ((kmi *)arg0)->Count);
		
		printf("Node number: %d\n", ((kmi *)arg0)->NodeNumber);
		counters = (m_nodeinfo*)(arg0 + sizeof(struct nt`_EVENT_HEADER) + 12);
		print(*counters);

		/* Dump rest of the NUMA node info */

		if (((kmi *)arg0)->Count > 1)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(1)) + (sizeof(uint32_t)*(1)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(1)) + (sizeof(uint32_t)*(1)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 2)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(2)) + (sizeof(uint32_t)*(2)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(2)) + (sizeof(uint32_t)*(2)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 3)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(3)) + (sizeof(uint32_t)*(3)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(3)) + (sizeof(uint32_t)*(3)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 4)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(4)) + (sizeof(uint32_t)*(4)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(4)) + (sizeof(uint32_t)*(4)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 5)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(5)) + (sizeof(uint32_t)*(5)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(5)) + (sizeof(uint32_t)*(5)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 6)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(6)) + (sizeof(uint32_t)*(6)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(6)) + (sizeof(uint32_t)*(6)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 7)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(7)) + (sizeof(uint32_t)*(7)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(7)) + (sizeof(uint32_t)*(7)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 8)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(8)) + (sizeof(uint32_t)*(8)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(8)) + (sizeof(uint32_t)*(8)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 9)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(9)) + (sizeof(uint32_t)*(9)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(9)) + (sizeof(uint32_t)*(9)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 10)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(10)) + (sizeof(uint32_t)*(10)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(10)) + (sizeof(uint32_t)*(10)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 11)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(11)) + (sizeof(uint32_t)*(11)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(11)) + (sizeof(uint32_t)*(11)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 12)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(12)) + (sizeof(uint32_t)*(12)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(12)) + (sizeof(uint32_t)*(12)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 13)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(13)) + (sizeof(uint32_t)*(13)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(13)) + (sizeof(uint32_t)*(13)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 14)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(14)) + (sizeof(uint32_t)*(14)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(14)) + (sizeof(uint32_t)*(14)) + sizeof(uint32_t));
			print(*counters);
		}
		if (((kmi *)arg0)->Count > 15)
		{
			nodenumber = (uint32_t *) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(15)) + (sizeof(uint32_t)*(15)) );
			printf ("Node Number: %d \n", *nodenumber);
			counters = (m_nodeinfo*) (arg0 + sizeof(struct nt`_EVENT_HEADER) + 8 + (sizeof(m_nodeinfo)*(15)) + (sizeof(uint32_t)*(15)) + sizeof(uint32_t));
			print(*counters);
		}

	}
	exit(1);
	printcounter++;
}

Salvar o arquivo como etwnumamemstats.d

Abra um prompt de comando como Administrador e execute o script usando a opção -s.

Quando executado em um PC com Windows no modo cliente, um único nó NUMA é exibido.

C:\> dtrace -s etwnumamemstats.d
trace: script 'etwnumamemstats.d' matched 36 probes
CPU     ID                    FUNCTION:NAME
  0  42735       0xff_0xffffffffffffffff:12

Partition ID: 0
Count: 1
Node number: 1
m_nodeinfo {
    uint64_t TotalPageCount = 0xab98d
    uint64_t SmallFreePageCount = 0
    uint64_t SmallZeroPageCount = 0x1bec
    uint64_t MediumFreePageCount = 0
    uint64_t MediumZeroPageCount = 0x5a
    uint64_t LargeFreePageCount = 0
    uint64_t LargeZeroPageCount = 0
    uint64_t HugeFreePageCount = 0
    uint64_t HugeZeroPageCount = 0
}
  0  42735       0xff_0xffffffffffffffff:12

Tipos de dados ETW com suporte

Tipo de ETW Tipo de dado da linguagem D Observações
etw_struct Número Inteiro O valor da carga útil desse tipo representa a contagem de membros que uma nova estrutura terá.
etw_string cadeia Não aplicável
etw_mbcsstring cadeia Não aplicável
etw_int8 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int8_t' no script D é aconselhada
etw_uint8 Número Inteiro O tamanho do tipo deve ser compatível, e a conversão para 'uint8_t' no script D é aconselhável.
etw_int16 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int16_t' no script D é aconselhada
etw_uint16 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'uint16_t' no script D é aconselhável
etw_int32 Número Inteiro Não aplicável
etw_uint32 Número Inteiro Não aplicável
etw_int64 Número Inteiro O tipo deve ser explicitamente 'int64_t', pois D usa como padrão 'int32_t'
etw_uint64 Número Inteiro O tipo deve ser explicitamente 'int64_t', pois D usa como padrão 'int32_t'
etw_float Escalar Constantes de ponto flutuante não são permitidas no script D, mas permitem isso em símbolos carregados
etw_double Escalar Constantes de ponto flutuante não são permitidas no script D, mas permitem isso em símbolos carregados
etw_bool32 Número Inteiro Não aplicável
etw_hexint32 Número Inteiro Não aplicável
etw_hexint64 Número Inteiro O tipo deve ser explicitamente 'int64_t', pois D usa como padrão 'int32_t'
etw_countedmbcsstring Número Inteiro Não aplicável
etw_intptr Número Inteiro O tamanho do tipo de dados é alterado de acordo com a arquitetura ('int32_t' versus 'int64_t')
etw_uintptr Número Inteiro O tamanho do tipo de dados é alterado de acordo com a arquitetura ('int32_t' versus 'int64_t')
etw_pointer Número Inteiro O tamanho do tipo de dados é alterado de acordo com a arquitetura ('int32_t' versus 'int64_t')
etw_char16 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int16_t' no script D é aconselhada
etw_char8 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int8_t' no script D é aconselhada
etw_bool8 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int8_t' no script D é aconselhada
etw_hexint8 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int8_t' no script D é aconselhada
etw_hexint16 Número Inteiro O tamanho do tipo deve corresponder e a conversão para 'int16_t' no script D é aconselhada
etw_pid Número Inteiro Não aplicável
etw_tid Número Inteiro Não aplicável
etw_mbcsxml Número Inteiro Não aplicável
etw_mbcsjson Número Inteiro Não aplicável
etw_countedmbcsxml Número Inteiro Não aplicável
etw_countedmbcsjson Número Inteiro Não aplicável
etw_win32error Número Inteiro Não aplicável
etw_ntstatus Número Inteiro Não aplicável
etw_hresult Número Inteiro Não aplicável

Consulte Também

DTrace no Windows

Programação do Windows DTrace

Exemplos de código do Windows DTrace

Despejo ao vivo do DTrace