<?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>Windbg on Welcome to Christophe Nasarre's Blog</title><link>https://chrisnas.github.io/tags/windbg/</link><description>Recent content in Windbg on Welcome to Christophe Nasarre's Blog</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Thu, 29 Jun 2017 15:04:00 +0000</lastBuildDate><atom:link href="https://chrisnas.github.io/tags/windbg/index.xml" rel="self" type="application/rss+xml"/><item><title>ClrMD Part 5 – How to use ClrMD to extend SOS in WinDBG</title><link>https://chrisnas.github.io/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/</link><pubDate>Thu, 29 Jun 2017 15:04:00 +0000</pubDate><guid>https://chrisnas.github.io/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/</guid><description>This fifth post of the ClrMD series shows how to leverage this API inside a WinDBG…</description><content:encoded><![CDATA[<p>This fifth post of the ClrMD series shows how to leverage this API inside a WinDBG extension. The <a href="https://github.com/criteo/criteo-dotnet-blog/tree/master/ClrMD-Part5_WinDBG-Extension">associated code</a> allows you to translate a task state into a human readable value.</p>
<p>Part 1: <a href="/posts/2017-02-21_clrmd-part-1-going-beyond/">Bootstrap ClrMD to load a dump</a>.</p>
<p>Part 2: <a href="/posts/2017-03-24_clrmd-part-2-from-clrruntime/">Find duplicated strings with ClrMD heap traversing</a>.</p>
<p>Part 3: <a href="/posts/2017-05-03_clrmd-part-3-static-instance-fields/">List timers by following static fields links</a>.</p>
<p>Part 4: <a href="/posts/2017-05-31_clrmd-part-4-timer-callbacks/">Identify timers callback and other properties</a>.</p>
<h3 id="introduction">Introduction</h3>
<p>Since the beginning of this series, you have seen how to use ClrMD to write your own tool to extract meaningful information from a dump file (or a live process). However, most of the time, you are also using WinDBG and SOS to navigate inside the .NET data structures.</p>
<p>It would be convenient if you could leverage the new .NET exploration features based on ClrMD the same way you are using SOS. This post will explain how to achieve this goal by implementing an extension that exports commands callable from within WinDBG.</p>
<h3 id="deciphering-a-task-status">Deciphering a Task status</h3>
<p>During one of our debugging investigations, we needed to get the value of the <strong>Status</strong> property for a few <strong>Task</strong> instances. If you take a look at the implementation of the property getter in a decompiler (or from <a href="https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs">source code</a>), you will see that it is computed based on the value of the internal <strong>m_stateFlags</strong> field.</p>
<p>In WinDBG, the <strong>!DumpHeap -stat</strong> command lists all types with their instance count. If the <strong>.prefer_dml 1</strong> command has been set, you even get hyperlinks on some values such as the address or MT (for MethodTable). If you click the MT value for <strong>System.Threading.Tasks.Task</strong>, you get all instances of type <strong>Task</strong>:</p>
<p><img loading="lazy" src="/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/TasksAddresses.png"></p>
<p>Click any address and look at the value of the <strong>m_stateFlags</strong> field:</p>
<p><img loading="lazy" src="/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/TaskStateFlags.png"></p>
<p>It is easy to automate the retrieval of the <strong>m_stateFlags</strong> instance field value with ClrMD as explained <a href="/posts/2017-05-03_clrmd-part-3-static-instance-fields/">earlier</a>:</p>
<p><strong>GetTaskStateFromAddress.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><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</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="kt">ulong</span> <span class="n">GetTaskStateFromAddress</span><span class="p">(</span><span class="kt">ulong</span> <span class="n">address</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="kt">var</span> <span class="n">type</span> <span class="p">=</span> <span class="n">Runtime</span><span class="p">.</span><span class="n">GetHeap</span><span class="p">().</span><span class="n">GetObjectType</span><span class="p">(</span><span class="n">address</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">type</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">&amp;&amp;</span> <span class="p">(</span><span class="n">type</span><span class="p">.</span><span class="n">Name</span><span class="p">.</span><span class="n">StartsWith</span><span class="p">(</span><span class="s">&#34;System.Threading.Task&#34;</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">// try to get the m_stateFlags field value</span>
</span></span><span class="line"><span class="cl">        <span class="n">ClrInstanceField</span> <span class="n">field</span> <span class="p">=</span> <span class="n">type</span><span class="p">.</span><span class="n">GetFieldByName</span><span class="p">(</span><span class="s">&#34;m_stateFlags&#34;</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">field</span> <span class="p">!=</span> <span class="kc">null</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="kt">var</span> <span class="n">val</span> <span class="p">=</span> <span class="n">field</span><span class="p">.</span><span class="n">GetValue</span><span class="p">(</span><span class="n">address</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">val</span> <span class="p">!=</span> <span class="kc">null</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">try</span>
</span></span><span class="line"><span class="cl">                <span class="p">{</span>
</span></span><span class="line"><span class="cl">                    <span class="k">return</span> <span class="p">(</span><span class="kt">ulong</span><span class="p">)(</span><span class="kt">int</span><span class="p">)</span><span class="n">val</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">catch</span> <span class="p">(</span><span class="n">InvalidCastException</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><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="k">return</span> <span class="m">0</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>ClrType</strong> corresponding to the address is first checked to ensure that it represents a <strong>Task</strong> instance. Next, its <strong>GetFieldByname</strong> helper method returns a <strong>ClrInstanceField</strong> that provides the status via its <strong>GetValue</strong> function.</p>
<p>The next step is to transform this number into a <strong>TaskStatus</strong> enumeration value by simply using a decompiler and copying the logic from the <strong>Task</strong> getter code:</p>
<p><strong>GetTaskState.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></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="kt">string</span> <span class="n">GetTaskState</span><span class="p">(</span><span class="kt">ulong</span> <span class="n">flag</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">TaskStatus</span> <span class="n">rval</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_FAULTED</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">Faulted</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_CANCELED</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">Canceled</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_RAN_TO_COMPLETION</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">RanToCompletion</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_WAITING_ON_CHILDREN</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">WaitingForChildrenToComplete</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_DELEGATE_INVOKED</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">Running</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_STARTED</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">WaitingToRun</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">((</span><span class="n">flag</span> <span class="p">&amp;</span> <span class="n">TASK_STATE_WAITINGFORACTIVATION</span><span class="p">)</span> <span class="p">!=</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">WaitingForActivation</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="n">flag</span> <span class="p">==</span> <span class="m">0</span><span class="p">)</span> <span class="n">rval</span> <span class="p">=</span> <span class="n">TaskStatus</span><span class="p">.</span><span class="n">Created</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="k">else</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="n">rval</span><span class="p">.</span><span class="n">ToString</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>It would be a time saver if this translation could be done by a command right inside WinDBG instead of relying on another tool based on ClrMD in which addresses are pasted.</p>
<h3 id="windbg-extension-101">WinDBG extension 101</h3>
<p>In addition of being a native Windows debugger, WinDBG supports extensions: .dll files that you load with the <strong>.load</strong> command. They are exporting commands that are callable from within WinDBG with the “<strong>!</strong>” prefix. These commands are usual native exports that can be seen with tools such as <a href="http://www.dependencywalker.com/">http://www.dependencywalker.com/</a> as shown by the next screenshot:</p>
<p><img loading="lazy" src="/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/DWwithSOS.png"></p>
<p>As you can see, all SOS commands are functions exported by the sos.dll native binary. Before digging into the extension functions implementation, notice that a few other functions could also be exported. Among them, the <strong>DebugExtensionInitialize</strong> function provides version information (i.e. which version of the debugging API is expected) and must be exported to be called by WinDBG when the dll is loaded.</p>
<p>Read <a href="https://blogs.msdn.microsoft.com/sgajjela/2013/03/02/how-to-develop-windbg-extension-dll">this post</a> for more details about how to develop a native WinDBG extension.</p>
<p>All extension command functions take two parameters:</p>
<ul>
<li><strong>An IDebugClient</strong> instance to interact with WinDBG</li>
<li>An ANSI string for the arguments (such as “<em>-stat</em>” for !<em>dumpheap</em>)</li>
</ul>
<p>The bridge between your extension commands and WinDBG is provided by the <strong>IDebugClient</strong> COM interface. But don’t be scared: no need to manually deal with native COM interface with ClrMD! The <strong>DataTarget</strong>**.**<strong>CreateFromDebuggerInterface</strong> method takes an <strong>IDebugClient</strong> interface and returns an instance of <strong>DataTarget</strong>. As you might remember from <a href="/posts/2017-02-21_clrmd-part-1-going-beyond/">the initial post of this series</a>, <strong>DataTarget</strong> is the gateway to the dump (or live-debugged attached process): we are now back to the known ClrMD world.</p>
<h3 id="reuse-clrmd-samples">Reuse ClrMD Samples</h3>
<p>Hopefully, most of the glue to bind the native world to ClrMD is already available! You simply reuse the partial <strong>DebuggerExtensions</strong> class given <a href="https://github.com/Microsoft/clrmd/tree/master/src/Samples/WindbgExtension">in the samples</a>.</p>
<p>You extend the class with your extension methods that take the following signature:</p>
<p><strong>MyCommandSignature.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></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">static</span> <span class="k">void</span> <span class="n">MyCommand</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The first parameter is a pointer to the <strong>IDebugClient</strong> interface provided by WinDBG. The first thing to do in your extension command method is to call the <strong>InitApi</strong> static method with the interface pointer and let the magic happens.</p>
<p><strong>MyCommand-2.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="c1">// Must be the first thing in our extension.</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(!</span><span class="n">InitApi</span><span class="p">(</span><span class="n">client</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>After that call, the output of the Console will be redirected to WinDBG and your code is free to use the following properties to access the dump via ClrMD:</p>
<p><strong>DebuggerExtensionPartial.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></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">partial</span> <span class="k">class</span> <span class="nc">DebuggerExtensions</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="kd">static</span> <span class="n">IDebugClient</span> <span class="n">DebugClient</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">DataTarget</span> <span class="n">DataTarget</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="n">ClrRuntime</span> <span class="n">Runtime</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="kd">private</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>The second parameter <em>args</em> received by your method is a string that contains the parameters added by the user after the name of your command. For example, if the user types “MyCommand param1 param2”, the <em>args</em> parameter will be “param1 param2”.</p>
<h3 id="exposing-native-functions">Exposing native functions</h3>
<p>The last part of magic glue is how to export a native function from a .NET assembly. This is made possible by the UnmanagedExports nuget package by Robert Giesecke.</p>
<p><img loading="lazy" src="/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/ExportNuGet.png"></p>
<p>Once added to your project, decorate the functions to export with the <strong>DllExport</strong> attribute and the native name of the function that will be visible in WinDBG as a command.</p>
<p>There is a little trick here: the names of exported functions are case sensitive for WinDBG. If you take a look again at sos.dll in Dependency Walker and sort exports by Function column, you will notice a few duplicates such as <em>CLRStack</em>/ <em>ClrStack</em>/ <em>clrstack</em> as shown in the following screenshot:</p>
<p><img loading="lazy" src="/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/MultipleExportsSOS.png"></p>
<p>For usability sake, it is a good practice to provide several syntaxes for the same command, including short version such as !<em>dso</em> for <em>!DumpStackObject</em>in SOS. Unfortunately the <strong>DllExport</strong> attribute does not allow multiple applications on the same method with different exported names. You need to define a different method per exported name and all of them will call the same internal helper method.</p>
<p><strong>MultipleDllExport.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><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</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="na">[DllExport(&#34;tks&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">tks</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</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">OnTkState</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">args</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="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[DllExport(&#34;tkstate&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">tkstate</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</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">OnTkState</span> <span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">args</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="na">
</span></span></span><span class="line"><span class="cl"><span class="na">[DllExport(&#34;tkState&#34;)]</span>
</span></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">tkState</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</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">OnTkState</span> <span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">args</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></span><span class="line"><span class="cl"><span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">OnTkState</span> <span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</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">// Must be the first thing in our extension.</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(!</span><span class="n">InitApi</span><span class="p">(</span><span class="n">client</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 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>Thanks to the <strong>GetTaskStateFromAddress</strong> and <strong>GetTaskState</strong> helper methods described earlier, the implementation of the <strong>OnTkState</strong> method is straightforward once the address or the value has been extracted from the <strong>args</strong> parameter.</p>
<h3 id="dont-forget-your-user-implement-help">Don’t forget your user: implement help</h3>
<p>A good extension always provides an help command that (1) lists the available commands with shortcuts and (2) additional details on each command. Simply add a new file that defines the exports for help/Help and parses the string argument if needed.</p>
<p><strong>DebuggerExtensionImpl.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><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</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">partial</span> <span class="k">class</span> <span class="nc">DebuggerExtensions</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="na">    [DllExport(&#34;Help&#34;)]</span>
</span></span><span class="line"><span class="cl">    <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">Help</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</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">OnHelp</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">args</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="na">
</span></span></span><span class="line"><span class="cl"><span class="na">   [DllExport(&#34;help&#34;)]</span>
</span></span><span class="line"><span class="cl">   <span class="kd">public</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">help</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="p">[</span><span class="n">MarshalAs</span><span class="p">(</span><span class="n">UnmanagedType</span><span class="p">.</span><span class="n">LPStr</span><span class="p">)]</span> <span class="kt">string</span> <span class="n">args</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">OnHelp</span><span class="p">(</span><span class="n">client</span><span class="p">,</span> <span class="n">args</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></span><span class="line"><span class="cl">    <span class="kd">const</span> <span class="kt">string</span> <span class="n">_help</span> <span class="p">=</span> <span class="s">&#34;...&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kd">const</span> <span class="kt">string</span> <span class="n">_tksHelp</span> <span class="p">=</span> <span class="s">&#34;...&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kd">private</span> <span class="kd">static</span> <span class="k">void</span> <span class="n">OnHelp</span><span class="p">(</span><span class="n">IntPtr</span> <span class="n">client</span><span class="p">,</span> <span class="kt">string</span> <span class="n">args</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">// Must be the first thing in our extension.</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(!</span><span class="n">InitApi</span><span class="p">(</span><span class="n">client</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">string</span> <span class="n">command</span> <span class="p">=</span> <span class="n">args</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">args</span> <span class="p">!=</span> <span class="kc">null</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">            <span class="n">command</span> <span class="p">=</span> <span class="n">args</span><span class="p">.</span><span class="n">ToLower</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">switch</span> <span class="p">(</span><span class="n">command</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">case</span> <span class="s">&#34;tks&#34;</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">            <span class="k">case</span> <span class="s">&#34;tkstate&#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">WriteLine</span><span class="p">(</span><span class="n">_tksHelp</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">            <span class="k">default</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="n">_help</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">            <span class="k">break</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></code></pre></td></tr></table>
</div>
</div><h3 id="tips-to-use-the-extension">Tips to use the extension</h3>
<p>Don’t forget that you might need two versions of your assembly: one for the x86 version of WinDBG if your applications are 32 bit and one for the x64 version of WinDBG in the 64 bit case. If you want to be able to easily load your extension with the .load <myextension> command, copy it with Microsoft.Diagnostics.Runtime.dll (i.e. ClrMD assembly) to the winext subfolder of x64/x86 WinDBG folders:</p>
<p><img loading="lazy" src="/posts/2017-06-29_clrmd-part-5-extend-sos-windbg/WinDbgFolders.png"></p>
<p>Before being able to use any of its commands, you must load SOS with the well-known <strong>.loadby sos clr</strong> mantra. But this is not enough: you also have to run at least one SOS command. You are now ready to call any of your extension commands!</p>
<h3 id="next-step">Next step…</h3>
<p>The next episodes will bring you into the mysteries under the <strong>dynamic</strong> keyword and how to simplify the syntax to leverage ClrMD.</p>
<hr>
<p><em>Co-authored with <a href="https://twitter.com/KooKiz">Kevin Gosse</a></em></p>
]]></content:encoded></item></channel></rss>