The case of slow Visual Studio startup


In this post I would use Perfview /ETW to diagnose the delayed start-up of visual studio.

To analyze the problem start-up VS within Perfview as a run command

This would launch visual studio and collect etw traces. I have also enabled CodeMarkers , which is ETW traces for Visual Studio in case if you want to trace any extensions performance.

perfview-mainAfter it completes I choose the CPU Stacks and filtered with devenv.exe process.

On the CPU window I choose call-tree tab which displays the threads.perfviewcpuviewThe most amount of time is spent on the start-up thread and that is what we want to zoom into.

groupedcall-stacksWhen I expanded it does not show the information and everything is grouped into OTHER which does not help me.

The reason for that is perfview groups call-stacks for better viewing. I cleared the “groupparts” textbox and then expanded the start-up thread.

actualcallstacksFrom the call-stacks I could make almost 42% of time is spent on Xamarin and DevExpress extensions within VS. Now I could turn them off and have a better performance.

Perfview is great tool for identifying where the time is being spent!

Measure GC Allocations and Collections using TraceEvent


In this post I will explore  how we could use TraceEvent to measure our code (even at function level) for GC Allocations and Collections.

Save this with “.linq” extension and then open in  linqpad.

Here is the TL;DR

Why would I want to know GC events on a function level? Doesn’t the PerfMon counter  provide that information on an application level? Isn’t Premature optimization root of all evil?

Yes, for most of the part Premature optimization is not necessary. And PerfMon GC counter’s would give answers for the whole application. But it is usually after we build the application and when we start running into performance issues we start looking at them.

The motivation behind this are two things Measure Early and Often for Performance and Essential Truths Everyone Should Know about Performance in a Large Managed Codebase

If you haven’t read or watched the above video please do it. It let’s us know why and how to do it.

In the above video Dustin talks about Roslyn code base and how they used Perfview to measure Roslyn code base and identify potential bottlenecks early to avoid Perf issues.

One of the key differences between managed code and native code with respect to performance is GC. If GC is working hard then your application might not be able to get the performance that you are expecting. GC is good but if we don’t know which calls allocate what amount of data then it is an issue. Especially if you have a section code that is hit very often and which requires a lot of Perf, it is good know where the allocations are coming from. It is not explicit always.

In the above video Dustin shows few examples of Roslyn code where they were able to identify subtle issues that could allocate a lot when you are trying to get the most out of the code.There is also Roslyn Heap Allocation Analyzer which looks at the code help us identify allocations which isn’t necessary. It is a cool project.

I took one of the examples from the video as a motivation to check if  I could measure and make it a utility in my toolbox to help me when I need one.

In the above example I am trying look for a word “pede” in the lorem ipsum text. The code could get it using “foreach” or using the “Any” operator. I would like to run this few times to check what are the allocations and how long does it take. I used LINQPad as a scratch pad.

Here is the result of GC Allocations of using “Any” for 500 iterations and NOT the foreach

GCAllocations

The were 118 allocations of Enumerator and 146 allocations Func. GC usually allocates 100K each time it allocates that’s what is shown in the allocation amount column.

And here is GC Allocations when using “foreach”

GCAllocationWithForEach

There are hardly any new allocations compared to the previous one.

Here is the GC Collections when using  “Any”

GCCollection

There were 18 GC Collections using Any.

Here it is using foreach  and there were 0 collections.

ZeorCollections

Here is measure it time duration results using Any

MeasureWithAny

And here it is using Foreach

MeasureWithForEach

Look ma I figured out why my ETW EventSource isn’t tracing


The EventSource class in the framework 4.5 helps in writing custom ETW tracing.

When using EventSource class built within the framework, if the order of the methods don’t match ordinal number position in the class it would fail generating ETW traces. The EventSource has dependency on the order of the methods in the class.

This code would produce a valid ETW traces

This one would fail producing any ETW Traces.

The difference between them are the order of the methods. If you notice in the failing ETW tracing class the FailedTraceEvent is Second and the FailedDetailedEvent is first which is causing the trace not to be generated. The actual exception text would

Event FailedDetailedEvent is givien event ID 1 but 2 was passed to WriteEvent.

It’s one of those quirks that I ran into when building ETW tracing.

How to troubleshoot these kind of failures?

By default these kind of failed ETW exceptions would not be raised to be handled by the client code. The reason being,in production if you enable ETW tracing and all of a sudden the application crashes would not be something that we would want.

To troubleshoot this, use ETW tracing for exceptions. Use ETW to trace custom ETW failures. How cool is this?  My choice of tool is Perfview.

may-3-2015-etw-exception

Within Perfview in the Events Window I enter Test|Ex in the filter text box which is shown in the above picture. FYI filter text box supports Regular expression. So by entering Test|Ex, I am filtering events with Test or Ex which for exceptions. With that information I could filter all the TestEvent’s and any exceptions that have been raised which shows ArgumentException.

The call-stack of the ArgumentException shows on the static contructor .cctor of  FailedEvent.

may-3-2015-exception-callstack

Get managed call-stacks in .NET for Registry access using ETW


I was recently debugging managed code which was accessing system registry implicitly because of an external dependent library.  So the first thing I asked was a Procmon log for of registry access. And then I also wanted look at the call-stacks for registry access, which Procmon does provide.

Here is a sample call-stack from Procmon for registry access. I am using linqpad as an example in this

ntoskrnl.exe    CmpCallCallBacks + 0x1c0    0xfffff80002c870d0
ntoskrnl.exe     ?? ::NNGAKEGL::`string’ + 0x4c81d    0xfffff80002c31116
ntoskrnl.exe    KiSystemServiceCopyEnd + 0x13    0xfffff800028d4853
ntdll.dll    ZwSetInformationKey + 0xa    0x779214aa
wow64.dll    wow64.dll + 0x2d252    0x7399d252
wow64.dll    wow64.dll + 0x1008f    0x7398008f
wow64.dll    wow64.dll + 0xcf87    0x7397cf87
wow64cpu.dll    TurboDispatchJumpAddressEnd + 0x24    0x7390276d
wow64.dll    wow64.dll + 0xd07e    0x7397d07e
wow64.dll    wow64.dll + 0xc549    0x7397c549
ntdll.dll    LdrpInitializeProcess + 0x17e2    0x779184c8
ntdll.dll     ?? ::FNODOBFM::`string’ + 0x2bea0    0x77917623
ntdll.dll    LdrInitializeThunk + 0xe    0x7790308e
ntdll.dll    NtOpenKey + 0x12    0x77acf9da
KernelBase.dll    OpenRegKey + 0x134    0x772f2ef3
KernelBase.dll    OpenAltSortsKey + 0x29    0x772de216
KernelBase.dll    IsValidLocale + 0x127    0x772e3271
clr.dll    clr.dll + 0xe4d5f    0x6be34d5f
clr.dll    clr.dll + 0xe4ba8    0x6be34ba8
mscorlib.ni.dll    mscorlib.ni.dll + 0x2ba2f1    0x697ba2f1
mscorlib.ni.dll    mscorlib.ni.dll + 0x2ba2c1    0x697ba2c1
mscorlib.ni.dll    mscorlib.ni.dll + 0x2b9faa    0x697b9faa
mscorlib.ni.dll    mscorlib.ni.dll + 0x2b9e0a    0x697b9e0a
mscorlib.ni.dll    mscorlib.ni.dll + 0x2ba15c    0x697ba15c
mscorlib.ni.dll    mscorlib.ni.dll + 0x2ba106    0x697ba106
mscorlib.ni.dll    mscorlib.ni.dll + 0x2b9db8    0x697b9db8
mscorlib.ni.dll    mscorlib.ni.dll + 0x2b9d34    0x697b9d34
clr.dll    clr.dll + 0x24a2a    0x6bd74a2a
clr.dll    clr.dll + 0x23153    0x6bd73153
clr.dll    clr.dll + 0x231cc    0x6bd731cc
clr.dll    clr.dll + 0x2323b    0x6bd7323b
clr.dll    clr.dll + 0x23415    0x6bd73415
clr.dll    clr.dll + 0x2355a    0x6bd7355a
clr.dll    clr.dll + 0x94844    0x6bde4844
<unknown>    0xb2420    0xb2420
mscorlib.ni.dll    mscorlib.ni.dll + 0x255870    0x69755870
mscorlib.ni.dll    mscorlib.ni.dll + 0x2557cd    0x697557cd
mscorlib.ni.dll    mscorlib.ni.dll + 0x255764    0x69755764
<unknown>    0x4e00d9    0x4e00d9
clr.dll    clr.dll + 0x21db    0x6bd521db
clr.dll    clr.dll + 0x24a2a    0x6bd74a2a
clr.dll    clr.dll + 0x24bcc    0x6bd74bcc
clr.dll    clr.dll + 0x24c01    0x6bd74c01
clr.dll    clr.dll + 0x24c21    0x6bd74c21
clr.dll    clr.dll + 0xece82    0x6be3ce82
clr.dll    clr.dll + 0xecf90    0x6be3cf90
clr.dll    clr.dll + 0xecda4    0x6be3cda4
clr.dll    clr.dll + 0xed199    0x6be3d199
clr.dll    clr.dll + 0xed09a    0x6be3d09a
clr.dll    clr.dll + 0x16af00    0x6bebaf00
mscoreei.dll    mscoreei.dll + 0x55ab    0x726355ab
mscoree.dll    mscoree.dll + 0x7f16    0x75077f16
mscoree.dll    mscoree.dll + 0x4de3    0x75074de3
ntdll.dll    __RtlUserThreadStart + 0x70    0x77ae9d72
ntdll.dll    _RtlUserThreadStart + 0x1b    0x77ae9d45

It is all unmanaged call-stacks , what I was looking for was managed call-stacks. The solution to this was ETW tracing. I use Perfmonitor from the BCL teams codeplex site for getting managed call-stacks.

Here are the steps to get managed call-stacks for registry access

PerfMonitor.exe /registry /stacks /lineNumbers start

Run the process and do the steps for registry access

PerfMonitor.exe /registry /stacks /lineNumbers stop
PerfMonitor.exe  /stacks /lineNumbers print

The above should produce a xml file which is by default named as PerfMonitorOutput.print.xml

And here is the managed call-stacks for registry access.

<Event MSec=  “3449.0880” PID=”3356″ PName= “LINQPad” TID=”4536″ EventName=”RegistryOpen” Status=”0x00000000″ KeyHandle=”0x00000000″ ElapsedTime=”0″ KeyName=”\Registry\Machine\System\CurrentControlSet\Control\Nls\Locale\Alternate Sorts” Index=”0″ InitialTime=”1/3/1601 1:11:43 PM”>
<StackTrace>
<CodeAddress Address=”0xfffff80002c22003″ FullMethodName=”_NULL_IMPORT_DESCRIPTOR” ModuleName=”ntoskrnl”/>
<CodeAddress Address=”0xfffff80002bb5d3b” FullMethodName=”$$VProc_ImageExportDirectory” ModuleName=”ntoskrnl”/>
<CodeAddress Address=”0xfffff800028d4853″ FullMethodName=”KiDeliverApc” ModuleName=”ntoskrnl”/>
<CodeAddress Address=”0x77920e0a” FullMethodName=”ZwOpenKeyEx” ModuleName=”ntdll”/>
<CodeAddress Address=”0x7399d1f1″ ModuleName=”wow64″/>
<CodeAddress Address=”0x7398008f” ModuleName=”wow64″/>
<CodeAddress Address=”0x7397cf87″ ModuleName=”wow64″/>
<CodeAddress Address=”0x7390276d” ModuleName=”wow64cpu”/>
<CodeAddress Address=”0x7397d07e” ModuleName=”wow64″/>
<CodeAddress Address=”0x7397c549″ ModuleName=”wow64″/>
<CodeAddress Address=”0x779184c8″ FullMethodName=”LdrpInitializeProcess” ModuleName=”ntdll”/>
<CodeAddress Address=”0x77917623″ FullMethodName=” ?? ::FNODOBFM::`string&apos;” ModuleName=”ntdll”/>
<CodeAddress Address=”0x7790308e” FullMethodName=”LdrInitializeThunk” ModuleName=”ntdll”/>
<CodeAddress Address=”0x77acf9da” FullMethodName=”NtOpenKey” ModuleName=”ntdll”/>
<CodeAddress Address=”0x772f2ef3″ ModuleName=”KernelBase”/>
<CodeAddress Address=”0x772de216″ ModuleName=”KernelBase”/>
<CodeAddress Address=”0x772e3264″ ModuleName=”KernelBase”/>
<CodeAddress Address=”0x6be34d5f” ModuleName=”clr”/>
<CodeAddress Address=”0x6be34ba8″ ModuleName=”clr”/>
<CodeAddress Address=”0x697ba2f1″ FullMethodName=”System.Globalization.CultureData.InitCultureData()” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697ba2c1″ FullMethodName=”System.Globalization.CultureData.CreateCultureData(class System.String,bool)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697b9faa” FullMethodName=”System.Globalization.CultureData.GetCultureData(class System.String,bool)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697b9e0a” FullMethodName=”System.Globalization.CultureInfo..ctor(class System.String,bool)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697ba15c” FullMethodName=”System.Globalization.CultureInfo.GetCultureByName(class System.String,bool)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697ba106″ FullMethodName=”System.Globalization.CultureInfo.InitUserDefaultCulture()” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697b9db8″ FullMethodName=”System.Globalization.CultureInfo.Init()” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697b9d34″ FullMethodName=”System.Globalization.CultureInfo..cctor()” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x6bd74a2a” ModuleName=”clr”/>
<CodeAddress Address=”0x6bd73153″ ModuleName=”clr”/>
<CodeAddress Address=”0x6bd731cc” ModuleName=”clr”/>
<CodeAddress Address=”0x6bd7323b” ModuleName=”clr”/>
<CodeAddress Address=”0x6bd73415″ ModuleName=”clr”/>
<CodeAddress Address=”0x6bd7355a” ModuleName=”clr”/>
<CodeAddress Address=”0x6bde4844″ ModuleName=”clr”/>
<CodeAddress Address=”0x122420″/>
<CodeAddress Address=”0x69755870″ FullMethodName=”System.Version.TryParseVersion(class System.String,value class VersionResult&amp;)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x697557cd” FullMethodName=”System.Version.Parse(class System.String)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x69755764″ FullMethodName=”System.Version..ctor(class System.String)” ModuleName=”mscorlib”/>
<CodeAddress Address=”0x1e00d9″ FullMethodName=”LINQPad.Loader.Main(class System.String[])” ModuleName=”LINQPad”/>
<CodeAddress Address=”0x6bd521db” ModuleName=”clr”/>
<CodeAddress Address=”0x6bd74a2a” ModuleName=”clr”/>
<CodeAddress Address=”0x6bd74bcc” ModuleName=”clr”/>
<CodeAddress Address=”0x6bd74c01″ ModuleName=”clr”/>
<CodeAddress Address=”0x6bd74c21″ ModuleName=”clr”/>
<CodeAddress Address=”0x6be3ce82″ ModuleName=”clr”/>
<CodeAddress Address=”0x6be3cf90″ ModuleName=”clr”/>
<CodeAddress Address=”0x6be3cda4″ ModuleName=”clr”/>
<CodeAddress Address=”0x6be3d199″ ModuleName=”clr”/>
<CodeAddress Address=”0x6be3d09a” ModuleName=”clr”/>
<CodeAddress Address=”0x6bebaf00″ ModuleName=”clr”/>
<CodeAddress Address=”0x726355ab” ModuleName=”mscoreei”/>
<CodeAddress Address=”0x75077f16″ ModuleName=”mscoree”/>
<CodeAddress Address=”0x75074de3″ ModuleName=”mscoree”/>
<CodeAddress Address=”0x77ae9d72″ FullMethodName=”__RtlUserThreadStart” ModuleName=”ntdll”/>
<CodeAddress Address=”0x77ae9d45″ FullMethodName=”_RtlUserThreadStart” ModuleName=”ntdll”/>
</StackTrace>
</Event>

Here I was  able to get the managed as well as native call-stacks for registry access by keyname.  And here is my list of posts on ETW

Get Managed and Native Call Stacks from Event Tracing for Windows (ETW)


Here is the TOC of series of posts on ETW

With the latest release of PerfMonitor from the CLR team now we can have Managed call stacks along with native code. How cool is that?  To get managed / native call stacks we need minimum of Vista, this is not available in XP.

Here is the sample source code I am going to be using to demonstrate the managed and the native call-stacks

using System;
using System.Linq;
namespace ConsoleApplication9 {
class Program {
private static void Main() {
Console.Read();
AllocateLOH();
}
private static void AllocateLOH() {
for (int i = 0; i < 10; i++) {
var x = new object[85000];
x.Count();
}
}
}
}

This is a very simple app that allocates large chunks of memory, which will get allocated in the LOH and frequent allocations in LOH causes high CPU. To identify this we would have to attach it to windbg and put a break-point on “WKS::gc_heap::allocate_large_object” and here I have blogged about the same

But with ETW we will not need Windbg, the ETW can do stack walks for the function calls and this makes it really great. To enable this feature first thing is to download the source and compile the code from codeplex . FYI if you are using x64 th tool would not work. It would work on wow64 , so compile it for x86.

After which run the command


PerfMonitor crawlableCLRStacks

This will take a while to complete, it is essentially setting the ngened images to debug attributes so we can get better stack walks. The next command is to start etw trace


PerfMonitor.exe  /lineNumbers /stacks /keyAny:0x1 start

So we are essentially instructing the tool to  get GC Information (/keyAny:0x1) along with stackwalk . The tool provides help which really useful.

After which I start the application and let the complete and then I stop the trace.


Perfmonitor.exe stop

And then dump the trace to a xml file


perfmonitor.exe print

This will dump out a huge file but what we are interested in Large Object Allocations on GC call stacks


<Event MSec=  "6940.8972" PID="365044" PName="ConsoleApplication9" TID="364352" EventName="GCAllocationTick" AllocationAmount="0x00053040" AllocationKind="Large" ClrInstanceID="15">

<StackTrace>

<CodeAddress Address="0x66669909" FullMethodName="ETW::SamplingLog::SendStackTrace" ModuleName="clr"/>

<CodeAddress Address="0x66669a8d" FullMethodName="EtwCallout" ModuleName="clr"/>

<CodeAddress Address="0x6669d870" FullMethodName="CoMofTemplate_qqh" ModuleName="clr"/>

<CodeAddress Address="0x666d5d44" ModuleName="clr"/>

<CodeAddress Address="0x6653ff5b" FullMethodName="WKS::gc_heap::allocate_more_space" ModuleName="clr"/>

<CodeAddress Address="0x66475d43" FullMethodName="WKS::gc_heap::allocate_large_object" ModuleName="clr"/>

<CodeAddress Address="0x666d61d8" FullMethodName="WKS::GCHeap::Alloc" ModuleName="clr"/>

<CodeAddress Address="0x666a39a7" ModuleName="clr"/>

<CodeAddress Address="0x664ef3d8" FullMethodName="AllocateArrayEx" ModuleName="clr"/>

<CodeAddress Address="0x664ef464" ModuleName="clr"/>

<CodeAddress Address="0x1f00ee" FullMethodName="ConsoleApplication9.Program.AllocateLOH()"/>

<CodeAddress Address="0x1f008e"/>

<CodeAddress Address="0x663f213b" FullMethodName="CallDescrWorker" ModuleName="clr"/>

<CodeAddress Address="0x66412c2f" FullMethodName="CallDescrWorkerWithHandler" ModuleName="clr"/>

<CodeAddress Address="0x66412dcc" FullMethodName="MethodDesc::CallDescr" ModuleName="clr"/>

<CodeAddress Address="0x66412e01" FullMethodName="MethodDesc::CallTargetWorker" ModuleName="clr"/>

<CodeAddress Address="0x66412e21" FullMethodName="MethodDescCallSite::Call_RetArgSlot" ModuleName="clr"/>

<CodeAddress Address="0x664c8872" FullMethodName="ClassLoader::RunMain" ModuleName="clr"/>

<CodeAddress Address="0x664c8980" FullMethodName="Assembly::ExecuteMainMethod" ModuleName="clr"/>

<CodeAddress Address="0x664c8794" FullMethodName="SystemDomain::ExecuteMainMethod" ModuleName="clr"/>

<CodeAddress Address="0x664ed6f5" FullMethodName="ExecuteEXE" ModuleName="clr"/>

<CodeAddress Address="0x664ed5f7" FullMethodName="_CorExeMainInternal" ModuleName="clr"/>

<CodeAddress Address="0x664e6db8" FullMethodName="_CorExeMain" ModuleName="clr"/>

<CodeAddress Address="0x704561f0" ModuleName="mscoreei"/>

<CodeAddress Address="0x706f7f16" FullMethodName="ShellShim__CorExeMain" ModuleName="mscoree"/>

<CodeAddress Address="0x706f4de3" FullMethodName="_CorExeMain_Exported" ModuleName="mscoree"/>

<CodeAddress Address="0x77bc9d72" FullMethodName="__RtlUserThreadStart" ModuleName="ntdll"/>

<CodeAddress Address="0x77bc9d45" FullMethodName="_RtlUserThreadStart" ModuleName="ntdll"/>

</StackTrace>

</Event

Here is a simple linq statement to extract the above information

 void Main()
 {
 var events = from e in XDocument.Load(@"c:\temp\test.xml").Descendants("Events").Descendants("Event").Take(5)
 where e.Attribute("EventName").Value == "GCAllocationTick" && e.Attribute("AllocationKind").Value == "Large"
 select new {Amount = Dec(e.Attribute("AllocationAmount").Value) ,
 Stack = e.Descendants("StackTrace").Descendants().Attributes("FullMethodName")};

var f = from e in events
 let stack = e.Stack.Select (s => s.Value )
 select new {AllocatedAmount = e.Amount,callstack = stack};
 }
 public static  int Dec(string hex)
 {
 var x1 = hex.Remove(0,3);
 return int.Parse(x1,System.Globalization.NumberStyles.AllowHexSpecifier);
 }
 

And here is the outcome from the linq code

IEnumerable<> (2 items)

AllocatedAmount callstack
340032
IEnumerable<String> (23 items)
ETW::SamplingLog::SendStackTrace
EtwCallout
CoMofTemplate_qqh
WKS::gc_heap::allocate_more_space
WKS::gc_heap::allocate_large_object
WKS::GCHeap::Alloc
AllocateArrayEx
ConsoleApplication9.Program.AllocateLOH()
CallDescrWorker
CallDescrWorkerWithHandler
MethodDesc::CallDescr
MethodDesc::CallTargetWorker
MethodDescCallSite::Call_RetArgSlot
ClassLoader::RunMain
Assembly::ExecuteMainMethod
SystemDomain::ExecuteMainMethod
ExecuteEXE
_CorExeMainInternal
_CorExeMain
ShellShim__CorExeMain
_CorExeMain_Exported
__RtlUserThreadStart
_RtlUserThreadStart
340032
IEnumerable<String> (23 items)
ETW::SamplingLog::SendStackTrace
EtwCallout
CoMofTemplate_qqh
WKS::gc_heap::allocate_more_space
WKS::gc_heap::allocate_large_object
WKS::GCHeap::Alloc
AllocateArrayEx
ConsoleApplication9.Program.AllocateLOH()
CallDescrWorker
CallDescrWorkerWithHandler
MethodDesc::CallDescr
MethodDesc::CallTargetWorker
MethodDescCallSite::Call_RetArgSlot
ClassLoader::RunMain
Assembly::ExecuteMainMethod
SystemDomain::ExecuteMainMethod
ExecuteEXE
_CorExeMainInternal
_CorExeMain
ShellShim__CorExeMain
_CorExeMain_Exported
__RtlUserThreadStart
_RtlUserThreadStart
680064

Wow now we can see every callstack from ntdll to CLR along with our code in callstack. How cool it is to see managed callstack and native in one single block. This wasn’t possible until now. Thanks to the CLR team. Just like I mentioned in my previous blog post we wouldn’t have to use Windbg to get this information. This is only tip of the iceberg , we can do lot of cool things with this.  I am sure Microsoft is working hard to integrate this within XPERF , but I am going to take a stab and see how I can display this data in a much more graphical way in my future posts. So next time someone complains of Performance issue in managed code you can probably ask them to just get on trace file , which will include everything to figure compared to getting  few memory dumps , perfmon counters , webserver log .

Exploring Event Tracing for Windows (ETW) within Task Parallel Library (TPL) using PerfMonitor


Like I mentioned in my previous post , Today I am going to exploring TPL ETW traces. Yes, I know we could use the built in VS.NET profiler, but in production environment we don’t have VS.NET and guess what even VS.NET parallel profiler uses only ETW to get those good looking graphs.

With the today’s release of source code from BCL, I am going to be using PerfMonitor to parse these traces and dump them out in xml file. TPL uses ‘self-manifests’ (The manifest is dumped as event data) we don’t need a man file to interpret it (since it is in the stream).   The TraceEvent code has the ability to decode these events using the manifest in the data stream.

The sample I am going to be using for tracing is MandelbrotFractals_CSharp which is from the PFX Team

I am starting the trace using xperf , on the TPL ETW provider


xperf -start clr -on 2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5 -f clrevents.etl

Started the application and then stopped the trace using


xperf -stop clr

I had to make few changes to the TraceEvent Code, these are minor . Especially the code didn’t handle Boolean and it was throwing an exception , anybody can make this simple fix.

To get the trace from the ETL to xml here is the command


PerfMonitor.exe /keepETL print clrevents.etl

In the above command we are instructing the exe to convert the etl to xml and not to delete the ETL file after doing it.

The above command output’s an xml file. Here is a sample output from the actual xml file.


<Event MSec= "24181.0409" PID="1635144" PName="(1635144)" TID="1634556" EventName="TaskStarted" ProviderName="TplEtwProvider" OriginatingTaskSchedulerID="1" OriginatingTaskID="0" TaskID="5"/>

<Event MSec= "24181.0734" PID="1635144" PName="(1635144)" TID="1634556" EventName="TaskScheduled" ProviderName="TplEtwProvider" OriginatingTaskSchedulerID="1" OriginatingTaskID="5" TaskID="6" CreatingTaskID="4" TaskCreationOptions="10500"/>

<Event MSec= "24181.0772" PID="1635144" PName="(1635144)" TID="1634556" EventName="ParallelFork" ProviderName="TplEtwProvider" OriginatingTaskManager="1" OriginatingTaskID="5" ForkJoinContextID="1"/>

<Event MSec= "24413.3543" PID="1635144" PName="(1635144)" TID="1634556" EventName="ParallelJoin" ProviderName="TplEtwProvider" OriginatingTaskSchedulerID="1" OriginatingTaskID="5" ForkJoinContextID="1"/>

<Event MSec= "24413.3624" PID="1635144" PName="(1635144)" TID="1634556" EventName="TaskScheduled" ProviderName="TplEtwProvider" OriginatingTaskSchedulerID="1" OriginatingTaskID="5" TaskID="7" CreatingTaskID="4" TaskCreationOptions="10500"/>

<Event MSec= "24413.3791" PID="1635144" PName="(1635144)" TID="1634556" EventName="TaskCompleted" ProviderName="TplEtwProvider" OriginatingTaskSchedulerID="1" OriginatingTaskID="0" TaskID="5" IsExceptional="False"/>

From the above we can see when the task was scheduled, which is the originating task and when did it complete.

This wouldn’t have been possible without ETW, even with Windbg we cannot these information. This can help us understand what is happening with in TPL , which will enable us to design better.

FYI, Perfmonitor is just an implementation of the TraceEvent API which converts the ETW data to xml. It gives us possibilities to extend this.

In the coming posts I will be exploring how ETW can literally replace debugger in Production environment.   Don’t get me wrong, I am very BIG fan of Windbg and have been using it to understand the OS and the CLR.

Thanks to Vance Morrison for letting me know about TraceEvent.

Undocumented Event Tracing for Windows (ETW) providers in .NET 4.0 (BCL)


Continuing with my explorations in ETW I happened notice that there are few undocumented ETW providers within the .NET 4.0 BCL. These are in the mscorlib.dll and they are

  1. System.Collections.Concurrent.CDSCollectionETWBCLProvider : This is used by the ConcurrentBag , ConcurrentDictionary and the ConcurrentStack
  2. System.Diagnostics.Eventing.FrameworkEventSource : This is used by System.Resources.ResourceManager
  3. System.Threading.CdsSyncEtwBCLProvider : This is used by SpinWait, SpinLock and Barrier
  4. System.Threading.TplEtwProvider  & System.Linq.Parallel.PlinqEtwProvider : This is already documented here

To get list of ETW providers on an application ,get pid of the application and then issue this command


logman query providers -pid 1853452

The outcome of the above command is

For example .from above result ,the provider 2e5dba47-a3d2-4d16-8ee0-6671ffdcd7b5 belongs to TplEtwProvider , it is not a registered with a man file. These providers that i have mentioned above are specifically useful in diagnosing parallel applications. In the forth coming posts I will demonstrate how we can leverage ETW in  understanding parallel applications performance.