Condividi tramite


DTrace ETW

Utilizzare DTrace per Windows per elaborare gli eventi ETW esistenti e per aggiungere nuovi eventi ETW.

Event Tracing for Windows (ETW) è una funzionalità di traccia a livello di kernel che consente di registrare eventi kernel o definiti dall'applicazione in un file di log. È possibile utilizzare gli eventi in tempo reale o da un file di log e usarli per eseguire il debug di un'applicazione o per determinare dove si verificano problemi di prestazioni nell'applicazione. Per informazioni generali su ETW, vedere Informazioni sulla traccia eventi.

Nota

DTrace è supportato nelle build Insider di Windows dopo la versione 18980 e Windows Server Build 18975.

Per informazioni generali sull'uso di DTrace in Windows, vedere DTrace.

ETW Windows DTrace Provider

È possibile usare DTrace per acquisire e segnalare gli eventi ETW registrati e di traccia del manifesto. Per eseguire il probe di parole chiave/livelli/eventID specifici, i probe ETW funzioneranno in modo molto più affidabile se non si usano caratteri jolly. Specificare invece completamente il probe in base a queste regole:

Probename = etw

Modname = Guid del provider nel formato xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, usando tutti i caratteri minuscoli.

Funcname = Level_Keyword del form 0x00_0x0000000000000000. Per trovare una corrispondenza con tutto ciò che deve essere impostato su 0xff_0xffffffffffffffff.

Probename = ID evento intero o "generic_event" in modo che corrispondano a tutti gli ID evento.

Il filtro basato su Probename funziona solo per gli eventi manifestati. Usare il carattere jolly (*) per gli eventi registrati nella traccia.

Il payload ETW è accessibile tramite arg0. Questo è costituito da nt'_EVENT_HEADER seguito da una data specifica dell'evento.

Determinazione dei provider ETW disponibili

Usare il comando logman per visualizzare i provider ETW attivi e i relativi GUID provider.

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}
...

Visualizzazione delle informazioni sul provider ETW esistenti

DTrace ha la possibilità di generare eventi ETW. Ciò è utile per gli scenari in cui è presente una pipeline ETW esistente da segnalare, raccogliere e analizzare.

Usare questo comando DTrace di esempio per segnalare gli eventi del provider 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

Aggiunta di nuovi eventi ETW

È possibile creare eventi di traccia Etw chiamando la macro etw_trace. Gli eventi verranno registrati solo se è presente un listener attivo per il provider di traccia specificato, altrimenti verranno ignorati.

La macro etw_trace supporta tipi di dati di base, ad esempio int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 e string. Per altri dettagli, vedere la tabella Tipi di dati ETW supportati di seguito.

Esempio di macro ETW_TRACE:

Questo script genera un evento ETW personalizzato quando la routine syscall restituisce 0xc0000001 - STATUS_UNSUCCESSFUL.

È possibile modificare il this->status valore in modo da usare valori NTSTATUS diversi per registrare valori restituiti syscall diversi.

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

Codice di esempio ETW NUMA MEM STATS

Questo script di esempio usa il provider ETW Microsoft-Windows-Kernel-Memory per eseguire il dump della memoria del nodo NUMA. Le dimensioni della pagina possono essere convertite in dimensioni in KB moltiplicando per 4. Per informazioni generali su NUMA, vedere Supporto NUMA.

Questo codice si trova anche in 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++;
}

Salvare il file come etwnumamemstats.d

Aprire un prompt dei comandi come Amministrazione istrator ed eseguire lo script usando l'opzione -s.

In esecuzione in un PC Windows client viene visualizzato un singolo 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

Tipi di dati ETW supportati

Tipo ETW Tipo di dati D Language Note
etw_struct Intero Il valore del payload di questo tipo rappresenta il numero di membri a cui avrà una nuova struttura.
etw_string string N/D
etw_mbcsstring string N/D
etw_int8 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int8_t' nello script D
etw_uint8 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'uint8_t' nello script D
etw_int16 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int16_t' nello script D
etw_uint16 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'uint16_t' nello script D
etw_int32 Intero N/D
etw_uint32 Intero N/D
etw_int64 Intero Il tipo deve essere 'int64_t' in modo esplicito perché per impostazione predefinita D è 'int32_t'
etw_uint64 Intero Il tipo deve essere 'int64_t' in modo esplicito perché per impostazione predefinita D è 'int32_t'
etw_float Scalare Le costanti a virgola mobile non sono consentite nello script D, ma lo consentono sui simboli caricati
etw_double Scalare Le costanti a virgola mobile non sono consentite nello script D, ma lo consentono sui simboli caricati
etw_bool32 Intero N/D
etw_hexint32 Intero N/D
etw_hexint64 Intero Il tipo deve essere 'int64_t' in modo esplicito perché per impostazione predefinita D è 'int32_t'
etw_countedmbcsstring Intero N/D
etw_intptr Intero Le dimensioni del tipo di dati cambiano in base all'architettura ('int32_t' rispetto a 'int64_t')
etw_uintptr Intero Le dimensioni del tipo di dati cambiano in base all'architettura ('int32_t' rispetto a 'int64_t')
etw_pointer Intero Le dimensioni del tipo di dati cambiano in base all'architettura ('int32_t' rispetto a 'int64_t')
etw_char16 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int16_t' nello script D
etw_char8 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int8_t' nello script D
etw_bool8 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int8_t' nello script D
etw_hexint8 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int8_t' nello script D
etw_hexint16 Intero È consigliabile che le dimensioni del tipo corrispondano e il cast a 'int16_t' nello script D
etw_pid Intero N/D
etw_tid Intero N/D
etw_mbcsxml Intero N/D
etw_mbcsjson Intero N/D
etw_countedmbcsxml Intero N/D
etw_countedmbcsjson Intero N/D
etw_win32error Intero N/D
etw_ntstatus Intero N/D
etw_hresult Intero N/D

Vedi anche

DTrace in Windows

Programmazione DTrace di Windows

Esempi di codice di Windows DTrace

Live Dump di Dtrace