Compartir a través de


DTrace ETW

Use DTrace para Windows para procesar eventos ETW existentes y para agregar nuevos eventos ETW.

El seguimiento de eventos para Windows (ETW) es una instalación de seguimiento de nivel de kernel que permite registrar eventos definidos por el kernel o la aplicación 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, vea 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, vea DTrace.

Proveedor ETW de Windows DTrace

Puede usar DTrace para capturar y notificar eventos ETW registrados y basados en manifiestos. Para sondear palabras clave, niveles o eventID específicos, los sondeos 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, utilizando todos los caracteres hexadecimales en minúsculas.

Funcname = Level_Keyword en el formato 0x00_0x0000000000000000. Para que coincida con todo, esto debe establecerse en 0xff_0xffffffffffffffff.

Probename = ID de evento entero o "generic_event" para que coincida con todos los ID de eventos.

El filtrado basado en Probename solo funciona para eventos manifiestos. Use comodín (*) para eventos de seguimiento.

Se accede a la carga útil 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 ETW disponibles

Use el comando logman para mostrar los proveedores 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 ETW existente

DTrace tiene la capacidad de generar eventos ETW. Esto resulta útil para escenarios en los que existe una canalización ETW para informar, recopilar y analizar.

Use este comando DTrace de ejemplo para informar sobre los 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 ETW

Los eventos de seguimiento 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 Tipos de datos ETW admitidos a continuación para obtener más detalles.

Macro de ETW_TRACE de ejemplo:

Este script genera un evento ETW personalizado cuando la rutina syscall devuelve 0xc0000001: STATUS_UNSUCCESSFUL.

Puede cambiar el this->status valor para usar otros valores NTSTATUS para registrar valores devueltos 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 Microsoft-Windows-Kernel-Memory el proveedor ETW 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++;
}

Guarde el archivo como etwnumamemstats.d

Abra una interfaz de comandos como Administrador y ejecute el script con la opción -s.

En ejecución 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 ETW admitidos

Tipo ETW Tipo de dato de D Language Notas
etw_struct Entero El valor del payload de este tipo representa el recuento de miembros que tendrá una nueva estructura.
etw_string cuerda / cadena No disponible
etw_mbcsstring cuerda / cadena No disponible
etw_int8 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "int8_t" en el script D.
etw_uint8 Entero El tamaño de tipo debe coincidir y se recomienda convertir en "uint8_t" en el script D.
etw_int16 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "int16_t" en el script D.
etw_uint16 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "uint16_t" en el script D.
etw_int32 Entero No disponible
etw_uint32 Entero No disponible
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 Escalar No se permiten constantes de punto flotante en el script D, pero sí en símbolos cargados.
etw_double Escalar No se permiten constantes de punto flotante en el script D, pero sí en símbolos cargados.
etw_bool32 Entero No disponible
etw_hexint32 Entero No disponible
etw_hexint64 Entero El tipo debe ser explícitamente "int64_t", ya que D tiene como valor predeterminado "int32_t".
etw_countedmbcsstring Entero No disponible
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 realizar la conversión a "int16_t" en el script D.
etw_char8 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "int8_t" en el script D.
etw_bool8 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "int8_t" en el script D.
etw_hexint8 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "int8_t" en el script D.
etw_hexint16 Entero El tamaño de tipo debe coincidir y se recomienda realizar la conversión a "int16_t" en el script D.
etw_pid Entero No disponible
etw_tid Entero No disponible
etw_mbcsxml Entero No disponible
etw_mbcsjson Entero No disponible
etw_countedmbcsxml Entero No disponible
etw_countedmbcsjson Entero No disponible
etw_win32error Entero No disponible
etw_ntstatus Entero No disponible
etw_hresult Entero No disponible

Véase también

DTrace en Windows

Programación de Windows de DTrace

Ejemplos de código de Windows de DTrace

Volcado en directo de DTrace