Поделиться через


DTrace ETW

Используйте DTrace для Windows для обработки существующих событий ETW и добавления новых событий ETW.

Трассировка событий для Windows (ETW) — это средство трассировки на уровне ядра, которое позволяет регистрировать события ядра или события, определенные приложением, в файл журнала. События можно использовать в режиме реального времени или из файла журнала и использовать их для отладки приложения или определения проблем с производительностью приложения. Общие сведения о трассировке событий см. в разделе "О трассировке событий".

Замечание

DTrace поддерживается в инсайдерских сборках Windows после версии 18980 и Windows Server сборке 18975.

Общие сведения о работе с DTrace в Windows см. в разделе DTrace.

Поставщик DTrace для Windows ETW

DTrace можно использовать для записи и отчета о событиях, зафиксированных в трассировке, и событиях ETW на основе манифестов. Для проверки определенных ключевых слов, уровней или идентификаторов событий пробы ETW будут работать гораздо надежнее, если вы не используете подстановочные карточки. Вместо этого полностью укажите датчик на основе следующих правил:

Probename = etw

Modname = GUID поставщика в форме xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, используя все строчные символы.

Funcname = Level_Keyword формы 0x00_0x0000000000000000. Чтобы сопоставить все это, необходимо задать значение 0xff_0xffffffffffffffff.

Probename = целочисленный идентификатор события или "generic_event" для сопоставления всех идентификаторов событий.

Фильтрация на основе имени пробы работает только для манифестированных событий. Используйте подстановочный знак (*) для трассируемых событий.

Нагрузка ETW доступ осуществляется через arg0. Это состоит из nt'_EVENT_HEADER, за которым следует конкретная дата события.

Определение доступных поставщиков ETW

Используйте команду logman для отображения активных провайдеров ETW и их идентификаторов GUID.

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

Отображение сведений о существующем поставщике ETW

DTrace имеет возможность выводить события ETW. Это полезно для сценариев, когда существует конвейер ETW для создания отчетов, сбора и анализа.

Используйте эту команду DTrace, чтобы регистрировать события провайдера 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

Добавление новых событий ETW

События трассировки etw можно создать, вызвав макрос etw_trace. События будут регистрироваться только если существует активный прослушиватель для указанного поставщика трассировки, в противном случае события будут пропущены.

Макрос etw_trace поддерживает базовые типы данных, такие как int8, uint8, int16, uint16, int32, uint32, int64, uint64, hexint32, hexint64 и string. Дополнительные сведения см. в таблице поддерживаемых типов данных ETW ниже.

Пример макроса ETW_TRACE:

Этот скрипт создает настраиваемое событие ETW, когда подпрограмма syscall возвращает 0xc0000001 - STATUS_UNSUCCESSFUL.

Можно изменить this->status значения, чтобы использовать другие значения NTSTATUS для регистрации различных возвращаемых значений системных вызовов.

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

В этом примере скрипт использует поставщика Microsoft-Windows-Kernel-Memory ETW для дампа памяти узла NUMA. Размер страницы может быть преобразован в размер в КБ путем умножения на 4. Общие сведения о NUMA см. в разделе "Поддержка NUMA".

Этот код также находится в 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++;
}

Сохраните файл как etwnumamemstats.d

Откройте командную строку от имени администратора и запустите скрипт с помощью параметра -s.

На клиентском Windows-компьютере отображается один узел 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

Поддерживаемые типы данных ETW

Тип ETW Тип данных языка D Примечания
etw_struct Целое число Значение полезной нагрузки этого типа представляет количество членов новой структуры.
etw_string струна Не применимо
etw_mbcsstring струна Не применимо
etw_int8 Целое число Размер типа должен совпадать, и рекомендуется приведение к int8_t в скрипте D.
etw_uint8 Целое число Размер типа должен совпадать, и приведение к типу `uint8_t` в скрипте D рекомендуется
etw_int16 Целое число Размер типа должен совпадать, и приведение к `int16_t` в скрипте D рекомендуется.
etw_uint16 Целое число Размер типа должен соответствовать, а приведение типа к `uint16_t` в скрипте D рекомендуется.
etw_int32 Целое число Не применимо
etw_uint32 Целое число Не применимо
etw_int64 Целое число Тип должен быть явным образом "int64_t", так как по умолчанию D используется значение "int32_t"
etw_uint64 Целое число Тип должен быть явным образом "int64_t", так как по умолчанию D используется значение "int32_t"
etw_float Скаляр Константы с плавающей запятой не допускаются в скрипте D, но разрешены для загруженных символов.
etw_double Скаляр Константы с плавающей запятой не допускаются в скрипте D, но разрешены для загруженных символов.
etw_bool32 Целое число Не применимо
etw_hexint32 Целое число Не применимо
etw_hexint64 Целое число Тип должен быть явным образом "int64_t", так как по умолчанию D используется значение "int32_t"
etw_countedmbcsstring Целое число Не применимо
etw_intptr Целое число Размер типа данных изменяется в соответствии с архитектурой ("int32_t" и "int64_t"
etw_uintptr Целое число Размер типа данных изменяется в соответствии с архитектурой ("int32_t" и "int64_t"
etw_pointer Целое число Размер типа данных изменяется в соответствии с архитектурой ("int32_t" и "int64_t"
etw_char16 Целое число Размер типа должен соответствовать, и приведение к `int16_t` в D-скрипте рекомендуется.
etw_char8 Целое число Размер типа должен соответствовать, и приведение к типу "int8_t" в скрипте D рекомендуется
etw_bool8 Целое число Размер типа должен соответствовать и приведение к "int8_t" в скрипте D рекомендуется
etw_hexint8 Целое число Размер типа должен совпадать, и рекомендуется приведение к int8_t в скрипте D.
etw_hexint16 Целое число Размер типа должен соответствовать, а приведение к `int16_t` в скрипте D рекомендуется
etw_pid Целое число Не применимо
etw_tid Целое число Не применимо
etw_mbcsxml Целое число Не применимо
etw_mbcsjson Целое число Не применимо
etw_countedmbcsxml Целое число Не применимо
etw_countedmbcsjson Целое число Не применимо
etw_win32error Целое число Не применимо
etw_ntstatus Целое число Не применимо
etw_hresult Целое число Не применимо

См. также

DTrace в Windows

Программирование DTrace Windows

Примеры кода DTrace Для Windows

Дамп DTrace Live