ETW DTrace
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 o kernel ou os eventos definidos pelo aplicativo em um arquivo de log. Você pode consumir os eventos em tempo real ou por meio 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 tem suporte nos builds do Insider do Windows após a versão 18980 e a compilação 18975 do Windows Server.
Para obter informações gerais sobre como trabalhar com o DTrace no Windows, consulte DTrace.
Provedor de DTrace do ETW do Windows
Você pode usar o DTrace para capturar e relatar o rastreamento registrado e o manifesto com base em eventos de ETW. Para investigar palavras-chave/níveis/eventIDs específicos, os testes de ETW funcionarão de forma muito mais confiável se você não usar curingas. Em vez disso, especifique totalmente a investigação com base nestas regras:
Probename = etw
Modname = GUID do provedor no formato xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, com todas as letras minúsculas.
Funcname = Level_Keyword no formato 0x00_0x0000000000000000. Para corresponder a tudo, isso deverá 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. Use o curinga (*) para eventos tracelogged.
A carga do ETW é acessada via arg0. Isso é composto por nt'_EVENT_HEADER, seguido da data específica do evento.
Determinação dos provedores de ETW disponíveis
Use o comando logman para exibir provedores de 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}
...
Exibição de informações existentes do provedor de ETW
O DTrace tem a capacidade de gerar eventos de ETW. Isso será útil para cenários em que há um pipeline de ETW existente para relatar, coletar e analisar.
Use este comando DTrace de exemplo para relatar 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
Adição de novos eventos de ETW
Os eventos de rastreamento de 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 oferece suporte a tipos de dado básicos como int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 e cadeia de caracteres. Consulte a tabela TIpos de dado ETW suportados abaixo para obter mais informações.
Exemplo de macro ETW_TRACE:
Esse script gera um evento ETW personalizado quando a rotina syscall retorna 0xc0000001 - STATUS_UNSUCCESSFUL.
É possível alterar o valor this->status
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 de ETW Microsoft-Windows-Kernel-Memory para despejar 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 para NUMA.
O 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.
Em execução em um PC cliente Windows, 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 dado de ETW com suporte
Tipo ETW | Tipo de dados de linguagem D | Observações |
---|---|---|
etw_struct | Inteiro | O valor de carga desse tipo representa a contagem de membros que uma nova estrutura terá. |
etw_string | string | N/D |
etw_mbcsstring | string | N/D |
etw_int8 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int8_t" no script D é recomendável |
etw_uint8 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "uint8_t" no script D é recomendável |
etw_int16 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int16_t" no script D é recomendável |
etw_uint16 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "uint16_t" no script D é recomendável |
etw_int32 | Inteiro | N/D |
etw_uint32 | Inteiro | N/D |
etw_int64 | Inteiro | O tipo deve ser explicitamente "int64_t", já que o padrão D é "int32_t" |
etw_uint64 | Inteiro | O tipo deve ser explicitamente "int64_t", já que o padrão D é "int32_t" |
etw_float | Scalar | Constantes de ponto flutuante não são permitidas no script D, mas permitem isso em símbolos carregados |
etw_double | Scalar | Constantes de ponto flutuante não são permitidas no script D, mas permitem isso em símbolos carregados |
etw_bool32 | Inteiro | N/D |
etw_hexint32 | Inteiro | N/D |
etw_hexint64 | Inteiro | O tipo deve ser explicitamente "int64_t", já que o padrão D é "int32_t" |
etw_countedmbcsstring | Inteiro | N/D |
etw_intptr | Inteiro | O tamanho do tipo de dados muda de acordo com a arquitetura ("int32_t" versus "int64_t") |
etw_uintptr | Inteiro | O tamanho do tipo de dados muda de acordo com a arquitetura ("int32_t" versus "int64_t") |
etw_pointer | Inteiro | O tamanho do tipo de dados muda de acordo com a arquitetura ("int32_t" versus "int64_t") |
etw_char16 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int16_t" no script D é recomendável |
etw_char8 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int8_t" no script D é recomendável |
etw_bool8 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int8_t" no script D é recomendável |
etw_hexint8 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int8_t" no script D é recomendável |
etw_hexint16 | Inteiro | O tamanho do tipo deve corresponder, e a conversão para "int16_t" no script D é recomendável |
etw_pid | Inteiro | N/D |
etw_tid | Inteiro | N/D |
etw_mbcsxml | Inteiro | N/D |
etw_mbcsjson | Inteiro | N/D |
etw_countedmbcsxml | Inteiro | N/D |
etw_countedmbcsjson | Inteiro | N/D |
etw_win32error | Inteiro | N/D |
etw_ntstatus | Inteiro | N/D |
etw_hresult | Inteiro | N/D |