DTrace ETW
Verwenden Sie DTrace für Windows, um vorhandene ETW-Ereignisse zu verarbeiten und neue ETW-Ereignisse hinzuzufügen.
Event Tracing for Windows (ETW) ist eine Tracing-Funktion auf Kernel-Ebene, mit der Sie kernel- oder anwendungsdefinierte Ereignisse in einer Protokolldatei aufzeichnen können. Sie können die Ereignisse in Echtzeit oder aus einer Protokolldatei abrufen und sie zur Fehlersuche in einer Anwendung oder zur Ermittlung von Leistungsproblemen in der Anwendung verwenden. Allgemeine Informationen über ETW finden Sie unter Über die Ereignisverfolgung.
Hinweis
DTrace wird in den Insider-Builds von Windows nach Version 18980 und Windows Server Build 18975 unterstützt.
Allgemeine Informationen zur Arbeit mit DTrace unter Windows finden Sie unter DTrace.
ETW Windows DTrace-Anbieter
Sie können DTrace verwenden, um protokollierte und manifestbasierte ETW-Ereignisse zu erfassen und zu protokollieren. Um bestimmte Schlüsselwörter/Ebenen/Ereignis-IDs abzufragen, funktionieren ETW-Sonden viel zuverlässiger, wenn Sie keine Platzhalter verwenden. Spezifizieren Sie stattdessen Ihren Test vollständig auf der Grundlage dieser Regeln:
Probename = etw
Modname = Provider-Guid in der Form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, wobei alle Kleinbuchstaben zu verwenden sind.
Funcname = Level_Keyword in der Form 0x00_0x0000000000000000. Damit alles passt, sollte dieser Wert auf 0xff_0xffffffffffffffffff gesetzt werden.
Probename = Ganzzahlige Ereignis-ID oder "generic_event", um alle Ereignis-IDs abzugleichen.
Das Filtern nach dem Probenamen funktioniert nur bei manifestierten Ereignissen. Verwenden Sie den Platzhalter (*) für protokollierte Ereignisse.
Der Zugriff auf die ETW-Nutzlast erfolgt über arg0. Dieser besteht aus nt`_EVENT_HEADER, gefolgt von einem ereignisspezifischen Datum.
Ermittlung der verfügbaren ETW-Anbieter
Verwenden Sie den Befehl logman, um aktive ETW-Provider und ihre Provider-GUIDs anzuzeigen.
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}
...
Anzeige vorhandener ETW-Anbieterinformationen
DTrace hat die Fähigkeit, ETW-Ereignisse auszugeben. Dies ist hilfreich für Szenarien, in denen es eine bestehende ETW-Pipeline gibt, die gemeldet, gesammelt und analysiert werden muss.
Verwenden Sie diesen Beispiel-DTrace-Befehl, um Ereignisse des Microsoft-Windows-Kernel-Memory-Anbieters zu melden.
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
Hinzufügen neuer ETW-Ereignisse
Etw-Trace-Ereignisse können durch den Aufruf des Makros etw_trace erzeugt werden. Ereignisse werden nur dann protokolliert, wenn es einen aktiven Listener für den angegebenen Trace-Provider gibt, andernfalls werden sie übersprungen.
Das Makro etw_trace unterstützt grundlegende Datentypen wie int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 und string. Weitere Einzelheiten finden Sie in der nachstehenden Tabelle Unterstützte ETW-Datentypen.
Beispiel für das Makro ETW_TRACE:
Dieses Skript erzeugt ein benutzerdefiniertes ETW-Ereignis, wenn die Syscall-Routine 0xc0000001 - STATUS_UNSUCCESSFUL zurückgibt.
Sie können den Wert this->status
ändern, um einen anderen NTSTATUS-Wert zu verwenden, um verschiedene Syscall-Rückgabewerte zu protokollieren.
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
ETW NUMA MEM STATS Beispielcode
Dieses Beispielskript verwendet den ETW-Anbieter Microsoft-Windows-Kernel-Memory, um den Speicher von NUMA-Knoten zu sichern. Die Seitengröße kann durch Multiplikation mit 4 in die Größe in KB umgerechnet werden. Allgemeine Informationen zu NUMA finden Sie unter NUMA Support.
Dieser Code findet sich auch unter 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++;
}
Speichern Sie die Datei unter dem Namen etwnumamemstats.d
Öffnen Sie eine Eingabeaufforderung als Administrator und führen Sie das Skript mit der Option -s aus.
Auf einem Windows-Client-PC wird ein einzelner NUMA-Knoten angezeigt.
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
Unterstützte ETW-Datentypen
ETW-Typ | D Sprachdatentyp | Hinweise |
---|---|---|
etw_struct | Ganzzahl | Der Payload-Wert dieses Typs gibt die Anzahl der Mitglieder an, die eine neue Struktur haben wird. |
etw_string | Zeichenfolge | N/V |
etw_mbcsstring | Zeichenfolge | N/V |
etw_int8 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int8_t` zu casten |
etw_uint8 | Ganzzahl | Die Größe des Typs sollte übereinstimmen, und es wird empfohlen, im D-Skript auf "uint8_t" zu casten |
etw_int16 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int16_t` zu casten |
etw_uint16 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript nach "uint16_t" zu casten |
etw_int32 | Ganzzahl | N/V |
etw_uint32 | Ganzzahl | N/V |
etw_int64 | Ganzzahl | Der Typ muss explizit "int64_t" sein, da D standardmäßig "int32_t" verwendet |
etw_uint64 | Ganzzahl | Der Typ muss explizit "int64_t" sein, da D standardmäßig "int32_t" verwendet |
etw_float | Skalar | Fließkomma-Konstanten sind in D-Skripten nicht erlaubt, aber bei geladenen Symbolen ist es erlaubt |
etw_double | Skalar | Fließkomma-Konstanten sind in D-Skripten nicht erlaubt, aber bei geladenen Symbolen ist es erlaubt |
etw_bool32 | Ganzzahl | N/V |
etw_hexint32 | Ganzzahl | N/V |
etw_hexint64 | Ganzzahl | Der Typ muss explizit "int64_t" sein, da D standardmäßig "int32_t" verwendet |
etw_countedmbcsstring | Ganzzahl | N/V |
etw_intptr | Ganzzahl | Die Größe des Datentyps hängt von der Architektur ab (`int32_t` vs. `int64_t`) |
etw_uintptr | Ganzzahl | Die Größe des Datentyps hängt von der Architektur ab (`int32_t` vs. `int64_t`) |
etw_pointer | Ganzzahl | Die Größe des Datentyps hängt von der Architektur ab (`int32_t` vs. `int64_t`) |
etw_char16 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int16_t` zu casten |
etw_char8 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int8_t` zu casten |
etw_bool8 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int8_t` zu casten |
etw_hexint8 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int8_t` zu casten |
etw_hexint16 | Ganzzahl | Die Typgröße sollte übereinstimmen, und es wird empfohlen, im D-Skript auf `int16_t` zu casten |
etw_pid | Ganzzahl | N/V |
etw_tid | Ganzzahl | N/V |
etw_mbcsxml | Ganzzahl | N/V |
etw_mbcsjson | Ganzzahl | N/V |
etw_countedmbcsxml | Ganzzahl | N/V |
etw_countedmbcsjson | Ganzzahl | N/V |
etw_win32error | Ganzzahl | N/V |
etw_ntstatus | Ganzzahl | N/V |
etw_hresult | Ganzzahl | N/V |