[Debugging Toolbox] Extracting Performance Monitor counters from .NET application
글 : Roberto Alexis Farah
번역 : 이태화
ASP.NET 에서 발생한 dump를 분석하면서 Performance Monitor log 를 얻는 것을 잊어버린던 경험이 있을 겁니다.
여러분이 이런 상황에 처해 있다면 제가 뉴스를 알려 드리겠습니다. 이 script 는ASP.NET Process 또는 Dump 에서 정보를 수집하여.NET Performance Monitor counter
들을 보여줍니다.
이 것은 제가 가지고 있는 좋은 script 중 하나 입니다.
PS : 전체 Module 에서 특정 값을 계산 한다던지 할때 유용한 방법일 것 같습니다.
screenshots:
설명:
lm1m - 모듈의 이름만을 얻을 수 있는 명령 .foreach 에서 사용됨
$sicmp - stricmp 와 같은 함수
${obj}!PerfCounters::m_pPrivatePerf - Module!Class::Member 를 접근하는 군요
as GCCounters .block - block 전체를 별명으로 설정해서 클릭으로 실행되게 하는군요
Source code for GET_PERFMON.TXT:
$$
$$ =============================================================================
$$ Get Performance Monitor counters from mscorsvr.dll or mscorwks.dll.
$$
$$ Compatibility: Win32.
$$
$$ Usage: $$>< to run the program.
$$
$$ Requirements: Public symbols.
$$
$$ If necessary change the filename below to include your path and filename.
$$ By default it uses the WinDbg path and the default file name is GET_PERFMON.TXT
$$
$$ Roberto Alexis Farah
$$ Blog: https://blogs.msdn.com/debuggingtoolbox/
$$
$$ All my scripts are provided "AS IS" with no warranties, and confer no rights.
$$ =============================================================================
$$
r @$t0 = 0;
r @$t1 = 0;
ad /q *
.block
{
as ScriptName MYSCRIPTS\\GET_PERFMON.TXT
}
.foreach(obj { lm1m })
{
.if((0 == $sicmp("${obj}", "mscorsvr")) | (0 == $sicmp("${obj}", "mscorwks")))
{
r @$t0 = ${obj}!PerfCounters::m_pPrivatePerf;
}
}
.if(0 == @$t0)
{
.printf /D " \nThis is not a .NET application!\n";
}
.else
{
.block
{
.block
{
as GCCounters .block
{
r @$t1 = poi(@$t0) + 0x4;
.printf "\n.NET GC Counters\n\n";
.printf "GenCollection 0 = 0n%d\n", poi(@$t1);
.printf "GenCollection 1 = 0n%d\n", poi(@$t1+0x4);
.printf "GenCollection 2 = 0n%d\n", poi(@$t1+0x8);
.printf "PromotedMemory = 0n%d\n", poi(@$t1+0xc);
.printf "PromotedMemory 1 = 0n%d\n", poi(@$t1+0x10);
.printf "PromotedFinalizationMem 0 = 0n%d\n", poi(@$t1+0x14);
.printf "Process ID = 0n%d\n", poi(@$t1+0x18);
.printf "GenHeapSize 0 = 0n%d\n", poi(@$t1+0x1c);
.printf "GenHeapSize 1 = 0n%d\n", poi(@$t1+0x20);
.printf "GenHeapSize 2 = 0n%d\n", poi(@$t1+0x24);
.printf "TotalCommittedBytes = 0n%d\n", poi(@$t1+0x28);
.printf "TotalReservedBytes = 0n%d\n", poi(@$t1+0x2c);
.printf "LargeObjectSize = 0n%d\n", poi(@$t1+0x30);
.printf "SurviveFinalize = 0n%d\n", poi(@$t1+0x34);
.printf "Handles = 0n%d\n", poi(@$t1+0x38);
.printf "Alloc = 0x%x\n", poi(@$t1+0x3c);
.printf "LargeAlloc = 0x%x\n", poi(@$t1+0x40);
.printf "InducedGCs = 0n%d\n", poi(@$t1+0x44);
.printf "TimeInGC = 0n%d\n", poi(@$t1+0x48);
.printf "TimeInGCBase = 0n%d\n", poi(@$t1+0x4c);
.printf "PinnedObjects = 0n%d\n", poi(@$t1+0x50);
.printf "SinkBlocks = 0n%d\n\n", poi(@$t1+0x54);
}
}
.block
{
as InteropCounters .block
{
r @$t1 = poi(@$t0) + 0x74;
.printf "\n.NET Interop Counters\n\n";
.printf "CCW = 0n%d\n", poi(@$t1);
.printf "Stubs = 0n%d\n", poi(@$t1+0x4);
.printf "Marshalling = 0n%d\n", poi(@$t1+0x8);
.printf "TLBImports = 0n%d\n", poi(@$t1+0xc);
.printf "TLBExports = 0n%d\n\n", poi(@$t1+0x10);
}
}
.block
{
as LoadingCounters .block
{
r @$t1 = poi(@$t0) + 0x88;
.printf "\n.NET Loading Counters\n\n";
.printf "Current ClassesLoaded = 0n%d\n", poi(@$t1);
.printf "Total ClassesLoaded = 0n%d\n", poi(@$t1+0x4);
.printf "Current AppDomains = 0n%d\n", poi(@$t1+0x8);
.printf "Total AppDomains = 0n%d\n", poi(@$t1+0xc);
.printf "Current Assemblies = 0n%d\n", poi(@$t1+0x10);
.printf "Total Assemblies = 0n%d\n", poi(@$t1+0x14);
.printf "Time Loading = 0n%d\n", poi(@$t1+0x18);
.printf "AsmSearchLen = 0n%d\n", poi(@$t1+0x20);
.printf "Total LoadFailures = 0n%d\n", poi(@$t1+0x24);
.printf "LoaderHeapSize = 0n%d\n", poi(@$t1+0x28);
.printf "AppDomainsUnloaded = 0n%d\n\n", poi(@$t1+0x2c);
}
}
.block
{
as ExceptionCounters .block
{
r @$t1 = poi(@$t0) + 0xb8;
.printf "\n.NET Exception Counters\n\n";
.printf "Total Exceptions = 0n%d\n", poi(@$t1);
.printf "Filters Executed = 0n%d\n", poi(@$t1+0x4);
.printf "Finallys Executed = 0n%d\n", poi(@$t1+0x8);
.printf "ThrowToCatchStackDepth = 0n%d\n\n", poi(@$t1+0xc);
}
}
.block
{
as LockAndThreadCounters .block
{
r @$t1 = poi(@$t0) + 0xc8;
.printf "\n.NET Locks and Threads Counters\n\n";
.printf "Total Contention = 0n%d\n", poi(@$t1);
.printf "QueueLength Current = 0n%d\n", poi(@$t1+0x4);
.printf "QueueLength Total = 0n%d\n", poi(@$t1+0x8);
.printf "CurrentThreadsLogical = 0n%d\n", poi(@$t1+0xc);
.printf "CurrentThreadsPhysical = 0n%d\n", poi(@$t1+0x10);
.printf "RecognizedThreads Current = 0n%d\n", poi(@$t1+0x14);
.printf "RecognizedThreads Total = 0n%d\n\n", poi(@$t1+0x18);
}
}
.block
{
as JITCounters .block
{
r @$t1 = poi(@$t0) + 0xe4;
.printf "\n.NET JIT Counters\n\n";
.printf "Methods Jitted = 0n%d\n", poi(@$t1);
.printf "IL Jitted Current = 0n%d\n", poi(@$t1+0x4);
.printf "IL Jitted Total = 0n%d\n", poi(@$t1+0x8);
.printf "JIT Failures = 0n%d\n", poi(@$t1+0xc);
.printf "Time In JIT = 0n%d\n", poi(@$t1+0x10);
.printf "Time In JIT Base = 0n%d\n\n", poi(@$t1+0x14);
}
}
.block
{
as SecurityCounters .block
{
r @$t1 = poi(@$t0) + 0xfc;
.printf "\n.NET Security Counters\n\n";
.printf "Total RunTime Checks = 0n%d\n", poi(@$t1);
.printf "Time Authorize = 0n%d\n", poi(@$t1+0x4);
.printf "Link Checks = 0n%d\n", poi(@$t1+0xc);
.printf "Time RunTime Checks = 0n%d\n", poi(@$t1+0x10);
.printf "Time RunTime Checks Base = 0n%d\n", poi(@$t1+0x14);
.printf "Stack Walk Depth = 0n%d\n\n", poi(@$t1+0x18);
}
}
}
.printf /D "<${ScriptName}\"> .NET GC Counters\n\n"
.printf /D "<${ScriptName}\"> .NET Interop Counters\n\n"
.printf /D "<${ScriptName}\"> .NET Loading Counters\n\n"
.printf /D "<${ScriptName}\"> .NET Exception Counters\n\n"
.printf /D "<${ScriptName}\"> .NET LocksAndThreads Counters\n\n"
.printf /D "<${ScriptName}\"> .NET JIT Counters\n\n"
.printf /D "<${ScriptName}\"> .NET Security Counters\n\n"
}