DTrace 코드 샘플
DTrace는 D 프로그래밍 언어를 지원합니다. 이 항목에서는 D 코드 샘플을 제공합니다.
Windows의 DTrace에 대한 일반적인 내용은 DTrace를 참조 하세요.
DTrace에 대한 자세한 내용은 케임브리지 대학의 OpenDTrace 사양 버전 1.0을 참조하세요.
참고 항목
DTrace는 버전 18980 및 Windows Server 빌드 18975 이후의 Windows 참가자 빌드에서 지원됩니다.
추가 샘플 스크립트
Windows 시나리오에 적용할 수 있는 추가 D 스크립트는 DTrace 소스 코드의 샘플 디렉터리에서 사용할 수 있습니다.
https://github.com/microsoft/DTrace-on-Windows/tree/windows/samples/windows
유용한 opentrace 도구 키트 스크립트 집합은 .에서 https://github.com/opendtrace/toolkit사용할 수 있습니다.
이름별 디스크 사용량
이 스크립트는 지정된 실행 파일 이름에 대한 디스크 카운터를 제공합니다. 실행 파일 이름은 대/소문자를 구분하며 스크립트가 시작될 때 명령줄에 제공됩니다.
#pragma D option quiet
#pragma D option destructive
intptr_t curptr;
struct nt`_EPROCESS *eprocess_ptr;
int found;
int firstpass;
BEGIN
{
curptr = (intptr_t) ((struct nt`_LIST_ENTRY *) (void*)&nt`PsActiveProcessHead)->Flink;
found = 0;
firstpass = 1;
bytesread = 0;
byteswrite = 0;
readcount = 0;
writecount = 0;
flushcount = 0;
}
tick-1ms
/found == 0/
{
/* use this for pointer parsing */
if (found == 0)
{
eprocess_ptr = (struct nt`_EPROCESS *)(curptr - offsetof(nt`_EPROCESS, ActiveProcessLinks));
processid = (string) eprocess_ptr->ImageFileName;
if ($1 == processid)
{
found = 1;
}
else
{
curptr = (intptr_t) ((struct nt`_LIST_ENTRY *) (void*)curptr)->Flink;
}
}
}
tick-2s
{
system ("cls");
if (found == 1)
{
if (firstpass)
{
firstpass = 0;
bytesread = (unsigned long long) eprocess_ptr->DiskCounters->BytesRead;
byteswrite = (unsigned long long) eprocess_ptr->DiskCounters->BytesWritten;
readcount = eprocess_ptr->DiskCounters->ReadOperationCount;
writecount = eprocess_ptr->DiskCounters->WriteOperationCount;
flushcount = eprocess_ptr->DiskCounters->FlushOperationCount;
}
else
{
bytesread = (unsigned long long) (eprocess_ptr->DiskCounters->BytesRead - bytesread);
byteswrite = (unsigned long long) (eprocess_ptr->DiskCounters->BytesWritten - byteswrite);
readcount = eprocess_ptr->DiskCounters->ReadOperationCount - readcount;
writecount = eprocess_ptr->DiskCounters->WriteOperationCount - writecount;
flushcount = eprocess_ptr->DiskCounters->FlushOperationCount - flushcount;
printf("*** Reports disk read/write every second *** \n");
printf("Process name: %s\n", eprocess_ptr->ImageFileName);
printf("Process Id: %d\n", (int) eprocess_ptr->UniqueProcessId);
printf("Bytes Read %llu \n", (unsigned long long) bytesread);
printf("Bytes Written %llu \n", (unsigned long long) byteswrite);
printf("Read Operation Count %d \n", readcount);
printf("Write Operation Count %d \n",writecount);
printf("Flush Operation Count %d \n", flushcount);
bytesread = (unsigned long long) eprocess_ptr->DiskCounters->BytesRead;
byteswrite = (unsigned long long) eprocess_ptr->DiskCounters->BytesWritten;
readcount = eprocess_ptr->DiskCounters->ReadOperationCount;
writecount = eprocess_ptr->DiskCounters->WriteOperationCount;
flushcount = eprocess_ptr->DiskCounters->FlushOperationCount;
}
}
else
{
printf("No matching process found for %s \n", $1);
exit(0);
}
}
파일을 diskuagebyname.d로 저장하고 -s 옵션을 사용하여 테스트 스크립트를 실행합니다. 원하는 exe의 대/소문자 구분 이름(예: 메모장.exe
C:\>dtrace -s diskusagebyname.d Notepad.exe
*** Reports disk read/write every second ***
Process name: Notepad.exe
Process Id: 6012
Bytes Read 0
Bytes Written 0
Read Operation Count 0
Write Operation Count 0
Flush Operation Count 0
*** Reports disk read/write every second ***
Process name: cmd.exe
Process Id: 4428
Bytes Read 18446744073709480960
Bytes Written 18446744073709522944
Read Operation Count -5
Write Operation Count -7
Flush Operation Count 0
디스크 작업의 실행 로그를 유지하려면 다음과 같이 명령을 텍스트 파일로 리디렉션합니다.
C:\>dtrace -s diskusagebyname.d Notepad.exe>>diskstats.txt
메모리 사용량 덤프
일부 진단 경우 상황을 이해하기 위해 커널 구조를 덤프해야 합니다. 이 코드는 시스템 메모리 사용량을 덤프하는 예제를 보여 줍니다. 아래 D 스크립트는 3초마다 타이머 프로브를 실행하고 시스템 메모리 사용량을 새로 고칩니다.
#pragma D option quiet
#pragma D option destructive
tick-3s
{
system ("cls");
this->pp = ((struct nt`_MI_PARTITION *) &nt`MiSystemPartition);
printf("***** Printing System wide page information ******* \n");
printf( "Total Pages Entire Node: %u MB \n", this->pp->Core.NodeInformation->TotalPagesEntireNode*4096/(1024*1024));
printf("Total Available Pages: %u Mb \n", this->pp->Vp.AvailablePages*4096/(1024*1024));
printf("Total ResAvail Pages: %u Mb \n", this->pp->Vp.ResidentAvailablePages*4096/(1024*1024));
printf("Total Shared Commit: %u Mb \n", this->pp->Vp.SharedCommit*4096/(1024*1024));
printf("Total Pages for PagingFile: %u Mb \n", this->pp->Vp.TotalPagesForPagingFile*4096/(1024*1024));
printf("Modified Pages : %u Mb \n", this->pp->Vp.ModifiedPageListHead.Total*4096/(1024*1024));
printf("Modified No Write Page Count : %u Mb \n", this->pp->Vp.ModifiedNoWritePageListHead.Total*4096/(1024*1024));
printf("Bad Page Count : %d Mb \n", this->pp->PageLists.BadPageListHead.Total*4096/(1024*1024));
printf("Zero Page Count : %d Mb \n", this->pp->PageLists.ZeroedPageListHead.Total*4096/(1024*1024));
printf("Free Page Count : %d Mb \n", this->pp->PageLists.FreePageListHead.Total*4096/(1024*1024));
/********** Printing Commit info ******************/
this->commit = ((struct nt`_MI_PARTITION_COMMIT ) ((struct nt`_MI_PARTITION *) &nt`MiSystemPartition)->Commit);
printf("***** Printing Commit Info ******* \n");
printf("Total Committed Pages: %u Mb \n", this->pp->Vp.TotalCommittedPages*4096/(1024*1024));
printf("Total Commit limit: %u Mb \n", this->pp->Vp.TotalCommitLimit*4096/(1024*1024));
printf("Peak Commitment: %u Mb \n", this->commit.PeakCommitment*4096/(1024*1024));
printf("Low Commit Threshold: %u Mb \n", this->commit.LowCommitThreshold*4096/(1024*1024));
printf("High Commit Threshold: %u Mb \n", this->commit.HighCommitThreshold*4096/(1024*1024));
printf("System Commit Reserve: %u Mb \n", this->commit.SystemCommitReserve*4096/(1024*1024));
}
파일을 dumpmemoryusage.d로 저장합니다.
이 샘플에서는 기호를 사용하므로 설치에 설명된 대로 기호 경로를 설정합니다.
set _NT_SYMBOL_PATH=srv*C:\symbols*https://msdl.microsoft.com/download/symbols
-s 옵션을 사용하여 테스트 스크립트를 실행합니다.
C:\>dtrace -s dumpmemoryusage.d
***** Printing System wide page information *******
Total Pages Entire Node: 2823 MB
Total Available Pages: 622 Mb
Total ResAvail Pages: 2369 Mb
Total Shared Commit: 166 Mb
Total Pages for PagingFile: 30 Mb
Modified Pages : 30 Mb
Modified No Write Page Count : 0 Mb
Bad Page Count : 0 Mb
Zero Page Count : 7 Mb
Free Page Count : 0 Mb
***** Printing Commit Info *******
Total Committed Pages: 2401 Mb
Total Commit limit: 4551 Mb
Peak Commitment: 2989 Mb
Low Commit Threshold: 3413 Mb
High Commit Threshold: 4295 Mb
System Commit Reserve: 5 Mb
이 프로그램은 메모리 사용량을 계속 모니터링하도록 설계되었습니다. Ctrl+C를 눌러 종료합니다.
활동에 대한 힙 사용 가능한 시간 분석 식별
이 샘플에서는 RtlFreeHeap 함수를 하위 함수로 분석하고 이러한 함수를 실행하는 데 걸린 최대 시간을 표시합니다.
/* Mark script destructive as we call "cls" in the tick-1sec provider*/
#pragma D option destructive
/* Program to calculate time taken in RtlFreeHeap function. Hit Ctrl-C to break. */
dtrace:::BEGIN
{
counter1 = 0;
}
tick-1sec
{
system ("cls");
printf("count of function hit = %d << Press Ctrl-C to exit >> \n", counter1);
}
/* Instrument entry of RtlFreeHeap*/
pid$1:ntdll:RtlFreeHeap:entry
{
/* self is thread local */
self->ts = timestamp;
self->depth = 0;
counter1++;
}
pid$1:ntdll:RtlFreeHeap:return
/self->ts/
{
this->ts = timestamp - self->ts;
@disttime = quantize(this->ts);
@funcName[probemod,probefunc] = max(this->ts);
self->ts = 0;
}
pid$1:::entry
/ self->ts /
{
self->functime[self->depth] = timestamp;
self->depth++;
}
pid$1:::return
/ self->ts /
{
if (self->depth > 0)
{
self->depth--;
/* this is a local scope */
this->ts = timestamp - self->functime[self->depth];
@funcName[probemod,probefunc] = max(this->ts);
}
}
END
{
system ("cls");
/* Print overall time distribution for RtlFreeHeap */
printa (@disttime);
/* Print top 15 functions along with average time spent executing them */
trunc (@funcName, 15);
printa( @funcName);
}
파일을 heapfree.d로 저장합니다.
이 스크립트는 프로세스 ID 또는 PID를 사용합니다. 스크립트를 테스트하려면 명령 프롬프트에서 작업 목록을 실행하고 원하는 PID를 찾습니다.
C:\>tasklist
Image Name PID Session Name Session# Mem Usage
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 108 K
Registry 72 Services 0 35,576 K
...
Notepad.exe 7944 31C5CE94259D4006 2 20,736 K
PID를 매개 변수로 지정하고 -s 옵션을 사용하여 표시된 대로 테스트 스크립트를 실행합니다.
C:\>dtrace -s heapfree.d 7944
count of function hit = 28 <<Press Ctrl-C to exit>>
PID와 연결된 프로그램에 관심이 있는 작업을 수행합니다(예: 메모장 입력하거나 글꼴 변경).
CRTL-C를 누르면 스택 정보와 함께 힙 사용량이 표시됩니다.
value ------------- Distribution ------------- count
4096 | 0
8192 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 23
16384 |@@@@@@ 4
32768 | 0
65536 | 0
131072 |@ 1
262144 | 0
ntdll RtlTryEnterCriticalSection 9444
ntdll RtlGetCurrentServiceSessionId 12302
ntdll RtlFreeHeap 151354
메모리 풀 추적
참고: aggsortkeypos 변수 집합을 사용하여 이 스크립트를 실행합니다. 이 변수는 D-Trace에 첫 번째 인덱스(크기)를 기준으로 출력을 정렬하도록 알릴 수 있습니다.
사용법:
dtrace -s <script.d> <Time> <Tag> -x aggsortkey -x aggsortkeypos=1
KSec 누수 추적 예제: dtrace -s PoolTrackingSummary.d "120s" 0x6365734b -x aggsortkey -x aggsortkeypos=1
값은 <Tag>
메모리 풀 태그의 인코딩된 ASCII 값입니다. 예를 들어 nt 파일 시스템의 태그 값을 인코딩하는 경우 - NtFf, 먼저 두 문자 집합을 반대로 변환하면 fFtN이 됩니다. ASCII 문자를 16진수로 변환하는 것은 46 66 74 4e이거나 프로그램에 대한 매개 변수로 0x4666744e.
또 다른 예는 Ksec를 변환하는 것입니다.
먼저 문자 순서를 반대로 바꿉니다. cesK
그런 다음 해당 문자를 16진수, 63 65 73 4b 또는 0x6365734b 프로그램에 대한 매개 변수로 변환합니다.
상위 소문자를 풀 태그와 일치시키는 것이 중요합니다.
출력: 스크립트는 120초 동안 실행되고 KSec 할당/무료 요약을 출력합니다. 필요한 시간으로 설정할 수 있습니다.
#pragma D option destructive
#pragma D option quiet
#pragma D option dynvarsize=240m
#pragma D option bufsize=120m
#pragma D option aggsize=120m
fbt:nt:ExAllocatePoolWithTag:entry
{
/* This is E100 in reserve. Convert ASCII to Hex => Hex('001E').*/
if (arg2 == $2)
{
self->size = (unsigned int) arg1;
@allocstack[stack(), self->size] = count();
}
}
fbt:nt:ExAllocatePoolWithTag:return
/self->size/
{
@mem[(uintptr_t) arg1] = sum(1);
addr[(uintptr_t) arg1] = 1;
/* printf("%Y: Execname %s allocated size %d bytes return ptr %x", walltimestamp, execname, (uint64_t) self->size, (uintptr_t) arg1 );*/
size[(uintptr_t) arg1] = self->size;
@sizealloc[self->size] = count();
@delta[self->size] = sum(1);
self->size = 0;
}
fbt:nt:ExFreePoolWithTag:entry
/addr[(uintptr_t) arg0]/
{
@mem[(uintptr_t) arg0] = sum (-1);
addr[(uintptr_t) arg0] -= 1;
@sizefree[size[(uintptr_t) arg0]] = count();
@delta[size[(uintptr_t) arg0]] = sum(-1);
}
tick-$1
{
exit(0);
}
END
{
printf("%10s %10s %10s %10s\n", "SIZE", "ALLOC", "FREE", "DELTA");
printa("%10d %@10d %@10d %@10d\n", @sizealloc, @sizefree, @delta);
printf("Printing stacks \n");
printa (@allocstack);
printf("== REPORT ==\n\n");
printa("0x%x => %@u\n",@mem);
}
파일을 pooltrackingsummary.d로 저장하고 -s 옵션을 사용하여 위에서 설명한 태그 값 및 기타 매개 변수를 제공하여 테스트 스크립트를 실행합니다.
120s 옵션인 프로그램은 2분 동안 실행됩니다. 이 시간 동안 Windows를 사용하여 모니터링되는 메모리 풀을 실행합니다. 예를 들어 웹 브라우저 또는 다른 프로그램을 로드하고 언로드합니다.
C:\>dtrace -s pooltrackingsummary.d "120s" 0x4666744e -x aggsortkey -x aggsortkeypos=1
SIZE ALLOC FREE DELTA
1552 16 0 16
Printing stacks
Ntfs.sys`NtfsCreateFcb+0x388
Ntfs.sys`NtfsCreateNewFile+0xaa8
Ntfs.sys`NtfsCommonCreate+0x2303
Ntfs.sys`NtfsFsdCreate+0x284
nt`IofCallDriver+0x55
FLTMGR.SYS`FltpLegacyProcessingAfterPreCallbacksCompleted+0x1b9
FLTMGR.SYS`FltpCreate+0x324
nt`IofCallDriver+0x55
nt`IoCallDriverWithTracing+0x34
nt`IopParseDevice+0x6ac
nt`ObpLookupObjectName+0x3fe
nt`ObOpenObjectByNameEx+0x1fa
nt`IopCreateFile+0x40f
nt`NtCreateFile+0x79
nt`KiSystemServiceCopyEnd+0x38
1552 3
Ntfs.sys`NtfsCreateFcb+0x388
Ntfs.sys`NtfsOpenFile+0x2d7
Ntfs.sys`NtfsCommonCreate+0x25a8
Ntfs.sys`NtfsFsdCreate+0x284
nt`IofCallDriver+0x55
FLTMGR.SYS`FltpLegacyProcessingAfterPreCallbacksCompleted+0x1b9
FLTMGR.SYS`FltpCreate+0x324
nt`IofCallDriver+0x55
nt`IoCallDriverWithTracing+0x34
nt`IopParseDevice+0x6ac
nt`ObpLookupObjectName+0x3fe
nt`ObOpenObjectByNameEx+0x1fa
nt`IopCreateFile+0x40f
nt`NtCreateFile+0x79
nt`KiSystemServiceCopyEnd+0x38
1552 4
Ntfs.sys`NtfsCreateFcb+0x388
Ntfs.sys`NtfsOpenFile+0x2d7
Ntfs.sys`NtfsCommonCreate+0x25a8
Ntfs.sys`NtfsFsdCreate+0x284
nt`IofCallDriver+0x55
FLTMGR.SYS`FltpLegacyProcessingAfterPreCallbacksCompleted+0x1b9
FLTMGR.SYS`FltpCreate+0x324
nt`IofCallDriver+0x55
nt`IoCallDriverWithTracing+0x34
nt`IopParseDevice+0x6ac
nt`ObpLookupObjectName+0x3fe
nt`ObOpenObjectByNameEx+0x1fa
nt`IopCreateFile+0x40f
nt`NtOpenFile+0x58
nt`KiSystemServiceCopyEnd+0x38
1552 3
...
== REPORT ==
0xffffd304c98dd380 => 1
0xffffd304cc4655a0 => 1
0xffffd304cccf15a0 => 1
0xffffd304ccdeb990 => 1
0xffffd304ce048760 => 1
0xffffd304cf1ee990 => 1
0xffffd304d0473010 => 1
0xffffd304d12075a0 => 1
0xffffd304d14135a0 => 1
0xffffd304d1674010 => 1
0xffffd304d33b3660 => 1
0xffffd304d3b29010 => 1
0xffffd304d42c6010 => 1
0xffffd304d48b2010 => 1
0xffffd304de1fa5f0 => 1
0xffffd304e1ad56a0 => 1
GUID 비교
DTrace는 GUID를 기본적으로 지원하지 않지만 이 샘플 코드에 표시된 대로 구조체를 만들어 작업할 수 있습니다. GUID를 비교하는 방법의 샘플은 다음과 같습니다.
nt`_GUID guidcmp;
/* Sleep After GUID: 29f6c1db-86da-48c5-9fdb-f2b67b1f44da */
dtrace:::BEGIN
{
printf("Begin\n");
guidcmp.Data1 = 0x29f6c1db;
guidcmp.Data2 = 0x86da;
guidcmp.Data3 = 0x48c5;
guidcmp.Data4[0] = 0x9f;
guidcmp.Data4[1] = 0xdb;
guidcmp.Data4[2] = 0xf2;
guidcmp.Data4[3] = 0xb6;
guidcmp.Data4[4] = 0x7b;
guidcmp.Data4[5] = 0x1f;
guidcmp.Data4[6] = 0x44;
guidcmp.Data4[7] = 0xda;
}
pid$target:PowrProf:PowerReadACValueIndexEx:entry
{
cidstr = (nt`_GUID *) (copyin(arg2, sizeof(nt`_GUID)));
printf("\nPrinting GUID to compare\n");
print(guidcmp);
printf("\nPrinting GUID received \n");
print(*cidstr);
if ( (cidstr->Data1 == guidcmp.Data1) &&
(cidstr->Data2 == guidcmp.Data2) &&
(cidstr->Data3 == guidcmp.Data3) &&
(cidstr->Data4[0] == guidcmp.Data4[0]) &&
(cidstr->Data4[1] == guidcmp.Data4[1]) &&
(cidstr->Data4[2] == guidcmp.Data4[2]) &&
(cidstr->Data4[3] == guidcmp.Data4[3]) &&
(cidstr->Data4[4] == guidcmp.Data4[4]) &&
(cidstr->Data4[5] == guidcmp.Data4[5]) &&
(cidstr->Data4[6] == guidcmp.Data4[6]) &&
(cidstr->Data4[7] == guidcmp.Data4[7]) )
{
printf("GUID matched \n");
}
else
{
printf("No match");
}
}
dtrace:::END
{
printf("End\n");
}
파일을 comparequid.d로 저장하고 -s 옵션을 사용하여 아래 표시된 매개 변수를 제공하여 테스트 스크립트를 실행합니다.
C:\Windows\system32>dtrace -s compareguid.d -c "powercfg /qh scheme_current sub_sleep standbyidle"
dtrace: script 'compareguid.d' matched 9 probes
CPU ID FUNCTION:NAME
0 1 :BEGIN Begin
Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e (Balanced)
GUID Alias: SCHEME_BALANCED
Subgroup GUID: 238c9fa8-0aad-41ed-83f4-97be242c8f20 (Sleep)
GUID Alias: SUB_SLEEP
Power Setting GUID: 29f6c1db-86da-48c5-9fdb-f2b67b1f44da (Sleep after)
GUID Alias: STANDBYIDLE
Minimum Possible Setting: 0x00000000
Maximum Possible Setting: 0xffffffff
Possible Settings increment: 0x00000001
Possible Settings units: Seconds
Current AC Power Setting Index: 0x00000708
Current DC Power Setting Index: 0x00000384
dtrace: pid 7560 has exited
0 42695 PowerReadACValueIndexEx:entry
Printing GUID to compare
struct _GUID {
UInt32 Data1 = 0x29f6c1db
UInt16 Data2 = 0x86da
UInt16 Data3 = 0x48c5
UInt8 [8] Data4 = [ 0x9f, 0xdb, 0xf2, 0xb6, 0x7b, 0x1f, 0x44, 0xda ]
}
Printing GUID received
struct _GUID {
UInt32 Data1 = 0x29f6c1db
UInt16 Data2 = 0x86da
UInt16 Data3 = 0x48c5
UInt8 [8] Data4 = [ 0x9f, 0xdb, 0xf2, 0xb6, 0x7b, 0x1f, 0x44, 0xda ]
}
0 42695 PowerReadACValueIndexEx:entry GUID matched