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


DTrace ETW

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

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

Примечание.

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

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

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

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

Probename = etw

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

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

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

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

Полезные данные 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.

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.

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

В этом примере скрипт использует поставщик ETW Microsoft-Windows-Kernel-Memory для дампа памяти узла 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

Откройте командную строку в качестве Администратор istrator и запустите скрипт с помощью параметра -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 Scalar Константы с плавающей запятой не допускаются в скрипте D, но разрешают его на загруженных символах.
etw_double Scalar Константы с плавающей запятой не допускаются в скрипте 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 Dump