ETW DTrace
Use DTrace para Windows para procesar eventos ETW existentes y para agregar nuevos eventos ETW.
Seguimiento de eventos para Windows (ETW) es una utilidad de seguimiento de nivel de kernel que le permite registrar eventos definidos por la aplicación o el kernel en un archivo de registro. Puede consumir los eventos en tiempo real o desde un archivo de registro y usarlos para depurar una aplicación o para determinar dónde se producen los problemas de rendimiento en la aplicación. Para obtener información general sobre ETW, consulte Acerca del seguimiento de eventos.
Nota:
DTrace se admite en las compilaciones de Insider de Windows después de la versión 18980 y Windows Server Build 18975.
Para obtener información general sobre cómo trabajar con DTrace en Windows, consulte DTrace.
Proveedor de DTrace de ETW para Windows
Puede usar DTrace para capturar y notificar eventos de ETW registrados y basados en manifiestos. Para sondear palabras clave, niveles o eventID específicos, los sondeos de ETW funcionarán de forma mucho más confiable si no usa caracteres comodín. En su lugar, especifique completamente el sondeo en función de estas reglas:
Probename = etw
Modname = Guid del proveedor con el formato xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, con todos los caracteres en minúsculas.
Funcname = Level_Keyword del formulario 0x00_0x0000000000000000. Para que coincida con todo, debe establecerse en 0xff_0xffffffffffffffff.
Probename = ID de evento de entero o "generic_event" para que coincida con todos los identificadores de evento.
El filtrado basado en Probename solo funciona para eventos con manifiestos. Use comodín (*) para eventos con registro de seguimiento.
Se accede a la carga ETW a través de arg0. Esto se compone de nt`_EVENT_HEADER seguido de la fecha específica del evento.
Determinación de los proveedores de ETW disponibles
Use el comando logman para mostrar los proveedores de ETW activos y sus GUID de proveedor.
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}
...
Visualización de la información del proveedor de ETW existente
DTrace tiene la capacidad de generar eventos de ETW. Esto resulta útil para escenarios en los que existe una canalización de ETW para informar, recopilar y analizar.
Use este comando DTrace de ejemplo para notificar eventos del proveedor 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
Adición de nuevos eventos de ETW
Los eventos de seguimiento de ETW se pueden crear llamando a la macro etw_trace. Los eventos solo se registrarán si hay un agente de escucha activo para el proveedor de seguimiento especificado; de lo contrario, se omitirán.
La macro etw_trace admite tipos de datos básicos, como int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 y string. Consulte la Tabla de tipos de datos de ETW admitidos a continuación para obtener más detalles.
Macro de ETW_TRACE de ejemplo:
Este script genera un evento de ETW personalizado cuando la rutina syscall devuelve 0xc0000001: STATUS_UNSUCCESSFUL.
Puede cambiar el valor this->status
para usar otros valores NTSTATUS para registrar valores de devolución 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 ejemplo de ETW NUMA MEM STATS
En este script de ejemplo se usa el proveedor de ETW Microsoft-Windows-Kernel-Memory para volcar la memoria del nodo NUMA. El tamaño de página se puede convertir al tamaño en KB multiplicando por 4. Para obtener información general sobre NUMA, consulte Compatibilidad con NUMA.
Este código también se encuentra en 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++;
}
Guardar el archivo como etwnumamemstats.d
Abra un símbolo del sistema como administrador y ejecute el script con la opción -s option.
Al ejecutarlo en un equipo Windows cliente, se muestra un único nodo 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 datos de ETW admitidos
Tipo ETW | Tipo de datos de lenguaje D | Notas |
---|---|---|
etw_struct | Entero | El valor de carga de este tipo representa el recuento de miembros que tendrá una nueva estructura. |
etw_string | string | N/D |
etw_mbcsstring | string | N/D |
etw_int8 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_uint8 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a `uint8_t` en el script D. |
etw_int16 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_uint16 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a `uint16_t` en el script D. |
etw_int32 | Entero | N/D |
etw_uint32 | Entero | N/D |
etw_int64 | Entero | El tipo debe ser explícitamente `int64_t`, ya que D tiene como valor predeterminado `int32_t`. |
etw_uint64 | Entero | El tipo debe ser explícitamente `int64_t`, ya que D tiene como valor predeterminado `int32_t`. |
etw_float | Scalar | No se permiten constantes de punto flotante en el script D, pero sí en símbolos cargados. |
etw_double | Scalar | No se permiten constantes de punto flotante en el script D, pero sí en símbolos cargados. |
etw_bool32 | Entero | N/D |
etw_hexint32 | Entero | N/D |
etw_hexint64 | Entero | El tipo debe ser explícitamente `int64_t`, ya que D tiene como valor predeterminado `int32_t`. |
etw_countedmbcsstring | Entero | N/D |
etw_intptr | Entero | El tamaño del tipo de datos cambia según la arquitectura (`int32_t` frente a `int64_t`) |
etw_uintptr | Entero | El tamaño del tipo de datos cambia según la arquitectura (`int32_t` frente a `int64_t`) |
etw_pointer | Entero | El tamaño del tipo de datos cambia según la arquitectura (`int32_t` frente a `int64_t`) |
etw_char16 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_char8 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_bool8 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_hexint8 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_hexint16 | Entero | El tamaño de tipo debe coincidir y se recomienda convertir a "int8_t" en el script D. |
etw_pid | Entero | N/D |
etw_tid | Entero | N/D |
etw_mbcsxml | Entero | N/D |
etw_mbcsjson | Entero | N/D |
etw_countedmbcsxml | Entero | N/D |
etw_countedmbcsjson | Entero | N/D |
etw_win32error | Entero | N/D |
etw_ntstatus | Entero | N/D |
etw_hresult | Entero | N/D |
Consulte también
Programación de Windows DTrace