Udostępnij przez


DTrace ETW

Użyj funkcji DTrace dla systemu Windows, aby przetworzyć istniejące zdarzenia ETW i dodać nowe zdarzenia ETW.

Śledzenie zdarzeń dla systemu Windows (ETW) to funkcja śledzenia na poziomie jądra, która umożliwia rejestrowanie zdarzeń jądra lub zdarzeń zdefiniowanych przez aplikację do pliku dziennika. Można przetwarzać zdarzenia w czasie rzeczywistym lub z rejestru i używać ich do debugowania aplikacji lub określania, gdzie występują problemy z wydajnością w aplikacji. Aby uzyskać ogólne informacje na temat funkcji ETW, zobacz About Event Tracing (Informacje o śledzeniu zdarzeń).

Uwaga / Notatka

Narzędzie DTrace jest obsługiwane w kompilacjach niejawnych systemu Windows po wersji 18980 i Windows Server Build 18975.

Aby uzyskać ogólne informacje na temat pracy z funkcją DTrace w systemie Windows, zobacz DTrace.

Dostawca DTrace systemu Windows ETW

Możesz użyć narzędzia DTrace do przechwytywania i raportowania zdarzeń ETW opartych na logach i manifestach. Aby sondować określone słowa kluczowe/poziomy/identyfikatory zdarzeń, sondy ETW będą działać znacznie bardziej niezawodnie, jeśli nie używasz symboli wieloznacznych. Zamiast tego w pełni określ sondę na podstawie następujących reguł:

Nazwa sondy = etw

Modname = identyfikator GUID dostawcy w postaci xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, używając wszystkich małych liter.

Funcname = Level_Keyword w formacie 0x00_0x0000000000000000. Aby dopasować wszystko, należy ustawić wartość 0xff_0xffffffffffffffff.

Probename = Integer Event ID lub "generic_event", aby dopasować wszystkie identyfikatory zdarzeń.

Filtrowanie na podstawie nazwy sondy działa tylko w przypadku zdarzeń manifestowanych. Użyj symbolu wieloznacznego (*) dla śledzonych zdarzeń.

Ładunek ETW jest dostępny za pośrednictwem arg0. Składa się to z nt'_EVENT_HEADER, po którym następuje data specyficzna dla zdarzenia.

Określanie dostępnych dostawców ETW

Użyj polecenia logman, aby wyświetlić aktywnych dostawców ETW i ich identyfikatory GUID dostawcy.

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

Wyświetlanie istniejących informacji o dostawcy ETW

DTrace może wyprowadzać zdarzenia ETW. Jest to przydatne w przypadkach, w których istnieje przewód ETW do zbierania, analizowania i raportowania.

Użyj przykładowego polecenia DTrace, aby zgłosić zdarzenia dotyczące dostawcy 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

Dodawanie nowych zdarzeń ETW

Zdarzenia śledzenia ETW można utworzyć, wywołując makro etw_trace. Zdarzenia będą rejestrowane tylko wtedy, gdy istnieje aktywny odbiornik dla określonego dostawcy śledzenia, w przeciwnym razie zostaną pominięte.

Makro etw_trace obsługuje podstawowe typy danych, takie jak int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 i string. Aby uzyskać więcej informacji, zobacz poniższą tabelę Obsługiwanych typów danych ETW .

Przykładowe makro ETW_TRACE:

Ten skrypt generuje niestandardowe zdarzenie ETW, gdy procedura syscall zwraca 0xc0000001 — STATUS_UNSUCCESSFUL.

Możesz zmienić wartość this->status, aby użyć innych wartości NTSTATUS, aby rejestrować różne wartości zwracane przez syscall.

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

Przykładowy kod ETW NUMA MEM STATS

W tym przykładowym skrypcie użyto dostawcyKernel-Memory ETW firmy Microsoft do zrzutu pamięci węzła NUMA. Rozmiar strony można przekonwertować na rozmiar w KB, mnożąc przez 4. Aby uzyskać ogólne informacje na temat NUMA, zobacz Obsługa NUMA.

Ten kod znajduje się również w lokalizacji 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++;
}

Zapisz plik jako etwnumamemstats.d

Otwórz wiersz polecenia jako administrator i uruchom skrypt przy użyciu opcji -s.

Na komputerze klienckim z systemem Windows jest wyświetlany jeden węzeł 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

Obsługiwane typy danych ETW

Typ ETW Typ danych języka D Notatki
etw_struct Integer Wartość ładunku tego typu reprezentuje liczbę elementów, jakie będzie miała nowa struktura.
etw_string ciąg N/A
etw_mbcsstring ciąg N/A
etw_int8 Integer Rozmiar typu powinien być zgodny, a rzutowanie do "int8_t" w skrypcie D jest zalecane.
etw_uint8 Integer Rozmiar typu powinien być zgodny, a rzutowanie na `uint8_t` w skrypcie D jest zalecane.
etw_int16 Integer Rozmiar typu powinien być zgodny, a rzutowanie do wartości "int16_t" w skrypsie D jest zalecane
etw_uint16 Integer Rozmiar typu powinien być zgodny, a rzutowanie do `uint16_t` w skrypcie D jest zalecane.
etw_int32 Integer N/A
etw_uint32 Integer N/A
etw_int64 Integer Typ musi być jawnie "int64_t", ponieważ domyślnie D ma wartość "int32_t"
etw_uint64 Integer Typ musi być jawnie "int64_t", ponieważ domyślnie D ma wartość "int32_t"
etw_float Skalar Stałe zmiennoprzecinkowe nie są dozwolone w skrypcie D, ale są akceptowane na załadowanych symbolach.
etw_double Skalar Stałe zmiennoprzecinkowe nie są dozwolone w skrypcie D, ale są dozwolone w odniesieniu do załadowanych symboli.
etw_bool32 Integer N/A
etw_hexint32 Integer N/A
etw_hexint64 Integer Typ musi być jawnie "int64_t", ponieważ domyślnie D ma wartość "int32_t"
etw_countedmbcsstring Integer N/A
etw_intptr Integer Rozmiar typu danych zmienia się zgodnie z architekturą ("int32_t" a "int64_t")
etw_uintptr Integer Rozmiar typu danych zmienia się zgodnie z architekturą ("int32_t" a "int64_t")
etw_pointer Integer Rozmiar typu danych zmienia się zgodnie z architekturą ("int32_t" a "int64_t")
etw_char16 Integer Rozmiar typu powinien być zgodny, a rzutowanie do wartości "int16_t" w skrypsie D jest zalecane
etw_char8 Integer Rozmiar typu powinien być zgodny, a rzutowanie do wartości "int8_t" w skrypsie D jest zalecane
etw_bool8 Integer Rozmiar typu powinien być zgodny, a rzutowanie do wartości "int8_t" w skrypsie D jest zalecane
etw_hexint8 Integer Rozmiar typu powinien być zgodny, a rzutowanie do wartości "int8_t" w skrypsie D jest zalecane
etw_hexint16 Integer Rozmiar typu powinien być zgodny, a rzutowanie do wartości "int16_t" w skrypcie D jest zalecane.
etw_pid Integer N/A
etw_tid Integer N/A
etw_mbcsxml Integer N/A
etw_mbcsjson Integer N/A
etw_countedmbcsxml Integer N/A
etw_countedmbcsjson Integer N/A
etw_win32error Integer N/A
etw_ntstatus Integer N/A
etw_hresult Integer N/A

Zobacz też

DTrace w systemie Windows

DTrace Windows Programming

Przykłady kodu systemu Windows DTrace

Zrzut na żywo DTrace