<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>ETW on Welcome to Christophe Nasarre's Blog</title><link>https://chrisnas.github.io/tags/etw/</link><description>Recent content in ETW on Welcome to Christophe Nasarre's Blog</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Fri, 28 Sep 2018 00:00:00 +0000</lastBuildDate><atom:link href="https://chrisnas.github.io/tags/etw/index.xml" rel="self" type="application/rss+xml"/><item><title>Monitor Finalizers, contention and threads in your application</title><link>https://chrisnas.github.io/posts/2018-09-28_monitor-finalizers-contention-threads/</link><pubDate>Fri, 28 Sep 2018 00:00:00 +0000</pubDate><guid>https://chrisnas.github.io/posts/2018-09-28_monitor-finalizers-contention-threads/</guid><description>This post of the series details more complicated CLR events related to finalizers and threading.</description><content:encoded><![CDATA[<p>Part 1: <a href="/posts/2018-06-19_replace-net-performance-counters/">Replace .NET performance counters by CLR event tracing</a>.</p>
<p>Part 2: <a href="/posts/2018-07-26_grab-etw-session-providers/">Grab ETW Session, Providers and Events</a>.</p>
<h2 id="introduction">Introduction</h2>
<p>In the previous post, you saw how the TraceEvent nuget helps you deciphering simple ETW events such as the one emitted when a first chance exception happens. Most situations trigger more than one event and could make their processing more complicated.</p>
<h2 id="who-said-finalizer">Who said Finalizer?</h2>
<p>In the early days of .NET, you might had to deal with native resources that you were responsible for cleaning up with the related unmanaged API or legacy COM component. It was a best practice to implement a ~finalizer method to ensure that everything was deleted the right way. These times are over for most of us now. If you don’t have an <strong>IntPtr</strong> field in your class, chances are that you don’t need a ~finalizer method.</p>
<p>The <a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-dispose?WT.mc_id=DT-MVP-5003325">Microsoft documentation about IDisposable/Finalizer</a> often leads people to implement both even though only <strong>IDisposable</strong> is needed (i.e. some fields of the class implement <strong>IDisposable</strong>). Having a large number of finalizers could impact memory consumption by having objects staying alive for a longer time and maybe even increase garbage collection total duration. Last but not least, some finalizers code outside of your code base could “block” on locks during their cleanup and… drastically slow down everything else.</p>
<p>Getting the name of these types with TraceEvent is a two steps process. First, a <strong>TypeBulkType</strong> event is received: it contains a list of <strong>GCBulkTypeValues</strong> which binds a <strong>TypeID</strong> integer to a string type name:</p>
<p><strong>OnTypeBulkType.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">OnTypeBulkType</span><span class="p">(</span><span class="n">GCBulkTypeTraceData</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">ProcessID</span> <span class="p">!=</span> <span class="n">_processId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="c1">// keep track of the id/name type associations</span>
</span></span><span class="line"><span class="cl">   <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">currentType</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">currentType</span> <span class="p">&lt;</span> <span class="n">data</span><span class="p">.</span><span class="n">Count</span><span class="p">;</span> <span class="n">currentType</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="n">GCBulkTypeValues</span> <span class="k">value</span> <span class="p">=</span> <span class="n">data</span><span class="p">.</span><span class="n">Values</span><span class="p">(</span><span class="n">currentType</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="n">_types</span><span class="p">[</span><span class="k">value</span><span class="p">.</span><span class="n">TypeID</span><span class="p">]</span> <span class="p">=</span> <span class="k">value</span><span class="p">.</span><span class="n">TypeName</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">   <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>This association is needed because when a finalizer is notified via the <strong>GCFinalizeObject</strong> event, the received data only contains the type ID:</p>
<p><strong>OnGCFinalizeObject.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">OnGCFinalizeObject</span><span class="p">(</span><span class="n">FinalizeObjectTraceData</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">ProcessID</span> <span class="p">!=</span> <span class="n">_processId</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="c1">// the type id should have been associated to a name via a previous TypeBulkType event</span>
</span></span><span class="line"><span class="cl">   <span class="n">NotifyFinalize</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">TimeStamp</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">ProcessID</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">TypeID</span><span class="p">,</span> <span class="n">_types</span><span class="p">[</span><span class="n">data</span><span class="p">.</span><span class="n">TypeID</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Note that in the two code snippets, there is an explicit check to keep the events only from the process we are interested in: as explained in part 1, this is needed for older versions of Windows.</p>
<h2 id="thread-contention-duration">Thread contention duration</h2>
<p>With .NET CLR LocksAndThreads “Contention Rate / sec” and “Total # of Contentions” performance counters, you can monitor how many times threads have been blocked while waiting for a lock owned by another thread. However, you don’t know for how long. The two TraceEvent <strong>ContentionStart</strong> and <strong>ContentionStop</strong> events allow you to get this crucial piece of information.</p>
<p>As their names imply and <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/contention-etw-events?WT.mc_id=DT-MVP-5003325">the corresponding documentation explains</a>, these two events let you know respectively when a thread starts to wait on a lock and when the lock has been acquired. In addition to the process and thread identifiers, the <strong>ContentionTraceData</strong> event argument gives you the type of contention with its <strong>ContentionFlags</strong> property: either managed or native</p>
<p><strong>ContentionTraceData.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">sealed</span> <span class="k">class</span> <span class="nc">ContentionTraceData</span> <span class="p">:</span> <span class="n">TraceEvent</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="kd">public</span> <span class="n">ContentionFlags</span> <span class="n">ContentionFlags</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Since contention is a per-thread waiting operation, you need to keep track of the starting time on a per-thread basis when <strong>ContentionStart</strong> happens.</p>
<p><strong>OnContentionStart.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">OnContentionStart</span><span class="p">(</span><span class="n">ContentionTraceData</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">ContentionInfo</span> <span class="n">info</span> <span class="p">=</span> <span class="n">_contentionStore</span><span class="p">.</span><span class="n">GetContentionInfo</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">ProcessID</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">ThreadID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">info</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="n">info</span><span class="p">.</span><span class="n">TimeStamp</span> <span class="p">=</span> <span class="n">data</span><span class="p">.</span><span class="n">TimeStamp</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">   <span class="n">info</span><span class="p">.</span><span class="n">ContentionStartRelativeMSec</span> <span class="p">=</span> <span class="n">data</span><span class="p">.</span><span class="n">TimeStampRelativeMSec</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <strong>ContentionStore</strong> class keeps track of the monitored processes and assign them a <strong>ContentionInfo</strong> instance where the contention details are stored.</p>
<p>Now you retrieve it back when the matching <strong>ContentionStop</strong> event occurs. The rest is just a matter of computing the time difference between the two events based on their <strong>TimeStampRelativeMSec</strong> property.</p>
<p><strong>OnContentionStop.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="k">void</span> <span class="n">OnContentionStop</span><span class="p">(</span><span class="n">ContentionTraceData</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">ContentionInfo</span> <span class="n">info</span> <span class="p">=</span> <span class="n">_contentionStore</span><span class="p">.</span><span class="n">GetContentionInfo</span><span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">ProcessID</span><span class="p">,</span> <span class="n">data</span><span class="p">.</span><span class="n">ThreadID</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">info</span> <span class="p">==</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="c1">// unlucky case when we start to listen just after the ContentionStart event</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">info</span><span class="p">.</span><span class="n">ContentionStartRelativeMSec</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="kt">var</span> <span class="n">contentionDurationMSec</span> <span class="p">=</span> <span class="n">data</span><span class="p">.</span><span class="n">TimeStampRelativeMSec</span> <span class="p">-</span> <span class="n">info</span><span class="p">.</span><span class="n">ContentionStartRelativeMSec</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">   <span class="kt">var</span> <span class="n">isManaged</span> <span class="p">=</span> <span class="p">(</span><span class="n">data</span><span class="p">.</span><span class="n">ContentionFlags</span> <span class="p">==</span> <span class="n">ContentionFlags</span><span class="p">.</span><span class="n">Managed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>You are now able to detect when thread contention occurs but also if the contention duration increases over time.</p>
<p><img loading="lazy" src="/posts/2018-09-28_monitor-finalizers-contention-threads/0_SEfZiyEmSjrA-UGR.png"></p>
<p>If you want to test contention, here is the kind of code you could use:</p>
<p><strong>TestContention.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">_workers</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Task</span><span class="p">[</span><span class="n">workerCount</span><span class="p">];</span>
</span></span><span class="line"><span class="cl"><span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span> <span class="n">i</span> <span class="p">&lt;</span> <span class="n">workerCount</span><span class="p">;</span> <span class="n">i</span><span class="p">++)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">_workers</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="p">=</span> <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(</span><span class="kd">async</span> <span class="p">()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">      <span class="p">{</span>
</span></span><span class="line"><span class="cl">         <span class="k">lock</span> <span class="p">(</span><span class="n">_lock</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">         <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">Thread</span><span class="p">.</span><span class="n">Sleep</span><span class="p">(</span><span class="m">5</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">         <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">   <span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>A few tasks are created to acquire the same lock over and over and sleeping 5 milliseconds before releasing it.</p>
<h2 id="how-to-count-threads-or-monitor-the-threadpool-usage">How to count threads or monitor the ThreadPool usage?</h2>
<p>In <a href="/posts/2018-06-19_replace-net-performance-counters/">a previous post</a>, it was mentioned that CLR performance counters related to threads were not able to provide an accurate count of the running threads. In fact, you could use the <strong>Process/Thread Count</strong> Windows kernel performance counter to get the accurate value. If you build .NET Core applications to run on Linux, you have to find other ways such as described <a href="https://stackoverflow.com/questions/268680/how-can-i-monitor-the-thread-count-of-a-process-on-linux">on stackoverflow</a>. However, there is an easy programmatic way to get the number of running threads in an application that works both on Windows and Linux: call <strong>Process.GetProcessById(<pid>).Threads.Count</strong> with its process ID.</p>
<p>Since this is a series dedicated to ETW, you would expect to simply listen to a few events to get the thread count. Well… It is almost that simple. Each time a thread gets started, the <strong>AppDomainResourceManagement/ThreadCreated</strong> event is emitted with basically the ID of the created thread as payload. In order to receive the sibling <strong>AppDomainResourceManagement/ThreadTerminated</strong> event, you need to call <strong>AppDomain.MonitoringIsEnabled</strong> in the monitored application. The <a href="https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/app-domain-resource-monitoring?WT.mc_id=DT-MVP-5003325">other ways described by the documentation</a> did not work for me.</p>
<p>If you want to figure out if your applications are not hammering too much the .NET thread pool, the CLR provides <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/thread-pool-etw-events?WT.mc_id=DT-MVP-5003325">many ETW events</a> for you to listen that map to the following TraceEvent events:</p>
<p><img loading="lazy" src="/posts/2018-09-28_monitor-finalizers-contention-threads/1_l13vhZSphdzzg1RQdQW8Fg.png"></p>
<p>The <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/thread-pool-etw-events#threadpoolworkerthreadadjustmentadjustment?WT.mc_id=DT-MVP-5003325">ThreadPoolWorkerThreadAdjustementAdjustment</a> event (there is no typo in this name) provides a <strong>Reason</strong> property. If its value is 0x06, then it means <strong>Starvation</strong>: if this event frequency is ~1 per second, it could be a good indication that the <strong>ThreadPool</strong> is receiving a burst of workitems or tasks to process. In addition, the <strong>ThreadPoolWorkerThreadAdjustmentTraceData</strong> argument received by the handler also gives the count of threads via the <strong>NewWorkerThreadCount</strong> property.</p>
<p>With all these events, you should be able to monitor how the .NET <strong>ThreadPool</strong> is used in your application.</p>
<p>The next post will be entirely dedicated to garbage collection analysis.</p>
<hr>
<p><em>Co-authored with <a href="https://twitter.com/kookiz">Kevin Gosse</a></em></p>
]]></content:encoded></item><item><title>Grab ETW Session, Providers and Events</title><link>https://chrisnas.github.io/posts/2018-07-26_grab-etw-session-providers/</link><pubDate>Thu, 26 Jul 2018 00:00:00 +0000</pubDate><guid>https://chrisnas.github.io/posts/2018-07-26_grab-etw-session-providers/</guid><description>This post of the series shows how to easily listen to CLR events with the TraceEvent package.</description><content:encoded><![CDATA[<p>Part 1: <a href="/posts/2018-06-19_replace-net-performance-counters/">Replace .NET performance counters by CLR event tracing</a>.</p>
<p>In the previous post, you saw that the CLR is emitting traces that could (should?) replace the performance counters you are using to monitor your application and investigate when something goes wrong. The perfview tool that was demonstrated is built on top of the <a href="https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.TraceEvent">Microsoft.Diagnostics.Tracing.TraceEvent Nuget package</a> and you should leverage it to build your own monitoring system. In addition, the <a href="https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.TraceEvent.Samples/">Microsoft.Diagnostics.Tracing.TraceEvent.Samples Nuget package</a> contains sample code to help you ramping up.</p>
<h2 id="manage-an-etw-session">Manage an ETW session</h2>
<p>Create a console application and add the TraceEvent Nuget package. Your project now contains a TraceEvent.ReadMe.txt and a more detailed _TraceEventProgrammersGuide.docx Word document. You should really take the time to read the latter: it describes the architecture in great details and helps understanding what is going on under the scene.</p>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_32rDLO5VyR1XMDS0.png"></p>
<p>In the <strong>Main</strong> entry point, add the following code to list existing ETW sessions:</p>
<p><strong>ShowETWSessions.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Current ETW sessions:&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">foreach</span><span class="p">(</span><span class="kt">var</span> <span class="n">session</span> <span class="k">in</span> <span class="n">TraceEventSession</span><span class="p">.</span><span class="n">GetActiveSessionNames</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="n">session</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;--------------------------------------------&#34;</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Like <strong>logman -ets</strong> command, this piece of code might be handy during debugging session. Why? Just because when you debug your code that creates an ETW session and if you stop the debugger before disposing it, the session becomes orphan and after a while, Windows simply refuses to create new ones. In addition to easily find your orphans sessions, another good reason to give a meaningful name to your session is to be able to stop it. Type the following command line: <strong>logman -ets stop <session name></strong> to close a running session and clean up the mess your debugging sessions might have created. This is definitively better than rebooting the machine.</p>
<p>The next step is to create a session. You get a <strong>TraceEventSession</strong> object either by attaching to an existing session or by creating a new one as shown in this code:</p>
<p><strong>MainETW.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kt">string</span> <span class="n">sessionName</span> <span class="p">=</span> <span class="s">&#34;EtwSessionForCLR_&#34;</span> <span class="p">+</span> <span class="n">Guid</span><span class="p">.</span><span class="n">NewGuid</span><span class="p">().</span><span class="n">ToString</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;Starting {sessionName}...\r\n&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="k">using</span> <span class="p">(</span><span class="n">TraceEventSession</span> <span class="n">userSession</span> <span class="p">=</span> <span class="k">new</span> <span class="n">TraceEventSession</span><span class="p">(</span><span class="n">sessionName</span><span class="p">,</span> <span class="n">TraceEventSessionOptions</span><span class="p">.</span><span class="n">Create</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">Task</span><span class="p">.</span><span class="n">Run</span><span class="p">(()</span> <span class="p">=&gt;</span>
</span></span><span class="line"><span class="cl">   <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// register handlers for events on the session source</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// more on this later...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// decide which provider to listen to with filters if needed</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// process the events in a blocking call</span>
</span></span><span class="line"><span class="cl">   <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="c1">// wait for the user to dismiss the session</span>
</span></span><span class="line"><span class="cl">   <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;Presse ENTER to exit...&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">   <span class="n">Console</span><span class="p">.</span><span class="n">ReadLine</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Why is it necessary to run the code that manipulates the session in another thread with <strong>Task.Run</strong>? The call to the <strong>Process</strong> method is synchronous so it would not be possible to get the user input. When the user exits, the session is disposed and the <strong>Process</strong> method returns. If you close the session with logman, the <strong>Process</strong> method will also return. This behavior applies because the <strong>TraceEventSession.StopOnDispose</strong> property is set to true by default.</p>
<p>Note that if you want to use TraceEvent to parse an .etl file, you simply need to pass the filename as an additional parameter to the <strong>TraceEventSession</strong> constructor; the rest of the code will be the same.</p>
<p>The code running in the task first registers handlers to the events as you will soon see. Next, you need to enable the providers you are interested in receiving events from. In our case, only the ClrTraceEventParser.ProviderGuid CLR provider is enabled (read <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-providers?WT.mc_id=DT-MVP-5003325">https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-providers</a> for more details about the two available CLR providers). In addition to the <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels#etw-event-levels">verbosity level</a>, you should set the keywords with the ClrTraceEventParser.Keywords enumeration values corresponding to <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels?WT.mc_id=DT-MVP-5003325">the categories of events you want to receive</a></p>
<p><strong>ProviderAndSource.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// register handlers for events on the session source</span>
</span></span><span class="line"><span class="cl"><span class="c1">// more on this later...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// decide which provider to listen to with filters if needed</span>
</span></span><span class="line"><span class="cl"><span class="n">userSession</span><span class="p">.</span><span class="n">EnableProvider</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">   <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">ProviderGuid</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="n">TraceEventLevel</span><span class="p">.</span><span class="n">Verbose</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="p">(</span><span class="kt">ulong</span><span class="p">)(</span>
</span></span><span class="line"><span class="cl">      <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">Keywords</span><span class="p">.</span><span class="n">Contention</span> <span class="p">|</span> <span class="c1">// thread contention timing</span>
</span></span><span class="line"><span class="cl">      <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">Keywords</span><span class="p">.</span><span class="n">Threading</span> <span class="p">|</span> <span class="c1">// threadpool events</span>
</span></span><span class="line"><span class="cl">      <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">Keywords</span><span class="p">.</span><span class="n">Exception</span> <span class="p">|</span> <span class="c1">// get the first chance exceptions</span>
</span></span><span class="line"><span class="cl">      <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">Keywords</span><span class="p">.</span><span class="n">GCHeapAndTypeNames</span> <span class="p">|</span> 
</span></span><span class="line"><span class="cl">      <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">Keywords</span><span class="p">.</span><span class="n">Type</span> <span class="p">|</span> <span class="c1">// for finalizer and exceptions type names</span>
</span></span><span class="line"><span class="cl">      <span class="n">ClrTraceEventParser</span><span class="p">.</span><span class="n">Keywords</span><span class="p">.</span><span class="n">GC</span> <span class="c1">// garbage collector details</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// this is a blocking call until the session is disposed</span>
</span></span><span class="line"><span class="cl"><span class="n">userSession</span><span class="p">.</span><span class="n">Source</span><span class="p">.</span><span class="n">Process</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">&#34;End of session&#34;</span><span class="p">);</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>Additional filters such as which process to monitor can be set by passing TraceEventProviderOptions to the <strong>EnableProvider()</strong> method. But wait! On a Windows 7 machine this kind of filtering is not working and you get events from all processes… No specific documentation to look at… This is where knowing what Win32 APIs are called behind the scene could help. Instead of decompiling the TraceEvent assembly, you should instead take a look at its implementation… because it is <a href="https://github.com/Microsoft/perfview/blob/master/src/TraceEvent">open sourced</a> with Perfview! The <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels?WT.mc_id=DT-MVP-5003325">Microsoft documentation</a> for the called <strong>EnableTraceEx2</strong> function states that <em>there are several types of scope filters that allow filtering based on the event ID, the process ID (PID), executable filename, the app ID, and the app package name.</em> <strong>This feature is supported on Windows 8.1,Windows Server 2012 R2, and later</strong>. If you need to filter out events based on process id, don’t worry: each event will provide it.</p>
<h2 id="listen-to-clr-events">Listen to CLR events</h2>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_Oe3Dr70NMSBEJp40.png"></p>
<p>This class derives from <strong>TraceEventDispatcher</strong> that provides the <strong>Process</strong> method. The <strong>TraceEventSource</strong> ancestor class is where the event handlers can be registered on the following properties:</p>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_TogfxrDCOMI4maqI.png"></p>
<p>Behind the scene, each provider emits strongly typed traces that could be difficult to parse manually: don’t worry, the TraceEvent library does the job for you through dedicated parsers exposed by <strong>TraceEventSource</strong>.</p>
<p>In case of .NET events, you usually rely on the <strong>ClrTraceEventParser</strong> that exposes via .NET event the 100+ different traces emitted by the CLR ETW provider… plus one called <strong>All</strong> just in case you want to see all of them.</p>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_Kp9Q3s1tAIzeLjin.png"></p>
<p>Here is the first naïve implementation to display all CLR traces as shown in the previous screenshot:</p>
<p><strong>NaiveListener.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="c1">// listen to all CLR events</span>
</span></span><span class="line"><span class="cl"><span class="n">userSession</span><span class="p">.</span><span class="n">Source</span><span class="p">.</span><span class="n">Clr</span><span class="p">.</span><span class="n">All</span> <span class="p">+=</span> <span class="k">delegate</span> <span class="p">(</span><span class="n">TraceEvent</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="c1">// skip verbose and unneeded events</span>
</span></span><span class="line"><span class="cl">   <span class="k">if</span> <span class="p">(</span><span class="n">SkipEvent</span><span class="p">(</span><span class="n">data</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">      <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">   <span class="c1">// raw dump of the events</span>
</span></span><span class="line"><span class="cl">   <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{data.ProcessID,7} &lt;{data.ProviderName}:{data.ID}&gt;__[{data.OpcodeName}] {data.EventName} &lt;| {data.GetType().Name}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The <strong>SkipEvent</strong> method is here just for you to filter out traces… and this is very helpful to remove meaningless noise when you are beginning to work with CLR traces and you need to see which events are generated.</p>
<p>Each trace payload is received as a generic <strong>TraceEvent</strong> object that exposes common properties:</p>
<ul>
<li><strong>ProcessID</strong>/<strong>ProcessName</strong>: information related to the process in which the trace has been emitted by the CLR</li>
<li><strong>ThreadID</strong>: numeric identifier of the thread from which the trace was sent</li>
<li><strong>ID</strong>: numeric identifier of the trace that helps you find the corresponding event in the Microsoft documentation (i.e. <em>Event ID</em>)</li>
<li><strong>OpcodeName</strong>: human readable name of the trace</li>
<li><strong>EventName</strong>: concatenation of task (= group such as “Contention” corresponding to the <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-keywords-and-levels?WT.mc_id=DT-MVP-5003325">Keyword</a> mentioned earlier) and <strong>OpcodeName</strong> separated by ‘/’</li>
</ul>
<h2 id="but-what-are-the-events-to-listen-to">But… what are the events to listen to?</h2>
<p>Son, the next big step is to learn which events are interesting for you to monitor. I would suggest you read the rest of this post before going to <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-events?WT.mc_id=DT-MVP-5003325">the Microsoft documentation</a> that describes all CLR events in details.</p>
<p>Let’s start with the simplest case: one trace is emitted when an exception is thrown. Microsoft <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/exception-thrown-v1-etw-event?WT.mc_id=DT-MVP-5003325">documents theExceptionThrown_V1</a> event with <strong>ExceptionKeyword</strong> and <strong>Warning</strong> verbosity level:</p>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_EX3lpKl42pBhyeXo.png"></p>
<p>Unfortunately, there is no <strong>ExceptionThrown_V1</strong> event at the <strong>ClrTraceEventParser</strong> level:</p>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_G4sPofzTLca9w_vl.png"></p>
<p>In fact, the ID property of the received traces maps the “Event ID” of the documentation so the <strong>ExceptionStart</strong> event happens to bring the same level of information as <strong>ExceptionThrown_V1</strong> via the <strong>ExceptionTraceData</strong> parameter passed to the handler:</p>
<p><img loading="lazy" src="/posts/2018-07-26_grab-etw-session-providers/0_OFHBgCPrdThmZHHR.png"></p>
<p>The corresponding handler implementation is straightforward:</p>
<p><strong>ExceptionStartHandler.cs</strong></p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="cl"><span class="kd">private</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">OnExceptionStart</span><span class="p">(</span><span class="n">ExceptionTraceData</span> <span class="n">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="n">Console</span><span class="p">.</span><span class="n">WriteLine</span><span class="p">(</span><span class="s">$&#34;{data.EventName} --&gt; {data.ExceptionType} : {data.ExceptionMessage}&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The interesting properties are <strong>ExceptionType</strong> for the name of the thrown exception and <strong>ExceptionMessage</strong> for its message. Note that if the exception contains inner exceptions, you know it by taking a look at the <strong>ExceptionFlags</strong> property but you can’t access them. Remember that you should have received earlier the trace corresponding to the inner exception when it was thrown so you are just missing the relationship between the two.</p>
<p>If for an unknown reason the number of exceptions raises for one of your applications, listening to this event is a very cheap way to know which exceptions are thrown and search for them into the source code!</p>
<p>The next episode will detail other important CLR traces you need to monitor your applications and even start an investigation.</p>
<hr>
<p><em>Co-authored with <a href="https://twitter.com/kookiz">Kevin Gosse</a></em></p>
]]></content:encoded></item><item><title>Replace .NET performance counters by CLR event tracing</title><link>https://chrisnas.github.io/posts/2018-06-19_replace-net-performance-counters/</link><pubDate>Tue, 19 Jun 2018 00:00:00 +0000</pubDate><guid>https://chrisnas.github.io/posts/2018-06-19_replace-net-performance-counters/</guid><description>This post of our new series shows why performance counters might not be the best solution to monitor your .NET application and why the CLR events will definitively be a better solution.</description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>At Criteo, each .NET application provides custom metrics to monitor deviation and trigger alerts. This is the first line of defense against misbehaviors. The next step is to figure out what could be the cause of these deviations. After source code changes analysis, it is often needed to dig deeper into performance counters exposed by the CLR such as the following:</p>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_nufe9ma4xSKydxmH.png"></p>
<p>Again, these counters are used to detect possible deviations in usual patterns. For example, some applications are supposed to answer under a 50 ms threshold. When the corresponding “number of timeouts” or “request time” metrics start to increase, several reasons linked to the CLR might be partially responsible but it is hard to tell:</p>
<ul>
<li><em># of Exceps Thrown / sec</em>: if the number increases, it is possible that performance is impacted but how to get the list of these unusual first chance exceptions caught by the applications?</li>
<li><em>Contention rate / sec</em>: an increase might suggest that threads are spending more time waiting for locks to be released but for how many milliseconds? This information is not available among the performance counters.</li>
<li><em>#Gen 2 Collections</em>: even if this counter does vary a lot, how could we be sure that blocking gen2 collections are responsible for the lack of responsiveness: no counter exposes how many milliseconds the applications threads were frozen in case of a compacting collection.</li>
</ul>
<p>As you can see, this second line of defense is not enough to start an investigation with a clear assumption in mind.</p>
<p>In addition, some counters are not showing what you might think:</p>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_qIKHUDB0aOR13eG9.png"></p>
<ul>
<li><em># Gen <n> Collections</em>: gen0 counter is also incremented after gen1 and gen2 collection, gen1 counter is also incremented after gen2 collection. There is no counter for the exact count of per generation collection trigger even though you could compute their value.</li>
</ul>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_1bvyQwbWXVljrTiZ.png"></p>
<ul>
<li><em># of current logical/physical threads</em>: it is not possible to make any link with the number of threads used by the thread pool or the TPL/Tasks. As you can see in the <a href="https://github.com/dotnet/coreclr/blob/release/1.0.0/src/vm/threads.cpp">early versions of the Core CLR</a> (i.e. where the performance counters code was not removed yet), the increment and decrement of counts do not seem thread safe: that could explain some issues (always less threads than expected in our monitoring boards) we faced in the past.</li>
</ul>
<p>You need to realize that performance counters are sampling-based and could show the same value that does not represent the current reality. For example, most of the GC counters will not change until the next garbage collection occurs; i.e. the <em>% Time in GC</em> could stay misleading (until the next GC) and this is very far from the % CPU time you are used to.</p>
<p>If you are moving to .NET Core, you will discover a worse situation: <strong>There are</strong> <strong>no more performance counters on Windows</strong> <strong>to monitor your applications</strong> And if you are targeting Linux… well…</p>
<p>However, as the rest of the articles will demonstrate, the CLR provides even more details via strongly typed tracing through Event Tracing for Windows (ETW) and LTTng on Linux.</p>
<h2 id="clr-and-event-tracing-for-windows">CLR and Event Tracing for Windows</h2>
<p>The ETW framework has existed for a long long time and allows consumers to listen to events emitted by producers as explained in <a href="http://download.microsoft.com/download/3/A/7/3A7FA450-1F33-41F7-9E6D-3AA95B5A6AEA/MSDNMagazineApril2007en-us.chm">a 2007 MSDN Magazine article</a>.</p>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_TxC5sfAh5Mfguhxn.png"></p>
<p>A tracing session wraps the providers and consumers together either for real-time processing or .etl file generation. If you already know <a href="https://github.com/microsoft/perfview/releases">the Perfview tool</a> or the <a href="https://docs.microsoft.com/en-us/windows-hardware/test/wpt?WT.mc_id=DT-MVP-5003325">Windows Performance Toolkit/xperf</a>, you should be familiar with the generated .etl files.</p>
<p>For debugging purpose, listening to events during a live session with a minimal impact on production machines is even more practical. The <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/clr-etw-events?WT.mc_id=DT-MVP-5003325">list of documented CLR events</a> is huge but no fear: this series of posts will focus on exceptions, finalizers, thread contention and garbage collection.</p>
<p>In addition to the documentation, I would recommend that you take a look at how the tracing is implemented in <a href="https://github.com/dotnet/coreclr/tree/master/src">.NET Core source code</a> for two reasons. First, you get access to the <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/ClrEtwAll.man">exact payload schema</a> of all generated events (even those not documented). Second, by searching the Core CLR source code for the FireEtw-prefixed methods generated at build time, you will get a better understanding of when things are happening. Don’t forget the higher level <a href="https://github.com/dotnet/coreclr/blob/master/src/vm/eventtrace.cpp">ETW::-prefixed methods and enums</a> that are also called by the runtime to emit traces.</p>
<p>Perfview has already been mentioned earlier to help analyzing traces but it is also useful for deciphering events produced by the CLR. On a trace, double-click the <strong>Events</strong> node:</p>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_i4-ZIxUxo8nxFTT3.png"></p>
<p>In the new window that pops up, select an event on the left side to get the list of occurrences on the right side:</p>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_djIq3H9GgyYmtnB7.png"></p>
<p>Right-click an event occurrence and select <strong>Dump Event</strong> to get its payload details:</p>
<p><img loading="lazy" src="/posts/2018-06-19_replace-net-performance-counters/0_BwE09NSYG7JXLjND.png"></p>
<p>such as the type name of the new instance that led the GC to trigger the <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/garbage-collection-etw-events#gcallocationtick_v2_event?WT.mc_id=DT-MVP-5003325">AllocationTick event</a> after ~100KB was allocated.</p>
<p>In addition to <a href="https://docs.microsoft.com/en-us/dotnet/framework/performance/controlling-logging?WT.mc_id=DT-MVP-5003325">the tooling available</a> to get the traces, Microsoft is providing <a href="https://www.nuget.org/packages/Microsoft.Diagnostics.Tracing.TraceEvent/">theMicrosoft.Diagnostics.Tracing.TraceEventNuget package</a>. With this library, you will be able to build your own tool or listen to the CLR events from within your running applications to replace the performance counters. The next episode of the series will ramp you up with the implementation of a basic listener.</p>
<hr>
<p><em>Co-authored with <a href="https://twitter.com/kookiz">Kevin Gosse</a></em></p>
]]></content:encoded></item></channel></rss>