다음을 통해 공유


DTrace ETW

Windows용 DTrace를 사용하여 기존 ETW 이벤트를 처리하고 새 ETW 이벤트를 추가합니다.

ETW(Windows용 이벤트 추적)는 커널 또는 애플리케이션 정의 이벤트를 로그 파일에 기록할 수 있는 커널 수준 추적 기능입니다. 실시간으로 또는 로그 파일에서 이벤트를 사용하여 애플리케이션을 디버그하거나 애플리케이션에서 성능 문제가 발생하는 위치를 확인할 수 있습니다. ETW에 대한 일반적인 내용은 이벤트 추적 정보를 참조 하세요.

참고 항목

DTrace는 버전 18980 및 Windows Server 빌드 18975 이후의 Windows 참가자 빌드에서 지원됩니다.

Windows에서 DTrace를 사용하는 방법에 대한 일반적인 내용은 DTrace를 참조 하세요.

ETW Windows DTrace 공급자

DTrace를 사용하여 기록된 추적 및 매니페스트 기반 ETW 이벤트를 캡처하고 보고할 수 있습니다. 특정 키워드(keyword)/levels/eventID를 검색하기 위해 야생 카드 사용하지 않는 경우 ETW 프로브가 훨씬 더 안정적으로 작동합니다. 대신 다음 규칙에 따라 프로브를 완전히 지정합니다.

Probename = etw

Modname = 모든 소문자를 사용하여 xxxxxxxx-xxxx-xxxx-xxxx-xxxx-xxxxxxxxxx 형식의 공급자 guid입니다.

Funcname = 양식 0x00_0x0000000000000000 Level_Keyword. 모든 항목과 일치하려면 0xff_0xffffffffffffffff 설정해야 합니다.

Probename = 모든 이벤트 ID와 일치하도록 정수 이벤트 ID 또는 "generic_event"입니다.

Probename 기반 필터링은 매니페스트된 이벤트에 대해서만 작동합니다. 추적된 이벤트에는 와일드 카드(*)를 사용합니다.

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 및 문자열과 같은 기본 데이터 형식을 지원합니다. 자세한 내용은 아래의 지원되는 ETW 데이터 형식 테이블을 참조하세요.

예제 ETW_TRACE 매크로:

이 스크립트는 syscall 루틴이 STATUS_UNSUCCESSFUL 0xc0000001 반환할 때 사용자 지정 ETW 이벤트를 생성합니다.

다른 NTSTATUS 값을 사용하여 다른 syscall 반환 값을 기록하도록 값을 변경할 this->status 수 있습니다.

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를 곱하여 KB의 크기로 변환할 수 있습니다. 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 PC에서 실행 중인 단일 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 string 해당 없음
etw_mbcsstring string 해당 없음
etw_int8 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int8_t'으로 캐스팅하는 것이 좋습니다.
etw_uint8 정수 형식 크기가 일치해야 하며 D 스크립트에서 'uint8_t'로 캐스팅하는 것이 좋습니다.
etw_int16 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int16_t'으로 캐스팅하는 것이 좋습니다.
etw_uint16 정수 형식 크기가 일치해야 하며 D 스크립트에서 'uint16_t'으로 캐스팅하는 것이 좋습니다.
etw_int32 정수 해당 없음
etw_uint32 정수 해당 없음
etw_int64 정수 D 기본값은 'int32_t'이므로 형식은 명시적으로 'int64_t'이어야 합니다.
etw_uint64 정수 D 기본값은 'int32_t'이므로 형식은 명시적으로 'int64_t'이어야 합니다.
etw_float 스칼라 부동 소수점 상수는 D 스크립트에서 허용되지 않지만 로드된 기호에서 허용합니다.
etw_double 스칼라 부동 소수점 상수는 D 스크립트에서 허용되지 않지만 로드된 기호에서 허용합니다.
etw_bool32 정수 해당 없음
etw_hexint32 정수 해당 없음
etw_hexint64 정수 D 기본값은 'int32_t'이므로 형식은 명시적으로 'int64_t'이어야 합니다.
etw_countedmbcsstring 정수 해당 없음
etw_intptr 정수 아키텍처에 따라 데이터 형식 크기가 변경됩니다('int32_t'과 'int64_t').
etw_uintptr 정수 아키텍처에 따라 데이터 형식 크기가 변경됩니다('int32_t'과 'int64_t').
etw_pointer 정수 아키텍처에 따라 데이터 형식 크기가 변경됩니다('int32_t'과 'int64_t').
etw_char16 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int16_t'으로 캐스팅하는 것이 좋습니다.
etw_char8 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int8_t'으로 캐스팅하는 것이 좋습니다.
etw_bool8 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int8_t'으로 캐스팅하는 것이 좋습니다.
etw_hexint8 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int8_t'으로 캐스팅하는 것이 좋습니다.
etw_hexint16 정수 형식 크기가 일치해야 하며 D 스크립트에서 'int16_t'으로 캐스팅하는 것이 좋습니다.
etw_pid 정수 해당 없음
etw_tid 정수 해당 없음
etw_mbcsxml 정수 해당 없음
etw_mbcsjson 정수 해당 없음
etw_countedmbcsxml 정수 해당 없음
etw_countedmbcsjson 정수 해당 없음
etw_win32error 정수 해당 없음
etw_nt상태 정수 해당 없음
etw_hresult 정수 해당 없음

참고 항목

Windows의 DTrace

DTrace Windows 프로그래밍

DTrace Windows 코드 샘플

DTrace 라이브 덤프