{"id":451,"date":"2015-11-20T17:58:00","date_gmt":"2015-11-20T17:58:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/maoni\/2015\/11\/20\/are-you-glad\/"},"modified":"2021-09-30T14:00:49","modified_gmt":"2021-09-30T21:00:49","slug":"are-you-glad","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/are-you-glad\/","title":{"rendered":"Are you GLAD?"},"content":{"rendered":"<p><span style=\"font-family: verdana,geneva\">Holidays are almost upon us. Last year around Christmas time I wrote a set of GC ETW blog entries to help with diagnosing GC heap related issues. This year I want to discuss taking that a (big) step further and making an SDK that does the analysis for you using these ETW events &#8211; so before I leave for vacation I&rsquo;d like to share with you the plan I have&nbsp;for the SDK.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">The SDK is called <strong><span style=\"color: #0070c0;font-size: small\"><span style=\"color: #0070c0;font-size: small\">GLAD<\/span><\/span><\/strong><span style=\"font-size: small\"> which stands for <\/span><strong><span style=\"color: #0070c0;font-size: small\"><span style=\"color: #0070c0;font-size: small\">G<\/span><\/span><\/strong><span style=\"font-size: small\">C <\/span><strong><span style=\"color: #0070c0;font-size: small\"><span style=\"color: #0070c0;font-size: small\">L<\/span><\/span><\/strong><span style=\"font-size: small\">atency <\/span><strong><span style=\"color: #0070c0;font-size: small\"><span style=\"color: #0070c0;font-size: small\">A<\/span><\/span><\/strong><span style=\"font-size: small\">nalysis and <\/span><strong><span style=\"color: #0070c0;font-size: small\"><span style=\"color: #0070c0;font-size: small\">D<\/span><\/span><\/strong><span style=\"font-size: small\">iagnostics. This will essentially be based on the analysis and diagnostics work for the GC we have in PerfView today but I wanted to isolate it out and make it into an SDK so everyone can benefit from it. And it will be open sourced on github &#8211; we will continue our development on GLAD there; and everyone else is welcome to contribute to it.<\/span><\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">We will start GLAD off by converting our analysis part into this form. The diagnostics will come after. The difference between the two is analysis shows you info on each GC while diagnostics actually looks at the histogram of the GCs and tells you what issues it found.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">I should note that I am not an API person so I will not take offence if you tell me my API design sucks,&nbsp;but at the same time I am asking you to help me out and suggest something better <span style=\"font-size: x-small\">\ud83d\ude42<\/span><\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">I would greatly appreciate your feedback on it so please feel free to comment (or if you are simply GLAD to see it, you can say just that too!). I will look at all the comments around Christmas (seems like an appropriate time to talk about this kind of stuff).<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: verdana,geneva;font-size: large\">Motivation<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">It&rsquo;s certainly not a trivial task to look at the GC behavior. Historically we&rsquo;ve built this knowledge in our own tool, PerfView, which takes ETW traces and produces insights in the GC behavior. PerfView is used by many teams internally at Microsoft. However, we recognize that some teams already have or are adding their own ETW processing pipeline. Obviously they would like to add GC pauses as part of their ETW analysis. Interpreting GC ETW events however presents a challenge for them. For example, even accounting for GC pauses, which is a basic and fundamental metric, is often incorrectly done due to the complexity introduced by Background GC. Therefore we would like to make the code available for anyone who would want to incorporate the GC behavior analysis into their own ETW processing.<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: verdana,geneva;font-size: large\">Background<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">For those of you who are not familiar with how the workflow for analysis with ETW events usually look like, let me use PerfView as an example.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">After you collect a trace (you can do this with PerfView as I mentioned in my ETW blog entries, or you could use WPA which is another Microsoft tool that&rsquo;s built by the OS folks, or something else of your choice), you need to convert the events from the binary form into a human consumable form. In order for this to happen you need to know the event layout. The GC ETW events&rsquo; layout is documented <a href=\"https:\/\/msdn.microsoft.com\/en-us\/library\/ff356162(v=vs.110).aspx\"><span style=\"text-decoration: underline\"><span style=\"color: #0563c1;font-size: small;text-decoration: underline\"><span style=\"color: #0563c1;font-size: small;text-decoration: underline\">here<\/span><\/span><\/span><\/a><span style=\"font-size: small\">. For example, the GCStart version 1 event has the following fields along with their types and explanation:<\/span><\/span><\/p>\n<p><span style=\"font-family: courier new,courier\">Count:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UInt32 (the GC index)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier\">Depth:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UInt32 (which generation)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier\">Reason:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UInt32 (why was this GC triggered)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier\">Type:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UInt32 (is this a non concurrent GC, a foreground or a background GC?)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier\">ClrInstanceID: UInt16 (you can ignore this for this discussion)<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">Also for each ETW, by default, there&rsquo;s a bunch of information already available to you such as the timestamp when this event was fired.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">We have a library that does exactly that for you &ndash; it&rsquo;s called <a href=\"http:\/\/blogs.msdn.com\/b\/dotnet\/archive\/2013\/08\/15\/announcing-traceevent-monitoring-and-diagnostics-for-the-cloud.aspx\"><span style=\"text-decoration: underline\"><span style=\"color: #0563c1;font-size: small;text-decoration: underline\"><span style=\"color: #0563c1;font-size: small;text-decoration: underline\">TraceEvent<\/span><\/span><\/span><\/a><span style=\"font-size: small\"> &#8211; it knows the event layouts (including a bunch of OS events, CLR events, asp.net events and etc) and as it encounters each event it will convert it to a type that you can consume in your code. So when it sees a GCStart event, it will construct a type that has exactly those fields that you can read; and it also gives you a chance to do some event processing. So let&rsquo;s say I want to write in my log everytime I see a GC start and end with their timestamps and the process it happened in:<\/span><\/span><span style=\"font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ ETWTraceEventSource is what knows how to convert a .etl file to<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ the human readable event form.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">ETWTraceEventSource source = <span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">new<\/span><\/span><\/span> ETWTraceEventSource(<span style=\"color: #a31515\"><span style=\"color: #a31515\"><span style=\"color: #a31515\">&#8220;myTrace.etl&#8221;<\/span><\/span><\/span>);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><br \/><span style=\"color: #008000\">\/\/ Clr is a provider that TraceEvent knows about which represents,<\/span><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ you guess it, the CLR ETW provider. It has a bunch of events defined<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ like GCStart and GCStop.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">source.Clr.GCStart += <span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">delegate<\/span><\/span><\/span>(GCStartTraceData data)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><br \/><span style=\"color: #008000;font-family: courier new,courier\">&nbsp;&nbsp;&nbsp; \/\/ Since GC is per process, we&#8217;d like note down the process ID as well.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; myLog.WriteLine(<span style=\"color: #a31515\"><span style=\"color: #a31515\"><span style=\"color: #a31515\">&#8220;Observed in process {0} GC#{1} started at {2}ms&#8221;<\/span><\/span><\/span>, <\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data.ProcessID, data.Count, data.TimeStampRelativeMSec);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">source.Clr.GCStop +=&nbsp; <span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">delegate<\/span><\/span><\/span>(GCEndTraceData data)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"color: #008000\"><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ Since GC is per process, we&#8217;d like note down the process ID as well<\/span><span style=\"font-family: courier new,courier;font-size: small\">.<\/span><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; myLog.WriteLine(<span style=\"color: #a31515\"><span style=\"color: #a31515\"><span style=\"color: #a31515\">&#8220;Observed in process {0} GC ended at {1}ms&#8221;<\/span><\/span><\/span>, <\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data.ProcessID, data.TimeStampRelativeMSec);<strong><br \/><\/strong><\/span><span style=\"font-family: courier new,courier;font-size: small\">};<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">(note that this code sample is based on the current TraceEvent)<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">GLAD essentially replaces the bold lines with much more interesting processing &ndash; it would provide rich info on each GC such as time managed threads were paused, promoted size, reasons why we decided to collect this generation, fragmentation, pinning, time for marking from different sources and etc.<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: verdana,geneva;font-size: large\">Details<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">The SDK will consist of the following:<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">1) The implementation of a set of action delegates that correspond to each event where each delegate takes one parameter which is the event data (that&rsquo;s what we&rsquo;ll be processing for that event).<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">2) A list of GCInformation for each GC we processed.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">Normally we don&rsquo;t care about partial GCs, ie, a GC that we don&rsquo;t have the full sequence of events for. There are 2 ways the user can recognize a complete GC:<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">1) via events &ndash; when we have processed a full sequence of events for a GC, we fire an event (not to be confused with an ETW event! This is just a c# event);<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">2) via checking for the isComplete field in GCInformation. The user can do this in the last ETW event in the sequence (which is the GCHeapStats event). <em>After he invoked our delegate to process this event<\/em>, isComplete is set to true unless we did not see the full sequence. Not seeing the full sequence usually happens at the beginning of the trace. Occasionally I&rsquo;ve also seen that we miss an event in this sequence.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">User could use this for real-time or post processing. During real-time processing, the user could choose to perform some action when he sees an &#8220;interesting&#8221; GC, eg, when a GC is just completed, he checks and sees that the duration is &gt;1s, he then stops his ETW collection and saves the events to a file for someone to investigate.<\/span><\/p>\n<p><span style=\"color: #800080;font-family: verdana,geneva;font-size: medium\">Input<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">Users will provide the SDK with the event data to interpret. If you use TraceEvent you automatically get the event data; if you choose to implement your own you need to implement the interfaces that are defined in the contracts. So I would strongly encourage you to just use TraceEvent.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">The following events are mandatory<\/span><\/p>\n<p><span style=\"color: #800080;font-family: courier new,courier\">Process Start\/Stop\/DCStart\/DCStop events from the Kernel provider<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">GC is per process which means there&rsquo;s per process state we&rsquo;d like to keep. These events give us the process info such as their PIDs and names.<\/span><\/p>\n<p><span style=\"color: #800080;font-family: courier new,courier\">GC informational level events from CLR public\/private providers<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">The following events are optional<\/span><\/p>\n<p><span style=\"color: #800080;font-family: courier new,courier\">GC verbose level events from CLR public\/private providers<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">If these events are present, more analysis will light up.<\/span><\/p>\n<p><span style=\"color: #800080;font-family: verdana,geneva;font-size: medium\">API<\/span><\/p>\n<p><span style=\"color: #0000ff\"><strong><span style=\"font-family: verdana,geneva\">Contracts <\/span><\/strong><\/span><span style=\"font-family: verdana,geneva;font-size: small\"><span style=\"font-size: small\">(gc-event-contracts.dll (name is subject to change))<\/span><\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: verdana,geneva\">The event layout definition<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\">interface<\/span> IGCStartTraceData<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp;&nbsp;int<\/span><\/span> Count { get; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; GCReason Reason { get; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; int<\/span><\/span> Depth { get; }<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ &#8230;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">interface <\/span><span style=\"font-family: courier new,courier;font-size: small\">IGCEndTraceData<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; int<\/span><\/span> Count { get; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; int<\/span><\/span> Depth { get; }<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ &#8230;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">Currently the layout definitions are in TraceEvent. In order to have both TraceEvent.dll (or some other tool that listens to ETW events) and the GLAD SDK refer to these event layouts, we need a common definition. TraceEvent.dll will implement getting the fields of events and GLAD will refer to these fields in order to calculate the GC information.<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: verdana,geneva\">The event handlers in the parser<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">We need to expose the definition of event handlers to allow GLAD to hook up all the events it processes (we don&rsquo;t want users to get a partially processed set which causes the GC information to not be filled in fully).<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">interface <\/span><span style=\"font-family: courier new,courier;font-size: small\">IGCEventParser<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; event Action&lt;IGCStartTraceData&gt; GCStart;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; event Action&lt;IGCEndTraceData&gt; GCEnd;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ other event handlers.<\/span><\/p>\n<p>\u3000<span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp; \/\/ this invokes the actual processing of the events<\/span><\/p>\n<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">bool<\/span><\/span> Process(); <\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva;font-size: small\">These are in its own dll so TraceEvent.dll and glad.dll can both reference it.<\/span><\/p>\n<p><span style=\"color: #0000ff\"><strong><span style=\"font-family: verdana,geneva;font-size: small\">Implementation <\/span><\/strong><\/span><span style=\"font-family: verdana,geneva;font-size: small\">(glad.dll (name is subject to change))<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva;font-size: small\">The following sections are for implementation and will be in glad.dll.<\/span><\/p>\n<p><strong><span style=\"font-family: verdana,geneva;font-size: small\">1) Delegates for ETW event processing<\/span><\/strong><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">\/\/ GC*Data describes the ETW event layout.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">delegate <\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">void<\/span><\/span> OnGCStart(IGCStartTraceData data);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">delegate <\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">void<\/span><\/span> OnGCEnd(IGCEndTraceData data);<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">\/\/ delegates for other events.<\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #008080\"><span style=\"color: #008080\"><br \/><\/span><\/span><\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">class <\/span><span style=\"font-family: courier new,courier;font-size: small\">GCProcessor<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"color: #008000\"><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ user&nbsp;can overwrite this.<\/span><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; <\/span><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">virtual<\/span><\/span> OnGCStartHandler(IGCStartTraceData data);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">virtual<\/span><\/span> OnGCEndHandler(IGCEndTraceData data);<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ other events.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><strong><span style=\"font-family: verdana,geneva\">2) The list of GCInformation <\/span><\/strong><span style=\"font-family: verdana,geneva\">that describes the processed info for all the GCs in a particular process.<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">GCInformation allows the user to attach additional info for each GC. This is useful internally for us to experiment with certain things before we publish it in GLAD. Eg, we are doing some additional processing, we can iterate on that some on our side; when it&rsquo;s all tested we take it out of userData and publish it.<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ This is the info for a particular GC<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">class <\/span><span style=\"font-family: courier new,courier;font-size: small\">GCInformation<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span>&nbsp;<\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">bool<\/span><\/span> isComplete;<\/span>&nbsp;&nbsp;&nbsp;<\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">int<\/span><\/span> gcIndex;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp;&nbsp;public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">double<\/span><\/span> durationMSec;<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ other GC fields<\/span><\/p>\n<p><span style=\"color: #008000\"><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ User might want to hang some info off of each GC.<\/span><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public<\/span><\/span> object userData;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">\/\/ This is the GCs for that particular process<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">class<\/span>&nbsp;<span style=\"font-family: courier new,courier;font-size: small\">GCInformationPerProcess<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">int<\/span><\/span> processID;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp;&nbsp;public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">int<\/span><\/span> currentGCIndex;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp;&nbsp;public<\/span><\/span> List&lt;GCInformation&gt; listGCInfo;<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ &#8230;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><span style=\"font-family: verdana,geneva\"><span style=\"font-family: Consolas;font-size: x-small\"><span style=\"font-family: Consolas;font-size: x-small\"><br \/><\/span><\/span><strong><strong><\/strong><\/strong><\/span><\/p>\n<p><span style=\"font-family: verdana,geneva;font-size: small\"><strong>3) The class for the user to invoke GLAD&rsquo;s processing.<\/strong><\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">class <\/span><span style=\"font-family: courier new,courier;font-size: small\">GCProcessor<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">virtual<\/span><\/span> OnGCStartHandler(IGCStartTraceData data);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">virtual<\/span><\/span> OnGCEndHandler(IGCEndTraceData data);<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ event handers for other events.<\/span><\/p>\n<p>\u3000<\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ hooks up all events that we process.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public<\/span><\/span> GCProcessor(IGCEventParser p)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p.GCStart += OnGCStartHandler;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p.GCEnd += OnGCEndHandler;<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ other events.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ If you are not interested in processing any of the GC events, you<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ can simply get the results after processing.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public<\/span><\/span> List&lt;GCInformationPerProcess&gt; GetResults()<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ return the list of processes with their GC info.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ If you are interested in additional processing, you can get the<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; \/\/ current GC and do your processing there.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public<\/span><\/span> GCInformation GetCurrentGC(<span style=\"color: #0000ff\"><span style=\"color: #0000ff\">int<\/span><\/span> ProcessID)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ returns the current GC in this process.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva;font-size: small\"><strong>4) Event to notify users of a GC sequence processing completion<\/strong><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ When we have processed the full sequence for a GC, we fire an event.<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ sender would be of type GCInformationPerProcess.<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">delegate<\/span>&nbsp;<span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">void<\/span><\/span><\/span> GCCompletionEventHandler(<span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">object<\/span><\/span><\/span> sender, <span style=\"color: #2b91af\"><span style=\"color: #2b91af\"><span style=\"color: #2b91af\">EventArgs<\/span><\/span><\/span> e);<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">class <\/span><span style=\"color: #2b91af;font-family: courier new,courier;font-size: small\"><span style=\"color: #2b91af\"><span style=\"color: #2b91af\">GCProcessor<\/span><\/span><\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #2b91af\"><span style=\"color: #2b91af\"><span style=\"color: #2b91af\"><br \/><\/span><\/span><\/span><\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span>&nbsp;&nbsp;&nbsp;<\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public <\/span><\/span><\/span><span style=\"color: #0000ff\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">event <\/span><\/span><\/span><span style=\"color: #2b91af\"><span style=\"color: #2b91af\"><span style=\"color: #2b91af\">EventHandler<\/span><\/span><\/span> OnGCSequenceCompletedHandler;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: verdana,geneva;font-size: large\">User scenarios<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva;font-size: small\">1) User doesn&rsquo;t do any processing of GC events on his own. But he&rsquo;s interested in getting the GC completion notification via our event.<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ ****in GLAD implementation****<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ in the GCHeapStats ETW event processor<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">currentGC.isComplete = <\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">true<\/span><\/span>;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">OnGCSequenceCompletedHandler (GCInformationPerProcess, null);<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ ****in user code****<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">void <\/span><span style=\"font-family: courier new,courier;font-size: small\">GCCompletionEventHandler(object sender, EventArgs e)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; GCInformationPerProcess gcProcess = sender as GCInformationPerProcess;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; GCInformation currentGC = gcProcess.listGCInfo[currentGCIndex];<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; myLog.WriteLine(&#8220;Process {0}: GC {1} took {2}ms&#8221;, gcProcess.processID, currentGC.gcIndex, currentGC.durationMSec);<\/span><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; if<\/span><\/span> (current.durationMSec &gt; 1000)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ stop collecting.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ source is the event parser.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">GCProcessor gcProcessor = <\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">new<\/span><\/span> GCProcessor(source);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">gcProcessor.OnGCSequenceCompletedHandler += GCCompletionEventHandler;<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">source.Process();<\/span><\/p>\n<p><span style=\"font-family: verdana,geneva\">2) User wants to do some processing with selected GC events.<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">\/\/ ****in user code****<\/span><\/p>\n<p><span style=\"color: #0000ff;font-family: courier new,courier;font-size: small\">class<\/span>&nbsp;<span style=\"font-family: courier new,courier;font-size: small\">MyGCProcessor : GCProcessor<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">{<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp; public<\/span><\/span> MyGCProcessor (IGCEventParser p) : base (p)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">&nbsp;&nbsp;&nbsp;&nbsp;public<\/span><\/span> override <span style=\"color: #0000ff\"><span style=\"color: #0000ff\">void<\/span><\/span> OnGCStart(IGCStartTraceData data)<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; {<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ Note for doing additional processing, you always need to call<\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ base event handler first. This is important.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; base.OnGCStart(data);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GCInformation gcInfo = GetCurrentGC(data.ProcessID);<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gcInfo.userData = <\/span><span style=\"font-family: courier new,courier;font-size: small\"><span style=\"color: #0000ff\"><span style=\"color: #0000ff\">new<\/span><\/span> MyGCInformation();<\/span><span style=\"font-family: courier new,courier;font-size: small\"><br \/><\/span><\/p>\n<p><span style=\"color: #008000;font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; \/\/ do some additional processing here.<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">&nbsp;&nbsp;&nbsp; }<\/span><\/p>\n<p><span style=\"font-family: courier new,courier;font-size: small\">}<\/span><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Holidays are almost upon us. Last year around Christmas time I wrote a set of GC ETW blog entries to help with diagnosing GC heap related issues. This year I want to discuss taking that a (big) step further and making an SDK that does the analysis for you using these ETW events &#8211; so [&hellip;]<\/p>\n","protected":false},"author":3542,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[3011,108],"class_list":["post-451","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-maoniposts","tag-performance"],"acf":[],"blog_post_summary":"<p>Holidays are almost upon us. Last year around Christmas time I wrote a set of GC ETW blog entries to help with diagnosing GC heap related issues. This year I want to discuss taking that a (big) step further and making an SDK that does the analysis for you using these ETW events &#8211; so [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/451","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/3542"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=451"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/451\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}