Partilhar via


DTrace ETW

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

O Rastreamento de Eventos para Windows (ETW) é um recurso de rastreamento no nível do kernel que permite registrar eventos definidos pelo kernel ou pelo aplicativo em um arquivo de log. Você pode consumir os eventos em tempo real ou a partir de um arquivo de log e usá-los para depurar um aplicativo ou para 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 é suportado nas builds do programa Insider do Windows após a versão 18980 e na build do Windows Server 18975.

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

Provedor DTrace do Windows ETW

Você pode usar o DTrace para capturar e relatar eventos ETW registrados por rastreamento e baseados em manifesto. Para investigar palavras-chave/níveis/eventIDs específicos, as sondas ETW funcionarão de forma muito mais confiável se você não usar curingas. Em vez disso, especifique completamente sua sonda com base nestas regras:

Nome da sonda = etw

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

Funcname = Level_Keyword da forma 0x00_0x0000000000000000. Para corresponder a tudo, isso deve ser definido como 0xff_0xffffffffffffffff.

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

A filtragem baseada em Probename só funciona para eventos manifestados. Utilize o curinga (*) para eventos registados.

A carga útil ETW é acessada via arg0. Este é composto por nt'_EVENT_HEADER seguido da data específica do evento.

Determinando provedores de ETW disponíveis

Use o comando logman para mostrar 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 produzir eventos ETW. Isso é útil para cenários em que há pipeline ETW existente para relatar, coletar e analisar.

Use este exemplo de comando DTrace para relatar eventos do provedor Microsoft-WindowsKernel-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

Os 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 suporta tipos de dados básicos como int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 e string. Consulte a tabela Tipos de dados ETW suportados abaixo para obter mais detalhes.

Exemplo ETW_TRACE macro:

Esse script gera um evento ETW personalizado quando a rotina syscall retorna 0xc0000001 - STATUS_UNSUCCESSFUL.

Você pode alterar o this->status valor para usar valores NTSTATUS diferentes para registrar valores de retorno 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 ETW Microsoft-Windows-Kernel-Memory para despejar a memória do 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 Suporte NUMA.

Este 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++;
}

Salve o arquivo como etwnumamemstats.d

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

Executado num PC cliente Windows, é exibido um único nó NUMA.

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 suportados

Tipo ETW D Tipo de dados de idioma Observações
etw_struct Número inteiro O valor da carga útil deste tipo representa a contagem de membros que uma nova estrutura terá.
etw_string cadeia (de caracteres) N/A
etw_mbcsstring cadeia (de caracteres) N/A
etw_int8 Número inteiro O tamanho do tipo deve corresponder, e recomenda-se converter para `int8_t` no script D.
etw_uint8 Número inteiro O tamanho do tipo deve ser correspondente e é aconselhável converter para `uint8_t` no script D.
etw_int16 Número inteiro O tamanho do tipo deve corresponder e é aconselhável transmitir para 'int16_t' no script D
etw_uint16 Número inteiro O tamanho do tipo deve corresponder, e é aconselhável converter para `uint16_t` no script D.
etw_int32 Número inteiro N/A
etw_uint32 Número inteiro N/A
etw_int64 Número inteiro O tipo deve ser explicitamente 'int64_t', uma vez que D assume como padrão 'int32_t'
etw_uint64 Número inteiro O tipo deve ser explicitamente 'int64_t', uma vez que D assume 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/A
etw_hexint32 Número inteiro N/A
etw_hexint64 Número inteiro O tipo deve ser explicitamente 'int64_t', uma vez que D assume como padrão 'int32_t'
etw_countedmbcsstring Número inteiro N/A
etw_intptr Número inteiro O tamanho do tipo de dados muda de acordo com a arquitetura ('int32_t' vs 'int64_t')
etw_uintptr Número inteiro O tamanho do tipo de dados muda de acordo com a arquitetura ('int32_t' vs 'int64_t')
etw_pointer Número inteiro O tamanho do tipo de dados muda de acordo com a arquitetura ('int32_t' vs 'int64_t')
etw_char16 Número inteiro O tamanho do tipo deve corresponder, e é aconselhável converter para 'int16_t' no script em D
etw_char8 Número inteiro O tamanho do tipo deve corresponder e é aconselhável transmitir para 'int8_t' no script D
etw_bool8 Número inteiro O tamanho do tipo deve corresponder e é aconselhável converter para «int8_t» no script D.
etw_hexint8 Número inteiro O tamanho do tipo deve corresponder, e recomenda-se converter para `int8_t` no script D.
etw_hexint16 Número inteiro O tamanho do tipo deve corresponder, e é aconselhável converter para 'int16_t' no script D
etw_pid Número inteiro N/A
etw_tid Número inteiro N/A
etw_mbcsxml Número inteiro N/A
etw_mbcsjson Número inteiro N/A
etw_countedmbcsxml Número inteiro N/A
etw_countedmbcsjson Número inteiro N/A
etw_win32error Número inteiro N/A
etw_ntstatus Número inteiro N/A
etw_hresult Número inteiro N/A

Ver também

DTrace no Windows

Programação do DTrace para Windows

Exemplos de código do Windows DTrace

Despejo ao vivo do DTrace