Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
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 |