<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Jan Krivanek</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/jankrivanek/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/jankrivanek/feed.xml</link>
    <description>Snippets of knowledge that I found interesting</description>
    <lastBuildDate>Wed, 17 Apr 2019 04:08:14 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>GC performing full blocking Gen2 collections under SustainedLowLatency despite there is no LowMemory notification from OS</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/gc-performing-full-blocking-gen2-collections-under-sustainedlowlatency-despite-there-is-no-lowmemory-notification-from-os</link>
      <pubDate>Mon, 09 Dec 2013 12:54:00 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2013/12/09/gc-performing-full-blocking-gen2-collections-under-sustainedlowlatency-despite-there-is-no-lowmemory-notification-from-os/</guid>
      <description><![CDATA[In this post I want to share my observation of a weird behavior in GC.
Background and documented...]]></description>
      <content:encoded><![CDATA[<p>In this post I want to share my observation of a weird behavior in GC.</p>
<h2>Background and documented behavior</h2>
<p>One can switch <a title="GCLatencyMode" href="http://blogs.msdn.com/controlpanel/blogs/posteditor.aspx/GCLatencyMode">GCLatencyMode</a>&nbsp;in order to adjust the GC behavior. According to this doc SustainedLowLatency mode should ensure that full blocking collections are only made under low memory pressure:</p>
<pre class="code">(...) <span>The collector tries to perform only generation 0, generation 1, and concurrent generation 2 collections.</span></pre>
<pre class="code"><span>Full blocking collections may still occur if the system is under memory pressure</span>
</pre>
<p>One can subscribe to Full Blocking GC notifications by using <a title="Garbage Collection Notifications" href="http://msdn.microsoft.com/en-us/library/cc713687(v=vs.110).aspx">Garbage Collection Notifications</a>.</p>
<p>According to CLR via C# book (and you can verify that through debugger like demonstrated in <a href="http://clivetong.wordpress.com/2013/05/30/windbg-and-the-case-of-the-unfreed-garbage/">this post</a>) GC subscribes to low memory notifications through usage of <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa366541(v=vs.85).aspx">CreateMemoryResourceNotification</a> API.</p>
<h2>Repro Code</h2>
<p>I put together code (by getting and adjusting code sample from mentioned <a title="Garbage Collection Notifications" href="http://msdn.microsoft.com/en-us/library/cc713687(v=vs.110).aspx">Garbage Collection Notifications</a>&nbsp;msdn doc) that lets you select the GCLatencyMode, runs allocations and display number of performed allocations, number of performed Gen 2 Collections (and how many are blocking) + info about low system memory notification:</p>
<pre class="code">namespace GCExperiments<br />
{<br />    using System;<br />    using System.Collections.Generic;<br />    using System.Runtime;<br />    using System.Runtime.InteropServices;<br />    using System.Threading;<br /><br />    public static class Win32Imports<br />    {<br />        public static int LowMemoryResourceNotification = 0;<br /><br />        [DllImport("kernel32.dll", SetLastError = true)]<br />        public static extern IntPtr CreateMemoryResourceNotification(int notificationType);<br /><br />        [DllImport("kernel32.dll", SetLastError = true)]<br />        public static extern bool QueryMemoryResourceNotification(IntPtr hResNotification, out bool isResourceStateMet);<br />    }<br /><br />    public class GCModesTest<br />    {<br />        public static void Main(string[] args)<br />        {<br />            GCModesTest t = new GCModesTest();<br />            t.Run();<br />        }<br /><br />        private List&lt;byte[]&gt; _load = new List&lt;byte[]&gt;();<br />        private IntPtr _hResNotification;<br /><br />        public void Run()<br />        {<br />            Console.WriteLine(<br />                "Press 'I' for test under (I)nteractive GC mode, press 'S' for test under (S)ustainableLowLatency mode and 'L' for test under (L)owLatency mode");<br /><br />            GCLatencyMode requestedMode;<br /><br />            switch (Console.ReadKey(true).Key)<br />            {<br />                case ConsoleKey.I:<br />                    requestedMode = GCLatencyMode.Interactive;<br />                    break;<br />                case ConsoleKey.S:<br />                    requestedMode = GCLatencyMode.SustainedLowLatency;<br />                    break;<br />                case ConsoleKey.L:<br />                    requestedMode = GCLatencyMode.LowLatency;<br />                    break;<br />                default:<br />                    Log("Unknown option, exiting");<br />                    return;<br />            }<br /><br />            GCSettings.LatencyMode = requestedMode;<br />            Console.WriteLine("Current GC mode: {0}, Is server GC: {1}",<br />                              GCSettings.LatencyMode, GCSettings.IsServerGC);<br /><br />            _hResNotification = Win32Imports.CreateMemoryResourceNotification(Win32Imports.LowMemoryResourceNotification);<br /><br />            GC.RegisterForFullGCNotification(1, 1);<br />            ThreadPool.QueueUserWorkItem(o =&gt; WaitForFullGCProc());<br /><br />            RunAllocations();<br />        }<br /><br />        private void RunAllocations()<br />        {<br />            try<br />            {<br />                int lastCollCount = 0;<br />                int newCollCount = 0;<br />                int allocations = 0;<br /><br />                while (true)<br />                {<br />                    _load.Add(new byte[500]);<br />                    allocations++;<br />                    newCollCount = GC.CollectionCount(2);<br />                    if (newCollCount != lastCollCount)<br />                    {<br />                        // Show collection count when it increases:<br />                        Log("Allocations: {0}. Gen 2 collection count: {1}, from that blocking collections: {2}",<br />                            allocations, GC.CollectionCount(2), _gcFullollectionNotificationsCount);<br />                        lastCollCount = newCollCount;<br />                    }<br />                }<br />            }<br />            catch (OutOfMemoryException)<br />            {<br />                Console.WriteLine("Out of memory.");<br />            }<br />        }<br /><br />        private int _gcFullollectionNotificationsCount = 0;<br /><br />        private void WaitForFullGCProc()<br />        {<br />            // CheckForNotify is set to true and false in Main. <br />            while (true)<br />            {<br />                // Check for a notification of an approaching collection.<br />                GCNotificationStatus s = GC.WaitForFullGCApproach();<br />                if (s == GCNotificationStatus.Succeeded)<br />                {<br />                    Log("GC Approach Notification raised.");<br />                }<br />                else if (s == GCNotificationStatus.Canceled)<br />                {<br />                    Log("GC Approach Notification cancelled.");<br />                    break;<br />                }<br />                else<br />                {<br />                    // This can occur if a timeout period <br />                    // is specified for WaitForFullGCApproach(Timeout)  <br />                    // or WaitForFullGCComplete(Timeout)   <br />                    // and the time out period has elapsed. <br />                    Log("GC Approach Notification not applicable.");<br />                    break;<br />                }<br /><br />                // Check for a notification of a completed collection.<br />                s = GC.WaitForFullGCComplete();<br />                if (s == GCNotificationStatus.Succeeded)<br />                {<br />                    bool isResourceStateMet;<br />                    bool succeeded = Win32Imports.QueryMemoryResourceNotification(_hResNotification,<br />                                                                                  out isResourceStateMet);<br /><br />                    if (!succeeded)<br />                    {<br />                        Log("Call to QueryMemoryResourceNotification failed!");<br />                    }<br /><br />                    _gcFullollectionNotificationsCount++;<br />                    Log(<br />                        "GC Completed Notification raised. Count: {0}, Gc gen 2 count: {1}, Is Low memory condition: {2}",<br />                        _gcFullollectionNotificationsCount, GC.CollectionCount(2), isResourceStateMet);<br />                }<br />                else if (s == GCNotificationStatus.Canceled)<br />                {<br />                    Log("GC Completed Notification not applicable.");<br />                    break;<br />                }<br />                else<br />                {<br />                    // Could be a time out.<br />                    Log("GC Completed Notification not applicable.");<br />                    break;<br />                }<br />            }<br />        }<br /><br />        private void Log(string format, params object[] args)<br />        {<br />            Console.WriteLine(format, args);<br />        }<br />    }</pre>
<pre class="prettyprint prettyprinted"><span class="pun">}</span></pre>
<h2>Observed behavior</h2>
<p>When running this code you can see for yourself that under SustainedLowLatency mode the full blocking Gen2 collections are being performed despite the system low memory condition notification is not raised. In fact it runs approximately same number of Gen2 collection and with approximately same frequency as under the Interactive mode, with the difference that all those are blocking under SustainedLowLatency mode. That seems to be contrary to the mentioned documentation.</p>
<p>I have&nbsp;<a href="http://connect.microsoft.com/VisualStudio/feedback/details/810047/gc-performing-full-blocking-gen2-collections-under-sustainedlowlatency-despite-there-is-no-lowmemory-notification-from-os">reported this issue to Microsoft</a>&nbsp;and I'll update this post once I have some further info.</p>
<h2>Sample outputs</h2>
<p>Here goes sample output of provided code for Interactive GC mode:</p>
<pre class="code">Press 'I' for test under (I)nteractive GC mode, press 'S' for test under (S)ustainableLowLatency mode and 'L' for test under (L)owLatency mode<br />Current GC mode: Interactive, Is server GC: False<br />GC Approach Notification raised.<br />Allocations: 43050. Gen 2 collection count: 1, from that blocking collections: 0<br />GC Completed Notification raised. Count: 1, Gc gen 2 count: 1, Is Low memory condition: False<br />Allocations: 102612. Gen 2 collection count: 2, from that blocking collections: 1<br />Allocations: 209854. Gen 2 collection count: 3, from that blocking collections: 1<br />Allocations: 412419. Gen 2 collection count: 4, from that blocking collections: 1<br />Allocations: 769896. Gen 2 collection count: 5, from that blocking collections: 1<br />Allocations: 1413345. Gen 2 collection count: 6, from that blocking collections: 1<br />Allocations: 2640671. Gen 2 collection count: 7, from that blocking collections: 1<br />Allocations: 4940414. Gen 2 collection count: 8, from that blocking collections: 1<br />Allocations: 9230099. Gen 2 collection count: 9, from that blocking collections: 1<br />Allocations: 17166009. Gen 2 collection count: 10, from that blocking collections: 1<br />Allocations: 23969919. Gen 2 collection count: 11, from that blocking collections: 1<br />Allocations: 26800670. Gen 2 collection count: 12, from that blocking collections: 1<br />Allocations: 28591795. Gen 2 collection count: 13, from that blocking collections: 1</pre>
<pre>&nbsp;</pre>
<pre>And here goes sample output for SustainedLowLatencyMode:</pre>
<pre>&nbsp;</pre>
<pre class="code">Press 'I' for test under (I)nteractive GC mode, press 'S' for test under (S)ustainableLowLatency mode and 'L' for test under (L)owLatency mode<br />Current GC mode: SustainedLowLatency, Is server GC: False<br />GC Approach Notification raised.<br />Allocations: 43038. Gen 2 collection count: 1, from that blocking collections: 0<br />GC Completed Notification raised. Count: 1, Gc gen 2 count: 1, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 102584. Gen 2 collection count: 2, from that blocking collections: 1<br />GC Completed Notification raised. Count: 2, Gc gen 2 count: 2, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 209795. Gen 2 collection count: 3, from that blocking collections: 2<br />GC Completed Notification raised. Count: 3, Gc gen 2 count: 3, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 424242. Gen 2 collection count: 4, from that blocking collections: 3<br />GC Completed Notification raised. Count: 4, Gc gen 2 count: 4, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 781680. Gen 2 collection count: 5, from that blocking collections: 4<br />GC Completed Notification raised. Count: 5, Gc gen 2 count: 5, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 1437019. Gen 2 collection count: 6, from that blocking collections: 5<br />GC Completed Notification raised. Count: 6, Gc gen 2 count: 6, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 2688148. Gen 2 collection count: 7, from that blocking collections: 6<br />GC Completed Notification raised. Count: 7, Gc gen 2 count: 7, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 5035521. Gen 2 collection count: 8, from that blocking collections: 7<br />GC Completed Notification raised. Count: 8, Gc gen 2 count: 8, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 9396673. Gen 2 collection count: 9, from that blocking collections: 8<br />GC Completed Notification raised. Count: 9, Gc gen 2 count: 9, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 17463633. Gen 2 collection count: 10, from that blocking collections: 9<br />GC Completed Notification raised. Count: 10, Gc gen 2 count: 10, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 19656130. Gen 2 collection count: 11, from that blocking collections: 10<br />GC Completed Notification raised. Count: 11, Gc gen 2 count: 11, Is Low memory condition: False<br />GC Approach Notification raised.<br />Allocations: 21157575. Gen 2 collection count: 12, from that blocking collections: 11<br />GC Completed Notification raised. Count: 12, Gc gen 2 count: 12, Is Low memory condition: True<br />Allocations: 22670954. Gen 2 collection count: 13, from that blocking collections: 12<br />GC Approach Notification raised.<br />Allocations: 24241691. Gen 2 collection count: 14, from that blocking collections: 12<br />GC Completed Notification raised. Count: 13, Gc gen 2 count: 14, Is Low memory condition: True<br />GC Approach Notification raised.<br />Allocations: 25856669. Gen 2 collection count: 15, from that blocking collections: 13</pre>
<pre>&nbsp;</pre>
<p>As you can see SustainedLowLatency mode is causing approximately same number of collections (despite it's far from low os memory pressure), but all of them are blocking.</p>]]></content:encoded>
    </item>
    <item>
      <title>Bug in ZipFile CreateFromDirectory - your archive can be irreparable corrupted</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/bug-in-zipfile-createfromdirectory-your-archive-can-be-irreparable-corrupted</link>
      <pubDate>Wed, 07 Aug 2013 00:42:00 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2013/08/07/bug-in-zipfile-createfromdirectory-your-archive-can-be-irreparable-corrupted/</guid>
      <description><![CDATA[In this post I want to share my observation of a bug in ZipFile.CreateFromDirectory method that will...]]></description>
      <content:encoded><![CDATA[<p>In this post I want to share my observation of a bug in <a href="http://msdn.microsoft.com/en-us/library/hh485707.aspx">ZipFile.CreateFromDirectory</a> method that will yield resulting archives corrupted. Luckily this happens only for quite large source directories - and so you might not be affected at all. I'll share my observations of limits that trigger this bug and also small code sample reproducing the bug.</p>
<h2>Our usage of ZipFile.CreateFromDirectory and discovery of a bug</h2>
<p>The <a href="http://msdn.microsoft.com/en-us/library/system.io.compression.zipfile.aspx">ZipFile</a> class has a very useful utility methods. We decided to use it for compressing our daily logs (which were running into dozens of GB in cumulative size). We basically zipped the previous days logs and if zipping operation finished successfully, we also deleted the original file:</p>
<pre class="code">private static void ArchiveDirectory(string target_dir)
{
	try
	{
		if (Directory.Exists(target_dir) &amp;&amp; !File.Exists(target_dir + ".zip"))
		{
			System.IO.Compression.ZipFile.CreateFromDirectory(target_dir, target_dir + ".zip");
			DeleteDirectory(target_dir);
		}
	}
	catch (Exception)
	{ /* This is intentional as there is no place to log during log archival */ } 
}
</pre>
<p>Where DeleteDirectory just recursively deletes the original directory with all its content.</p>
<p>This worked great, until we started to cross a magic line of about 45GB of a original directory to be zipped and at the same time about 4GB size of a destination zip archive (the size of a destination zip seems to be more significant here). Nothing changed during zipping - ZipFile.CreateFromDirectory created a zip file without throwing any exception. Destination zip files seem to have appropriate sizes and they contain all the source files. Problems started when we wanted to access original log files - not all of them could be accessed. Build in windows zip files support failed (0x80004005 - Unspecified Error). Various third party archiving tools failed (complaining about the zip file being compressed). So we tried to call reciprocal method: <a href="http://msdn.microsoft.com/en-us/library/hh485723.aspx">ZipFile.ExtractToDirectory</a> - it started to unpack the original zip - calming us down for a minute - but after unpacking those magic some 45GBs (or more probably 4GBs of a zipped folder) it threw:</p>
<pre class="code">System.IO.InvalidDataException: A local file header is corrupt.
at System.IO.Compression.ZipArchiveEntry.ThrowIfNotOpenable(Boolean needToUncompress, Boolean needToLoadIntoMemory)
at System.IO.Compression.ZipArchiveEntry.OpenInReadMode(Boolean checkOpenable)
at System.IO.Compression.ZipArchiveEntry.Open()
at System.IO.Compression.ZipFileExtensions.ExtractToFile(ZipArchiveEntry source, String destinationFileName, Boolean overwrite)
at System.IO.Compression.ZipFileExtensions.ExtractToDirectory(ZipArchive source, String destinationDirectoryName)
at System.IO.Compression.ZipFile.ExtractToDirectory(String sourceArchiveFileName, String destinationDirectoryName, Encoding entryNameEncoding)
at System.IO.Compression.ZipFile.ExtractToDirectory(String sourceArchiveFileName, String destinationDirectoryName)
</pre>
<h2>Reproduction of a bug - you can try it yourself</h2>
<p>This turned out to be a very significant problem for us (I'm building financial platform where missing logs is an unacceptable situation). So I quickly reported this to Microsoft and got some attention - what helped was probably also a very simple repro code, that can be used without any changes/configuration (you just need about 90GBs of a free space and about 30-60 minutes for running it):</p>
<pre class="code">namespace ZipFileTest
{
    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Linq;
    using System.Text;

    class Program
    {
        static void Main(string[] args)
        {
            ZipFileTest zft = new ZipFileTest();
            zft.DoTest();
        }
    }

    public class ZipFileTest
    {
        const string LOGS_DIR = "logs";
        const string LOG_FILE_NAME_PREFIX = "dummyLog";
        private const string LOG_LINES_STRING = @"
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. 
Nam cursus. Morbi ut mi. Nullam enim leo, egestas id, condimentum at, laoreet mattis, massa. 
Sed eleifend nonummy diam. 
Praesent mauris ante, elementum et, bibendum at, posuere sit amet, nibh. Duis tincidunt lectus quis dui viverra vestibulum. Suspendisse vulputate aliquam dui. Nulla elementum dui ut augue. Aliquam vehicula mi at mauris. Maecenas placerat, nisl at consequat rhoncus, sem nunc gravida justo, quis eleifend arcu velit quis lacus. Morbi magna magna, tincidunt a, mattis non, imperdiet vitae, tellus. Sed odio est, auctor ac, sollicitudin in, consequat vitae, orci. 
Fusce id felis. Vivamus sollicitudin metus eget eros.
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In posuere felis nec tortor. Pellentesque faucibus. Ut accumsan ultricies elit. 
Maecenas at justo id velit placerat molestie. Donec dictum lectus non odio. Cras a ante vitae enim iaculis aliquam. Mauris nunc quam, venenatis nec, euismod sit amet, egestas placerat, est. 
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Cras id elit. Integer quis urna. Ut ante enim, dapibus malesuada, fringilla eu, condimentum quis, tellus. Aenean porttitor eros vel dolor. Donec convallis pede venenatis nibh. Duis quam. Nam eget lacus. Aliquam erat volutpat. Quisque dignissim congue leo.
Mauris vel lacus vitae felis vestibulum volutpat. Etiam est nunc, venenatis in, tristique eu, imperdiet ac, nisl. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In iaculis facilisis massa. Etiam eu urna. Sed porta. Suspendisse quam leo, molestie sed, luctus quis, feugiat in, pede. Fusce tellus. Sed metus augue, convallis et, vehicula ut, pulvinar eu, ante. Integer orci tellus, tristique vitae, consequat nec, porta vel, lectus. Nulla sit amet diam. Duis non nunc. Nulla rhoncus dictum metus. Curabitur tristique mi condimentum orci. Phasellus pellentesque aliquam enim. Proin dui lectus, cursus eu, mattis laoreet, viverra sit amet, quam. 
Curabitur vel dolor ultrices ipsum dictum tristique. Praesent vitae lacus. Ut velit enim, vestibulum non, fermentum nec, hendrerit quis, leo. Pellentesque rutrum malesuada neque.
Nunc tempus felis vitae urna. Vivamus porttitor, neque at volutpat rutrum, purus nisi eleifend libero, a tempus libero lectus feugiat felis. Morbi diam mauris, viverra in, gravida eu, mattis in, ante. 
Morbi eget arcu. Morbi porta, libero id ullamcorper nonummy, nibh ligula pulvinar metus, eget consectetuer augue nisi quis lacus. 
Ut ac mi quis lacus mollis aliquam. Curabitur iaculis tempus eros. 
Curabitur vel mi sit amet magna malesuada ultrices. Ut nisi erat, fermentum vel, congue id, euismod in, elit. Fusce ultricies, orci ac feugiat suscipit, leo massa sodales velit, et scelerisque mi tortor at ipsum. Proin orci odio, commodo ac, gravida non, tristique vel, tellus. Pellentesque nibh libero, ultricies eu, sagittis non, mollis sed, justo. Praesent metus ipsum, pulvinar pulvinar, porta id, fringilla at, est.
Phasellus felis dolor, scelerisque a, tempus eget, lobortis id, libero. Donec scelerisque leo ac risus. Praesent sit amet est. In dictum, dolor eu dictum porttitor, enim felis viverra mi, eget luctus massa purus quis odio. Etiam nulla massa, pharetra facilisis, volutpat in, imperdiet sit amet, sem. 
Aliquam nec erat at purus cursus interdum. 
Vestibulum ligula augue, bibendum accumsan, vestibulum ut, commodo a, mi. Morbi ornare gravida elit. Integer congue, augue et malesuada iaculis, ipsum dui aliquet felis, at cursus magna nisl nec elit. Donec iaculis diam a nisi accumsan viverra. Duis sed tellus et tortor vestibulum gravida. Praesent elementum elit at tellus. Curabitur metus ipsum, luctus eu, malesuada ut, tincidunt sed, diam. Donec quis mi sed magna hendrerit accumsan. Suspendisse risus nibh, ultricies eu, volutpat non, condimentum hendrerit, augue. Etiam eleifend, metus vitae adipiscing semper, mauris ipsum iaculis elit, congue gravida elit mi egestas orci. Curabitur pede.
Maecenas aliquet velit vel turpis. Mauris neque metus, malesuada nec, ultricies sit amet, porttitor mattis, enim. In massa libero, interdum nec, interdum vel, blandit sed, nulla. In ullamcorper, est eget tempor cursus, neque mi consectetuer mi, a ultricies massa est sed nisl. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Proin nulla arcu, nonummy luctus, dictum eget, fermentum et, lorem. Nunc porta convallis pede.";

        private string[] _loglines;
        private int _fileCnt = 0;
        Random _rnd = new Random((int)DateTime.Now.Ticks);

        public ZipFileTest()
        {
            _loglines = LOG_LINES_STRING.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
        }

        private string CreateLogDirectory()
        {
            string basedir = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            basedir = basedir ?? string.Empty;

            string targetDir = System.IO.Path.Combine(basedir, LOGS_DIR, DateTime.UtcNow.ToString("yyyy-MM-dd"), Process.GetCurrentProcess().Id.ToString());

            if (!Directory.Exists(targetDir))
            {
                Directory.CreateDirectory(targetDir);
            }

            return targetDir;
        }

        private string RandomString(int size)
        {
            StringBuilder builder = new StringBuilder();

            builder.Append(
                Enumerable.Repeat(0, size)
                          .Select(dummy =&gt; Convert.ToChar(Convert.ToInt32(Math.Floor(26*_rnd.NextDouble() + 65))))
                          .ToArray());

            return builder.ToString();
        }

        private void CreateNextLogFile(string logDir)
        {
            FileStream stream = File.Open(
                            System.IO.Path.Combine(logDir, string.Format("{0}_{1}.log", LOG_FILE_NAME_PREFIX, _fileCnt++)),
                            FileMode.Append,
                            FileAccess.Write,
                            FileShare.Read);

            for (int i = 0; i &lt; 500000; i++)
            {
                string logLine = _loglines[_rnd.Next(_loglines.Length)];

                for (int j = 0; j &lt; _rnd.Next(5); j++)
                {
                    logLine = logLine.Insert(_rnd.Next(logLine.Length), RandomString(_rnd.Next(30)));
                }

                byte[] logLineBytes = Encoding.Default.GetBytes(logLine);
                stream.Write(logLineBytes, 0, logLineBytes.Length);
            }

            stream.Close();
        }

        private string ZipLogFiles(string logDir)
        {
            string dirToZip = Directory.GetParent(logDir).FullName;
            System.IO.Compression.ZipFile.CreateFromDirectory(dirToZip, dirToZip + ".zip");
            return dirToZip + ".zip";
        }

        private void UnzipZippedLogs(string zipName, string logDir)
        {
            string dirToUnzip = Path.Combine(Directory.GetParent(logDir).Parent.FullName, "UnzippedLogs");
            System.IO.Compression.ZipFile.ExtractToDirectory(zipName, dirToUnzip);
        }

        public void DoTest()
        {
            string logDir = CreateLogDirectory();
            for (int i = 0; i &lt; 700; i++)
            {
                CreateNextLogFile(logDir);
            }

            string zipName = ZipLogFiles(logDir);
            UnzipZippedLogs(zipName, logDir);
        }
    }
}
</pre>
<p>You need to reference System.IO.Compression.FileSystem assembly, so your section of csproj file with references will look this way:</p>
<pre class="code">  &lt;ItemGroup&gt;
    ...
    &lt;Reference Include="System.IO.Compression.FileSystem" /&gt;
    ...
  &lt;/ItemGroup&gt;
</pre>
<p>Do not pay to much attention to all the magic constants - they are tuned in a way so that you can reliably hit the issue with reasonable time and space runtime requirements for the repro.</p>
<p>When running this code the unzipping will throw above mentioned exception. The thing is that handling of this exception won't help you - archive is already corrupted during the zipping operation</p>
<h2>Workaround and a fix</h2>
<p>I have <a href="https://connect.microsoft.com/VisualStudio/feedback/details/794792/reproducible-bug-in-zipfile-createfromdirectory">reported this issue to Microsoft</a> and I'm in contact with owners of this code. They were able to find rootcause of the issue (my wild guess would be something with 32bit checksums or something like that - as this reproes with archives over 4GBs). It seems that there is no remedy for already archived folders (I'll try to find something myself - at least partial remedy, as we simply cannot miss logs), for newly created archives it's highly recommendable to split the original folder into multiple destination archives, each below 4GBs (you'll probably need to do some tuning to find the source sizes - as level of compression highly depends on patterns in your logs).</p>]]></content:encoded>
    </item>
    <item>
      <title>Why are IntelliTrace Windows and Menus missing?</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/why-are-intellitrace-windows-and-menus-missing</link>
      <pubDate>Mon, 04 Feb 2013 18:05:00 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2013/02/04/why-are-intellitrace-windows-and-menus-missing/</guid>
      <description><![CDATA[Visual Studio 2010 (and 2012) Ultimate is coming with a cool feature called IntelliTrace &ndash; if...]]></description>
      <content:encoded><![CDATA[<p>Visual Studio 2010 (and 2012) Ultimate is coming with a cool feature called IntelliTrace &ndash; if you don&rsquo;t know about this feature I recommend to explore it more (e.g. here <a title="http://msdn.microsoft.com/en-us/library/dd264915(v=VS.100).aspx" href="http://msdn.microsoft.com/en-us/library/dd264915(v=VS.100).aspx">http://msdn.microsoft.com/en-us/library/dd264915(v=VS.100).aspx</a> or even better directly in your VS Ultimate ;-)).</p>
<p>However it might happen to you that your IntelliTrace Events or IntelliTrace Calls windows are missing and you cannot find the menus for those any more (even if you&rsquo;re sure that it was working the day ago and that you are really running VS Ultimate). What is causing this?</p>
<h2>My IntelliTrace Events and IntelliTrace Calls Windows and Menus for those are missing</h2>
<p>During your debugging session you open the Debug &ndash;&gt; Windows menu and you see something like this:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/8836.IntelliTraceMenuMissing_6BC5D026.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/8836.IntelliTraceMenuMissing_5F00_6BC5D026.png"><img style="display: inline;" title="IntelliTraceMenuMissing" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/5287.IntelliTraceMenuMissing_thumb_4FB5113B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/5287.IntelliTraceMenuMissing_5F00_thumb_5F00_4FB5113B.png" alt="IntelliTraceMenuMissing" width="537" height="484" border="0" /></a></p>
<p>No menu for IntelliTrace &ndash; despite you double-checked that it&rsquo;s enabled in Debug &ndash;&gt; Options &ndash;&gt; IntelliTrace.</p>
<p>There is one more option that you need to check (and that would probably never came to your mind in connection with IntelliTrace) &ndash; and it&rsquo;s SQL integrated debugging (in your project properties, Debug pane):</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/2134.SqlIntegratedDebugging_0B74A9EF.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/2134.SqlIntegratedDebugging_5F00_0B74A9EF.png"><img style="display: inline;" title="SqlIntegratedDebugging" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/7737.SqlIntegratedDebugging_thumb_56681ABE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/7737.SqlIntegratedDebugging_5F00_thumb_5F00_56681ABE.png" alt="SqlIntegratedDebugging" width="527" height="407" border="0" /></a></p>
<p><strong>Checking this option will cause the IntelliTrace functionality to disappear.</strong></p>
<h2>Bringing the IntelliTrace functionality back</h2>
<p>Once you disable SQL Server debugging (it&rsquo;s off by default by the way), your IntelliTrace windows and menus will magically reappear!:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/1464.IntelliTraceMenu_2F2DD189.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/1464.IntelliTraceMenu_5F00_2F2DD189.png"><img style="display: inline;" title="IntelliTraceMenu" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/2045.IntelliTraceMenu_thumb_6AED6A3C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/2045.IntelliTraceMenu_5F00_thumb_5F00_6AED6A3C.png" alt="IntelliTraceMenu" width="522" height="470" border="0" /></a></p>
<p>I have logged bug for this with Visual Studio team &ndash; so let&rsquo;s hope this behavior will be history soon.</p>]]></content:encoded>
    </item>
    <item>
      <title>Crash in Unloaded_mydll.dll – how to find out who called unloaded dll.</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/crash-in-unloaded_mydll-dll-how-to-find-out-who-called-unloaded-dll</link>
      <pubDate>Tue, 11 Dec 2012 17:36:00 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2012/12/11/crash-in-unloaded_mydll-dll-how-to-find-out-who-called-unloaded-dll/</guid>
      <description><![CDATA[The other day I happened to came across an interesting crash dump. It was clear that something...]]></description>
      <content:encoded><![CDATA[<p>The other day I happened to came across an interesting crash dump. It was clear that something called our module which was already unloaded and so whole application crashed. On the first look it was however very unclear what code in what module called our dll. This case was closed as external cause, but I wanted to dig deeper to understand what actually happened.</p>
<h1>Symptoms</h1>
<p>The crash dump did have following confusing stack trace:</p>
<pre class="code">0:001&gt; k
Child-SP          RetAddr           Call Site
0000007b`cee4fe60 0000007b`d091e970 <span class="highlight">&lt;Unloaded_myproductiondll.dll&gt;+0x18361</span>
0000007b`cee4fe68 00000000`00000000 0x7b`d091e970
</pre>
<p>The exception record didn't contain much more helpful info. The last value of instruction pointer was really pointing to address within our module (as we'll see later):</p>
<pre class="code">0:001&gt; .ecxr
rax=0000000000000001 rbx=0000007bd091e970 rcx=000007feed8f33aa
rdx=0000000000000000 rsi=0000000000000001 rdi=0000000000000000
<span class="highlight">rip=000007fee6528361</span> rsp=0000007bcee4fe60 rbp=0000000000000000
r8=0000007bcee4fe28  r9=0000000000000000 r10=0000000000000000
r11=0000000000000246 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010200
&lt;Unloaded_myproductiondll.dll&gt;+0x18361:
000007fe`e6528361 ??              ???
</pre>
<h1>Investigation steps</h1>
<p>First I tried to have a look what modules are in the address space and where does the instruction pointer point to:</p>
<pre class="code">0:001&gt; lm
(&hellip;)

Unloaded modules:
000007fe`e6340000 000007fe`e6506000   msxml3.dll
000007fe`ec700000 000007fe`ec8f3000   WININET.dll
000007fe`ed520000 000007fe`ed65d000   urlmon.dll
000007fe`eb0c0000 000007fe`eb0c5000   Normaliz.dll
000007fe`eae70000 000007fe`eb0bc000   iertutil.dll

<span class="highlight">000007fe`e6510000 000007fe`e6548000 myproductiondll.dll </span>
000007fe`ed890000 000007fe`ed8e4000   WS2_32.dll
000007fe`ed370000 000007fe`ed379000   NSI.dll 
</pre>
<p>We can see that our module (myproductiondll) is in address space, last instruction pointer value is in it&rsquo;s ranges. Also it&rsquo;s not currently loaded. So we can try to reload it the standard way:</p>
<pre class="code">0:001&gt; !sym noisy
noisy mode - symbol prompts on

0:001&gt; .reload /f myproductiondll*

0:001&gt; lmvm myproductiondll.dll
start             end                 module name

Unloaded modules:
000007fe`e6510000 000007fe`e6548000   myproductiondll.dll
    <span class="highlight">Timestamp: Fri Oct 14 12:00:00 2011 (4E98E6E2)</span>
    Checksum:  0003E937
    ImageSize:  00038000
</pre>
<p>That didn&rsquo;t work (as it doesn&rsquo;t load modules that were already unloaded from address space). However verbose output of module list (lmvm) gives us little more info about the binary &ndash; including the timestamp. This should be enough to locate the actual binary on the release share location. Let&rsquo;s point executable image path to this location (you can read more on executable image path in my <a href="http://blogs.msdn.com/b/jankrivanek/archive/2012/10/26/setting-up-windows-debugging.aspx">blog post about setting up windows debugging</a>):</p>
<pre class="code">0:001&gt; .exepath+ \\foo\bar\Vxx.x\productionbinaries
DBGHELP: Symbol Search Path: srv*c:\mysymcache*http://symbolserver
Executable image search path is: \\foo\bar\Vxx.x\productionbinaries
Expanded Executable image search path is: \\foo\bar\Vxx.x\productionbinaries
</pre>
<p>The module output gave us the memory ranges where the module was loaded &ndash; we can compute size of the module and then force reload the module by supplying base address and size of the module:</p>
<pre class="code">0:001&gt; ? 000007fe`e6548000 - 000007fe`e6510000
Evaluate expression: 229376 = 00000000`00038000

0:001&gt; .reload myproductiondll.dll=000007fe`e6510000,00000000`00038000
DBGHELP: <span class="highlight">\\foo\bar\Vxx.x\productionbinaries\myproductiondll.dll - OK</span>
DBGENG:  Partial symbol load found image \\foo\bar\Vxx.x\productionbinaries\myproductiondll.dll.
SYMSRV:  http://symbolserver/myproductiondll.pdb/F08FC95575DXXYXX9YYY8CDE0EAEBB581/file.ptr
SYMSRV:  myproductiondll.pdb from \\symbols\files: 2165760 bytes - copied         
DBGHELP: myproductiondll - private symbols &amp; lines 
        c:\mysymcache\myproductiondll.pdb\F08FC95575DXXXXYXXF8CDE0EAEBB581\myproductiondll.pdb
</pre>
<p>Now once the module is fully loaded, we can have debugger to show us the stack trace once again:</p>
<pre class="code">0:001&gt; k
SYMSRV:    No longer indexed.
SYMSRV:  c:\mysymcache\kernel32.pdb\492C273DC1724466A04XXYXX91ACC1722\kernel32.pdb not found
SYMSRV:  http://symbolserver/kernel32.pdb/492C273DC1724466A04XXYXX91ACC1722/kernel32.pdb not found
DBGHELP: kernel32.pdb - file not found
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for kernel32.dll - 
DBGHELP: kernel32 - export symbols
Child-SP          RetAddr           Call Site
0000007b`cee4fe60 000007fe`ed241668 myproductiondll!ThreadWriteFunc+0x145 [x:\foo\bar.cxx @ 118]
0000007b`cee4fe90 000007fe`ed906e5d kernel32!BaseThreadInitThunk+0x18
0000007b`cee4fec0 00000000`00000000 ntdll!RtlUserThreadStart+0x21
</pre>
<p>So now we have a caller (it&rsquo;s the same dll &ndash; so no external fault by the way), also if symbols were properly source indexed (you can read more on source indexing in my <a href="http://blogs.msdn.com/b/jankrivanek/archive/2012/10/26/setting-up-windows-debugging.aspx">blog post about setting up windows debugging</a>), then we also have code location</p>
<h1>Root cause</h1>
<p>In this particular case, the dll has a worker thread that is being spawned during construction and which exits during destruction. So destructor code needs to synchronize with the worker thread and this was the attempt to do that:</p>
<pre class="code">(...)

        fOK = ::GetExitCodeThread
                (
                m_hWorkerThread,
                &amp;dwRet
                );

        //
        //  If we get a STILL_ALIVE code back, we wait for worker to finish
        //

        if (STILL_ACTIVE == dwRet)
        {
            ::WaitForSingleObject
                    (
                    m_hWorkerExitEvent,
                    100000      // time out after 100 seconds
                    );
        }

(...)
</pre>
<p>Basically destructor is waiting on an event that the worker thread code is setting BEFORE exiting:</p>
<pre class="code">}
(...)

    if ((NULL != pLocalPub) &amp;&amp;
        (NULL != pLocalPub-&gt;m_hEntryExitEvent))
    {
        ::SetEvent
                (
                pLocalPub-&gt;m_hEntryExitEvent
                );
    }

    ::ExitThread(0);  &lt;-------------------- thread can be swapped here and never exit 
                                                       before the destructor finishes!!!

    return 0;         &lt;-------------------- location of the crash
}


</pre>
<p>This can became problematic when the destructor is automatically being called by loader when unloading the dll (e.g. when member of this dll is being used as global variable on dllmain level of a different dll, which is even more dangerous &ndash; I&rsquo;ll try to create different blog post discussing how global loader lock&nbsp;in such a case&nbsp;prevents from proper synchronization). Worker thread can be swapped right after call to ExitThread and the destructor code might be able to finish and system loader might be able to finish it&rsquo;s work and unload the dll. Then once the scheduling mechanism swaps the worker thread back it is already executing code from unloaded module.</p>
<h1>Resolution</h1>
<p>In this particular case the resolution is very easy &ndash; when waiting for thread to finish one should always wait on handle of the thread which is automatically signaled by the OS once the thread exits (instead of relying on the thread code to signal some handle during it&rsquo;s cleanup logic).</p>
<p>More generically &ndash; one should carefully synchronize between the worker threads and make sure the reference counting is always properly accomplished so that module is not being unloaded until all outstanding worker threads are done with their work.</p>]]></content:encoded>
    </item>
    <item>
      <title>StringBuilder performance issues in .NET 4.0 and 4.5</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/stringbuilder-performance-issues-in-net-4-0-and-4-5</link>
      <pubDate>Fri, 30 Nov 2012 10:31:00 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2012/11/30/stringbuilder-performance-issues-in-net-4-0-and-4-5/</guid>
      <description><![CDATA[I have recently discovered interesting behavior change of StringBuilder class introduced in .NET...]]></description>
      <content:encoded><![CDATA[<p>I have recently discovered interesting behavior change of StringBuilder class introduced in .NET framework 4.0 (and remaining in 4.5). There is also a easy workaround once you know the behavior.</p>
<h1>Sample code exposing the problem</h1>
<p>Let&rsquo;s use the following test code:</p>
<pre class="code">        static void TestStringBuilder(bool reuse)
        {
            StringBuilder sb = new StringBuilder();
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();
            for (int i = 0; i &lt; 16384; i++)
            {
                for (uint j = 0; j &lt; 32; j++)
                {
                    <span class="highlight">sb.Append("a string");</span>
                }

                string dummy = sb.ToString();

                if (reuse)
                {
                    sb.Length = 0; //sb.Clear() behaves same way
                }
                else
                {
                    sb = new StringBuilder();
                }
            }
            stopWatch.Stop();
            Console.WriteLine("Total time: {0}", stopWatch.Elapsed);
        }

        static void TestStringBuilder2(bool reuse)
        {
            StringBuilder sb = new StringBuilder();
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();
            for (int i = 0; i &lt; 16384; i++)
            {
                for (uint j = 0; j &lt; 32; j++)
                {
                    <span class="highlight">sb.Insert(0, "a string");</span>
                }

                string dummy = sb.ToString();

                if (reuse)
                {
                    <span class="highlight">sb.Length = 0;</span> //sb.Clear() behaves same way
                }
                else
                {
                    sb = new StringBuilder();
                }
            }
            stopWatch.Stop();
            Console.WriteLine("Total time: {0}", stopWatch.Elapsed);
        }


        static void Main(string[] args)
        {
            TestStringBuilder(true);
            TestStringBuilder(false);
            TestStringBuilder2(true);
            TestStringBuilder2(false);
        }

</pre>
<p>Now let&rsquo;s try to build the code while targeting .NET 3.5 (or older) and then .NET 4.0 (or newer) and see what happens when we run it.</p>
<p>Output for .NET framework 3.5 (and below):</p>
<pre class="code">Total time: 00:00:00.0251184
Total time: 00:00:00.0294951
Total time: 00:00:00.0477691
Total time: 00:00:00.0535298
</pre>
<p>Output for .NET framework 4.0 (and higher):</p>
<pre class="code">Total time: 00:00:00.0167945
Total time: 00:00:00.0281705
<span class="highlight">Total time: 00:00:11.1285809</span>
Total time: 00:00:00.0849541
</pre>
<h1>Profiling results</h1>
<p>Once you connect some .NET performance profiler (I used build-in performance profiler in VS 2012), you&rsquo;ll notice that the vast majority of the time is spent inside the StringBuilder.set_Length property:</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/3580.SbCodeProfiled_2853D4E4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/3580.SbCodeProfiled_5F00_2853D4E4.png"><img style="display: inline;" title="SbCodeProfiled" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/0336.SbCodeProfiled_thumb_64136D97.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/0336.SbCodeProfiled_5F00_thumb_5F00_64136D97.png" alt="SbCodeProfiled" width="439" height="331" border="0" /></a></p>
<p>By using some .NET decompiler tool you&rsquo;d find out that using StringBuilder.Clear() wouldn&rsquo;t make any difference as this method just calls the setter of the Length property (so is effectively identical to the code we wrote).</p>
<h1>Where is all that time spent?</h1>
<p>That should always be our next question when finding similar behavior. We have few options of how to find this out:</p>
<ul>
<li>Run performance profiler in sampling mode (to gain more extensive data) while pointing it to Microsoft symbols server in order to get detailed stack information</li>
<li>Inspect the code &ndash; by decompiling it by some .NET decompiler tool, or by pointing debugger to Microsoft symbol server and turning on source server usage.</li>
<li>Indirectly profile (AKA trace - meaning in sampling mode) the process and collect various metrics (especially .NET performance and memory ones).</li>
</ul>
<p>By first two approaches we would find out that StringBuilder.set_Length code that is being executed is actually allocating a memory (and it seems to be mainly allocating memory on Large Objects Heap). By the third approach we would find the same indirectly. Let&rsquo;s see how extensively is this code using memory and causing garbage collection:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/7282.EditedPerfMon_40_43F860DA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/7282.EditedPerfMon_5F00_40_5F00_43F860DA.png"><img style="display: inline;" title="EditedPerfMon_40" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/7215.EditedPerfMon_40_thumb_23DD541D.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/7215.EditedPerfMon_5F00_40_5F00_thumb_5F00_23DD541D.png" alt="EditedPerfMon_40" width="457" height="639" border="0" /></a></p>
<p>We can see that the offending code is spending enormous amount of time inside the Garbage collection and that almost all memory being allocated is actually being allocated at Large Objects Heap. If we would compare this with behavior in .NET 3.5:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/8875.EditedPerfMon_35_07CC9532.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/8875.EditedPerfMon_5F00_35_5F00_07CC9532.png"><img style="display: inline;" title="EditedPerfMon_35" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/5732.EditedPerfMon_35_thumb_67B18874.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/5732.EditedPerfMon_5F00_35_5F00_thumb_5F00_67B18874.png" alt="EditedPerfMon_35" width="445" height="531" border="0" /></a></p>
<p>We would see more expectable results &ndash; reusing temporary variables between multiple iterations of small cycles causes garbage collection to do more work (as those local variables get promoted to higher generations and as a result garbage collection needs to collect and compact more generations in order to obtain enough memory), however it&rsquo;s not so prohibitively expensive to do so.</p>
<h1>Why is this happening?</h1>
<p>If you would inspect code of StringBuilder in .NET framework 3.5 or earlier and compare it to StringBuilder implementation in .NET framework 4.0 or newer you would find out that previously the StringBuilder internal memory storage was implemented as one big array (this was done to optimize ToString operation which basically just gave the &lsquo;pointer&rsquo; to the internal buffer), however this was changed to internally use linked list (in order to optimize appending/inserting operations).</p>
<p>The set_Length implementation then in case of shortening the StringBuilder tries to allocate chunk of memory to perform some consolidation of some nodes that should be left in string builder after shortening. However it fails to recognize that we&rsquo;re trying to completely erase the internal buffer (as the Insert operation prepends the nodes to the list rather then appends &ndash; and set_Length logic doesn&rsquo;t count on that). As a result set_Lenght will do some memory allocation and copying that will be actually thrown away right after that.</p>
<h1>What&rsquo;s the workaround for this?</h1>
<p>As already mentioned &ndash; we shouldn&rsquo;t generally reuse temporary local variables between short cycles with multiple iterations. That way we only cause those local variables to be promoted to higher generations and this will lead to more work for Garbage Collector (Unless we know that we will want the reuse the same variable multiple times even between different calls to the same function &ndash; then we might consider implementing some sort of Resource Pool design pattern). So we should simply use new StringBuilder in each iteration and this way we wouldn&rsquo;t hit the problem.</p>]]></content:encoded>
    </item>
    <item>
      <title>Setting up managed code debugging (with SOS and SOSEX)</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/setting-up-managed-code-debugging-with-sos-and-sosex</link>
      <pubDate>Thu, 15 Nov 2012 18:56:07 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2012/11/15/setting-up-managed-code-debugging-with-sos-and-sosex/</guid>
      <description><![CDATA[In this post I want to share some basics on how to set-up managed code debugging in windows...]]></description>
      <content:encoded><![CDATA[<p>In this post I want to share some basics on how to set-up managed code debugging in windows debugger. I’ll consider that reader already know basics about how to <a href="http://blogs.msdn.com/b/jankrivanek/archive/2012/10/26/setting-up-windows-debugging.aspx">setup windows debugging</a>. I’ll mainly discuss details about what and how need to be loaded in order to be able to use specialized extensions for managed code debugging (mainly sos and sosex). I’ll not be discussing the debugging itself (e.g. commands from sos and sosex extensions).</p>  <h2>Local live debugging</h2>  <p>In the case of local live debugging we don’t have to much obstacles in our way (since we have access to the used .net framework binaries and data access layers – those I’ll discuss later). You should make sure that you are using same bitness of debugger and extension as is the bitness of the target application (.NET application are usually built architecture agnostic and then run as 32 bit processes on 32 bit OS and 64 bit processes on 64 bit OS).</p>  <p>If you would be about to use mismatching bitness of debugger to attach/start the managed process you might see the following error:</p>  <p>&#160;</p>  <pre class="code">---------------------------
WinDbg:6.13.0014.1618 X86 
---------------------------
Could not create process '\\Foo\Bar\Baz.exe', Win32 error 0n50

The request is not supported.
---------------------------
OK   
---------------------------
</pre>

<h2>SOS extension</h2>

<p>Once you are in the debugger, broken in into the target process, you will want to load sos extension (name is a acronym for ‘Son Of Strike’ as the extension was developed as the next version of the extension named ‘Strike’). This extension is shipped with .NET framework (and also indexed on Microsoft symbols server which will come in handy for other scenarios). An old version (1.1) of this extension also ships with the debugger package – and this can actually get into your way.</p>

<p>For .NET framework 1.1 the SOS extension has all the information about .NET internals ‘baked-in’ itself (some of those information you would normally expect to be in the symbol files) – so all you needed was loading the SOS. From the .NET version 2.0 the definition of data structures was separated into separate binary – <strong>mscordacwks</strong> (sometimes called data access layer). When loading the SOS extension it will need to also load correct data access layer (for correct architecture and version of .NET framework targeted by the debugged application/dump). Another important change for our purposes was introduced in .NET framework 4.0 – the <strong>mscorwks.dll</strong> module was replaced with module <strong>clr.dll</strong>. I’ll show how all those facts affects the managed code debugging.</p>

<h3>Loading SOS extension during live local debugging</h3>

<p>During the live local debugging you have the luxury of knowing that you computer has the same version and architecture of .NET framework as what is the debugged target using (because they are on the same machine :-)). Same approach also applies when you are debugging remotely or postmortem but know for sure that you have same architecture and version on your computer as what was on the target computer.</p>

<p>In this case you know that your installation of .NET framework will also have appropriate version SOS extension and mscordacwks.dll binary. They will actually in the same folder as where is the core dll for .NET – mscorwks.dll (for .NET up to 4.0) or clr.dll (for .NET 4.0 and higher) in that case you can simply use <strong>loadby</strong> command that will load an extension from a location of another binary that is already loaded in the debugger (so you need to make sure those modules are already loaded, or force load the with ‘.load /f &lt;module&gt;’):</p>

<pre class="code">.loadby sos.dll mscorwks
.loadby sos.dll clr
</pre>

<h3>Loading SOS extension during postmortem or remote debugging</h3>

<p>If you would try previous commands on remote or postmortem debugging, you might happen to see the following error message:</p>

<pre class="code">The version of SOS does not match the version of CLR you are debugging.  Please
load the matching version of SOS for the version of CLR you are debugging.
CLR Version: 4.0.30319.19010
SOS Version: 4.0.30319.269
Failed to load data access DLL, 0x80004005
Verify that 1) you have a recent build of the debugger (6.2.14 or newer)
            2) the file mscordacwks.dll that matches your version of clr.dll is 
                in the version directory
            3) or, if you are debugging a dump file, verify that the file 
                mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.
            4) you are debugging on the same architecture as the dump file.
                For example, an IA64 dump file must be debugged on an IA64
                machine.

You can also run the debugger command .cordll to control the debugger's
load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.
If that succeeds, the SOS command should work on retry.

If you are debugging a minidump, you need to make sure that your executable
path is pointing to clr.dll as well.
</version></arch></arch></pre>

<p> The message itself already contains few helpful tips. However if you meet this situation you might be able to easily resolve majority of those cases by leveraging the fact that .NET Framework debugging related binaries are being indexed on Microsoft symbol server – internal and also external (for more info or symbol server, setting symbols path and execution image path you can refer to the <a href="http://blogs.msdn.com/b/jankrivanek/archive/2012/10/26/setting-up-windows-debugging.aspx">setup windows debugging</a> blog post). Basically for remote or postmortem debugging of managed application/dump in windows debugger you should follow those simple steps:</p>

<blockquote>
  <p>1) Set your symbol path to Microsoft symbol server (internal or external)</p>

  <p>2) Set your execution image path to Microsoft symbol server</p>

  <p>3) Make sure that the core CLR binary (mscorwks.dll or clr.dll) is loaded, reload it (with .reload /f) if it’s not</p>

  <p>4) Reload CLR debugging related binaries by using ‘.cordll –ve –u –l’ command (-ve for verbose, –u for unload, –l for load)</p>

  <p>5) Load the SOS module from the core CLR binary location (as you’d do that for live debugging)</p>
</blockquote>

<p> Here is the example debugging session demonstrating those steps:</p>

<pre class="code">Microsoft (R) Windows Debugger Version 6.13.0014.1618 X86
Copyright (c) Microsoft Corporation. All rights reserved.


Loading Dump File [\\XXXXXX\FooBar_121030_075015.dmp]
User Mini Dump File with Full Memory: Only application data is available

Comment: '
*** Procdump   -ma FooBar.exe
*** Manual dump'
Symbol search path is: YYYYYYYYY
Executable search path is: 
Windows 7 Version 7601 (Service Pack 1) MP (32 procs) Free x86 compatible
Product: Server, suite: TerminalServer DataCenter SingleUserTS
Machine Name:
Debug session time: Tue Oct 30 06:50:17.000 2012 (UTC - 8:00)
System Uptime: 16 days 19:11:53.254
Process Uptime: 0 days 23:42:59.000
................................................................
..................................................
Loading unloaded module list
.
<span class="highlight">Cannot Automatically load SOS</span>
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=000001ec edi=00000000
eip=7784f8b1 esp=0044f164 ebp=0044f1d0 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
ntdll!ZwWaitForSingleObject+0x15:
7784f8b1 83c404          add     esp,4

0:000&gt; !eeversion
<span class="highlight">No export eeversion found</span>

0:000&gt; .symfix+
0:000&gt; .sympath
Symbol search path is: SRV*http://msdl.microsoft.com/download/symbols
Expanded Symbol search path is: srv*http://msdl.microsoft.com/download/symbols

0:000&gt; .exepath SRV*http://msdl.microsoft.com/download/symbols
Executable image search path is: SRV*http://msdl.microsoft.com/download/symbols
Expanded Executable image search path is: srv*http://msdl.microsoft.com/download/symbols

0:000&gt; lmvm mscorwks
start    end        module name
709f0000 70f9b000   mscorwks   <span class="highlight">(deferred)</span>             
    Image path: C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
    Image name: mscorwks.dll
    Timestamp:        Sat Dec 24 22:48:13 2011 (4EF6C72D)
    CheckSum:         005B474B
    ImageSize:        005AB000
    File version:     2.0.50727.5456
    Product version:  2.0.50727.5456
    File flags:       0 (Mask 3F)
    File OS:          4 Unknown Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® .NET Framework
    InternalName:     mscorwks.dll
    OriginalFilename: mscorwks.dll
    ProductVersion:   2.0.50727.5456
    FileVersion:      2.0.50727.5456 (Win7SP1GDR.050727-5400)
    FileDescription:  Microsoft .NET Runtime Common Language Runtime - WorkStation
    LegalCopyright:   © Microsoft Corporation.  All rights reserved.
    Comments:         Flavor=Retail
    
0:000&gt; .reload /f mscorwks.dll

0:000&gt; lmvm mscorwks
start    end        module name
709f0000 70f9b000   mscorwks   (private pdb symbols)  D:\Debuggers_32bit\sym\mscorwks.pdb\7139B75336C24F7CAA1DC4060608770D2\mscorwks.pdb
    Loaded symbol image file: mscorwks.dll
    Image path: C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
    Image name: mscorwks.dll
    Timestamp:        Sat Dec 24 22:48:13 2011 (4EF6C72D)
    CheckSum:         005B474B
    ImageSize:        005AB000
    File version:     2.0.50727.5456
    Product version:  2.0.50727.5456
    File flags:       0 (Mask 3F)
    File OS:          4 Unknown Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft® .NET Framework
    InternalName:     mscorwks.dll
    OriginalFilename: mscorwks.dll
    ProductVersion:   2.0.50727.5456
    FileVersion:      2.0.50727.5456 (Win7SP1GDR.050727-5400)
    FileDescription:  Microsoft .NET Runtime Common Language Runtime - WorkStation
    LegalCopyright:   © Microsoft Corporation.  All rights reserved.
    Comments:         Flavor=Retail
    
0:000&gt; .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscordacwks.dll:2.0.50727.6387 f:0
doesn't match desired version 2.0.50727.5456 f:0
CLRDLL: Unable to find '' on the path
Cannot Automatically load SOS
<span class="highlight">CLRDLL: Loaded DLL D:\Debuggers_32bit\sym\mscordacwks_x86_x86_2.0.50727.5456.dll\4EF6C72D5ab000\mscordacwks_x86_x86_2.0.50727.5456.dll
CLR DLL status: Loaded DLL D:\Debuggers_32bit\sym\mscordacwks_x86_x86_2.0.50727.5456.dll\4EF6C72D5ab000\mscordacwks_x86_x86_2.0.50727.5456.dll</span>

0:000&gt; .loadby sos mscorwks

0:000&gt; !eeversion
2.0.50727.5456 retail
Workstation mode
SOS Version: 2.0.50727.6387 retail build
</pre>

<p>This should help you resolve vast majority of issues with loading modules for managed debugging, there still might be some special cases where you can see issues.</p>

<h3>Issues with loading SOS extension and related binaries</h3>

<p>One of those can happen when the mscordacwks is not properly indexed on the symols server. Then ouw would see similar output after running .cordll:</p>

<pre class="code">3: kd&gt; .cordll -ve -u -l
CLRDLL: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\mscordacwks.dll:4.0.30319.269 f:8
doesn't match desired version 4.0.30319.19010 f:8
CLRDLL: Unable to find 'R?' on the path
CLRDLL: Unable to find 'mscordacwks_AMD64_AMD64_4.0.30319.19010.dll' on the path
CLRDLL: Unable to get version info for '\\ntdev.corp.microsoft.com\wincorerelease13\fbl_core1_kernel\9257.0.121021-1715\amd64fre\bin\indexes\..\netfx\core\win\microsoft.net\framework64\v4\mscordacwks_AMD64_AMD64_4.0.30319.19010.dll', Win32 error 0n87
CLRDLL: ERROR: Unable to load DLL mscordacwks_AMD64_AMD64_4.0.30319.19010.dll, Win32 error 0n87
<span class="highlight">CLR DLL status: ERROR: Unable to load DLL mscordacwks_AMD64_AMD64_4.0.30319.19010.dll, Win32 error 0n87</span>
</pre>

<p>This can potentially happen to you inside Microsoft when working with specific build of .NET Framework that wasn’t properly indexed (or the index was already purged) – it should never happen outside of Microsoft. If this happens, contact CLR team, as they have their own share for the build binaries that might have longer data retention.</p>

<p>&#160;</p>

<p>Other – most common – issue can happen when you accidentally load the improper version of sos (e.g. from the debugger package, by simply running ‘.load sos’ and picking some wrong version that is in the path). .chain command can help reveal those issues:</p>

<pre class="code">0:000&gt; !eeversion
Doesn't work with 2.x
0:000&gt; .chain
Extension DLL search Path:
    D:\Debuggers_32bit\WINXP;D:\Debuggers_32bit\winext;D:\Debuggers_32bit\winext\arcade;D:\Debuggers_32bit\pri;D:\Debuggers_32bit;D:\Debuggers_32bit\winext\arcade;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Windows Kits\8.0\Windows Performance Toolkit\;C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;C:\Program Files (x86)\Odd\;C:\Program Files (x86)\Microsoft SQL Server\110\Tools\Binn\ManagementStudio\;C:\Program Files (x86)\Microsoft SQL Server\110\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\110\DTS\Binn\;C:\Program Files\Microsoft SQL Server\110\DTS\Binn\;D:\Debuggers_64bit
Extension DLL chain:
    <span class="highlight">D:\Debuggers_32bit\clr10\sos: image 6.13.0014.1618, API 1.0.0, built Wed Oct 17 10:41:23 2012
        [path: D:\Debuggers_32bit\clr10\sos.dll]</span>
    dbghelp: image 6.13.0014.1618, API 6.2.6, built Wed Oct 17 10:42:02 2012
        [path: D:\Debuggers_32bit\dbghelp.dll]
    ext: image 6.13.0014.1618, API 1.0.0, built Wed Oct 17 10:41:49 2012
        [path: D:\Debuggers_32bit\winext\ext.dll]
    exts: image 6.13.0014.1618, API 1.0.0, built Wed Oct 17 10:42:19 2012
        [path: D:\Debuggers_32bit\WINXP\exts.dll]
    uext: image 6.13.0014.1618, API 1.0.0, built Wed Oct 17 10:42:18 2012
        [path: D:\Debuggers_32bit\winext\uext.dll]
    ntsdexts: image 6.2.9255.0, API 1.0.0, built Wed Oct 17 10:42:28 2012
        [path: D:\Debuggers_32bit\WINXP\ntsdexts.dll]
</pre>

<h2>Loading SOSEX extension</h2>

<p>SOSEX is helpful extension that extends possibilities of SOS. You first need to have SOS loaded – as described above. Then you can simply download latest version of SOSEX from <a title="http://www.stevestechspot.com/" href="http://www.stevestechspot.com/">http://www.stevestechspot.com/</a> and load it into the debugger and use it:</p>

<pre class="code">0:000&gt; .load D:\Tools\sosex\32bit\sosex
This dump has no SOSEX heap index.
The heap index makes searching for references and roots much faster.
To create a heap index, run !bhi
0:000&gt; !sosex.help
SOSEX - Copyright 2007-2012 by Steve Johnson - http://www.stevestechspot.com/
To report bugs or offer feedback about SOSEX, please email sjjohnson@pobox.com
Quick Ref:
--------------------------------------------------
bhi       [filename]                                     BuildHeapIndex - Builds an index file for heap objects.
bpsc      (Deprecated.  Use !mbp instead)
chi                                                      ClearHeapIndex - Frees all resources used by the heap index and removes it from memory.
dlk       [-d]                                           Displays deadlocks between SyncBlocks and/or ReaderWriterLocks
dumpgen   &lt;GenNum&gt; [-free] [-stat] [-type &lt;TYPE_NAME&gt;]   Dumps the contents of the specified generation
                   [-nostrings]
finq      [GenNum] [-stat]                               Displays objects in the finalization queue
frq       [-stat]                                        Displays objects in the Freachable queue
gcgen     &lt;ObjectAddr&gt;                                   Displays the GC generation of the specified object
gch       [HandleType]...                                Lists all GCHandles, optionally filtered by specified handle types
help      [CommandName]                                  Display this screen or details about the specified command
lhi       [filename]                                     LoadHeapIndex - load the heap index into memory.
mbc       &lt;SOSEX breakpoint ID | *&gt;                      Clears the specified or all managed breakpoints
mbd       &lt;SOSEX breakpoint ID | *&gt;                      Disables the specified or all managed breakpoints
mbe       &lt;SOSEX breakpoint ID | *&gt;                      Enables the specified or all managed breakpoints
mbl       [SOSEX breakpoint ID]                          Prints the specified or all managed breakpoints
mbm       &lt;Type/MethodFilter&gt; [ILOffset] [Options]       Sets a managed breakpoint on methods matching the specified filter
mbp       &lt;SourceFile&gt; &lt;nLineNum&gt; [ColNum] [Options]     Sets a managed breakpoint at the specified source code location
mdso      [Options]                                      Dumps object references on the stack and in CPU registers in the current context
mdt       [TypeName | VarName | MT] [ADDR] [Options]     Displays the fields of an object or type, optionally recursively
mdv       [nFrameNum]                                    Displays arguments and locals for a managed frame
mfrag     [-stat] [-mt:&lt;MT&gt;]                             Reports free blocks, the type of object following the free block, and fragmentation statistics
mframe    [nFrameNum]                                    Displays or sets the current managed frame for the !mdt and !mdv commands
mgu       // TODO: Document
mk        [FrameCount] [-l] [-p] [-a]                    Prints a stack trace of managed and unmanaged frames
mln       [expression]                                   Displays the type of managed data located at the specified address or the current instruction pointer
mlocks    [-d]                                           Lists all managed lock objects and CriticalSections and their owning threads
mroot     &lt;ObjectAddr&gt; [-all]                            Displays GC roots for the specified object
mt        (no parameters)                                Steps into the managed method at the current position
mu        [address] [-s] [-il] [-n]                      Displays a disassembly around the current instruction with interleaved source, IL and asm code
muf       [MD Address | Code Address] [-s] [-il] [-n]    Displays a disassembly with interleaved source, IL and asm code
mwaits    [-d]                                           Lists all waiting threads and, if known, the locks they are waiting on
mx        &lt;Filter String&gt;                                Displays managed type/field/method names matching the specified filter string
refs      &lt;ObjectAddr&gt; [-target|-source]                 Displays all references from and to the specified object
rwlock    [ObjectAddr | -d]                              Displays all RWLocks or, if provided a RWLock address, details of the specified lock
sosexhelp [CommandName]                                  Display this screen or details about the specified command
strings   [ModuleAddress] [Options]                      Search the managed heap or a module for strings matching the specified criteria

ListGcHandles - See gch

Use !help &lt;command&gt; or !sosexhelp &lt;command&gt; for more details about each command.
</pre>

<p>&#160;</p>

<p>&#160;</p>

<p>That’s it today. Now you are introduced to setting up managed code debugging and next time I’ll try to post some real live interesting case scenario.</p>]]></content:encoded>
    </item>
    <item>
      <title>Setting up Windows debugging</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/setting-up-windows-debugging</link>
      <pubDate>Fri, 26 Oct 2012 14:12:51 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2012/10/26/setting-up-windows-debugging/</guid>
      <description><![CDATA[Below I want to share a basics of setting up debugging in one of the debuggers from Debugging Tools...]]></description>
      <content:encoded><![CDATA[<p>Below I want to share a basics of setting up debugging in one of the debuggers from Debugging Tools for Microsoft Windows (WinDbg, ntsd, cdb or kd). After performing all of the recommended steps, the session should be hopefully ‘actionable’ – ready for the actual debugging (that I’ll not discuss in this post – more about this later in individual case scenarios). In environments where debugging is a daily routine, those initialization steps should be preferably automated (through <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff540525(v=vs.85).aspx">dbgeng.dll API</a>, managed code or PowerShell scripting while using <a href="http://mdbglib.codeplex.com/">third party managed wrapper of dbgeng.dll</a>, dbgeng.dll API, custom wrapper of dbgeng.dll (I created a simple one with mostly P/Invoke calls to the native library), or so).</p>   <h2>Creating a debugging session</h2>  <p>First of all one must decide the way how he/she will be debugging:</p>  <ul>   <li>Live local user mode debugging</li>    <li>Live remote user mode debugging</li>    <li>Post-mortem (dump) debugging</li>    <li>Kernel mode debugging (live, or dump)</li>    <li>Live user mode debugging piped through kernel mode session</li>    <li>Debugging live user mode process in kernel mode context</li> </ul>  <p>Lets have a quick look on each of those – how can we setup those, and when to choose which</p>  <h3>Live local user mode debugging</h3>  <p>Live local user mode debugging is most common in a typical development scenario. Easiest way how to set such a session is by breaking into the existing running process (in WinDbg use File –&gt; Attach to a process; or by running &lt;debugger&gt; –p &lt;PID&gt; or &lt;debugger&gt; –pn &lt;process name&gt;), or by directly starting the process under the debugger (File –&gt; Open Executable; or by running &lt;debugger&gt; &lt;commandline&gt;). But there are also some more interesting ways of creating live user mode session.</p>  <p><strong>Have the OS start the process under the debugger</strong> whenever a particular process would be about to start. This can be done by setting the ‘debugger’ Executable Image global OS flag. There are few ways how to set this flag (direct registry edit, in kernel debugging session, command line), but the most intuitive is by using <em>Global Flags</em> UI editor (gflags.exe executable in Debuggers package):</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/0624.gflags_6BCD8F54.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/0624.gflags_5F00_6BCD8F54.png"><img title="gflags" style="display: inline;" border="0" alt="gflags" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/56/29/metablogapi/1205.gflags_thumb_2B9775DA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-56-29-metablogapi/1205.gflags_5F00_thumb_5F00_2B9775DA.png" width="244" height="241" /></a> </p>  <p><strong>Have the OS attach the debugger to the process with unhandled exception</strong> before it would be forcefully terminated. This OS behavior (applied to all native user mode processes) can be controlled by AeDebug registry key:</p>  <p><span class="code">HKLM\Software\Microsoft\Windows NT\CurrentVersion\AeDebug</span></p>  <p>First add or edit the ‘Auto’ string value and set it to “1” for automatic attachment of debugger without prompt dialog to user. Then add or edit the ‘Debugger’ string value to the full path to the debugger (relative paths are not recommendable as OS starts the debugger under the context of the crashing app – so the working directory might vary). For more detailed description of this setting visit <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb204634(v=vs.85).aspx">msdn</a>. Same registry key can be also updated by using the –iae (install AeDebugger) or –iaec (install AeDebugger with commandline) switches of user mode windows debugger of your choice (cdb, ntsd, windbg).     <br />To automatically attach to crashing managed applications edit:</p>  <p><span class="code">HKLM\Software\Microsoft\.NETFramework</span></p>  <p>First add or edit DbgJITDebugLaunchSettings string value of “2” and then add or edit DbgManagedDebugger string value to full path to the debugger. For more information about this setting of managed application post mortem debugging visit <a href="http://msdn.microsoft.com/en-us/library/2ac5yxx6(v=vs.71).aspx">msdn</a>.</p>  <p><strong>Attaching manually but noninvasively</strong>. In this mode the debugger can ‘read’ the state of the process (memory, registry …), however it cannot ‘write’. In order to be able to change process state – and so fully debug the process – <em>Debug port</em> member of the object representation of a process I kernel needs to point to a <em>Debug object</em> (kernel mode structure) which can be associated only with one debugger process at any point of time. Therefore only one debugger can invasively debug a user mode process. Noninvasive debugging doesn’t need to communicate through Debug port (as it only reads the state of the process) – so we can achieve scenario where we debug process with multiple debuggers (e.g. we want to issue few diagnostics commands from windbg, while we are debugging from Visual Studio). Also this way we don’t have to be worry about possibility of corrupting state of the debugged process. For noninvasive debugging use the ‘-pv’ switch:</p>  <p><span class="code">&lt;debugger&gt; –pv –p &lt;PID&gt;      <br />&lt;debugger&gt; –pv –pn &lt;ProcessName&gt;</span></p>  <h3>Live remote user mode debugging</h3>  <p>Once you have live user mode debugging set up you can share this session remotely via the TCP/IP or named pipes protocol (or also 1394 or COM transport protocols – but those are much more common for kernel mode debugging). Some of the techniques (with the exception of smart client debugging) can be used to share dump debugging session. There are several ways of doing this.</p>  <h4>Starting debugging server</h4>  <p>In your debugging session to be shared start the debugging server:</p>  <p><span class="code">.server &lt;protocol&gt;:&lt;parameters&gt;[,IcfEnable]</span></p>  <p>where protocol is either ‘tcp’ or ‘npipe’ and paramaters are either ‘port=&lt;port&gt;’ or ‘pipe=&lt;pipeName&gt;’. <strong>IcfEnable</strong> is optional convenient parameter which will case debugger to create the needed firewall exceptions on your behalf.&#160; <br />Alternatively you can set up the debugging server at the time of starting the debugger:</p>  <p><span class="code">windbg –server tcp:port=55555,IcfEnable notepad.exe</span></p>  <p>To list the debuggers server in your session use the <em>.servers</em> commands (helpful for easy copy-paste sharing of the remote with the person expected to perform the remote debugging):</p>  <p><span class="code">0:000&gt; .servers      <br /> On the client, use any of these command lines       <br /> 0: &lt;debugger&gt; -remote tcp:Port=12345,Server=JAKRIVAN-DEV</span></p>  <p>From the output it should be clear how to attach to the remote session.</p>  <p>Remember that if you are starting the debugger server, the debugging engine (dbgeng.dll) actually runs on the remote machine – and so also all the symbols and sources resolution or other file system operation are relative to the remote system (there are ways how to send the symbols to remote machine through debugging session, or how to have sources loaded on local machine – I’ll discus those later in sections on symbols and sources resolving)</p>  <h4>Starting debugging proxy server and smart client debugger</h4>  <p>In situations where we would want the debugging engine to run on the local machine (usually due to access to symbols and sources – typical scenario when debugging live issue from customer machine) <em>Debugging Proxy</em> is our friend. Debugging proxy started on the remote machine (needs to have the debug privilege) communicates with the remote debuggers via a low level protocol (memory reads, memory writes etc.) – and all the actual debugging logic is happening on the server side. To start the Debugging Proxy run on the remote machine:</p>  <p><span class="code">dbgsrv.exe –t &lt;transport&gt;:&lt;parameters&gt;</span></p>  <p>To connect to the remote Debugger Server run:</p>  <p><span class="code">&lt;debugger&gt; -premote &lt;transport&gt;:&lt;parameters&gt;,server=&lt;remote server&gt;</span>&#160;</p>  <p>You can connect to the actual process later or at the time of starting the debugger:</p>  <p><span class="code">&lt;debugger&gt; –premote &lt;transport&gt;:&lt;parameters&gt;,server=&lt;remote server&gt; –p &lt;remote process PID&gt;</span></p>  <p>Debugging via Debugger Proxy is very similar technique to what the Visual Studio uses for native mode debugging and managed debugging in version 2012. Managed mode debugging in older versions of Visual Studio are more similar to the basic remote debugging (so symbols need to be available from remote machine …).</p>  <h4>Piping console input output through remote.exe</h4>  <p>Another way of remote debugging is by using the remote.exe executable – since this executable is just remotely redirecting input and output it is then impossible to use the debugger engine API or windbg rich user interface to interact with the debugger. Therefore this way is recommendable in a very few limited scenarios (like e.g. when you need to see the actual output of the debugger when it’s initializing itself and may be failing due to various reasons – like incorrect arguments, insufficient privileges or resources etc. – then the native transport mechanism (the debugger server) wouldn’t even start at all, but the remote.exe can already transfer the output). In those special scenarios it might be helpful to use both remoting mechanisms when starting the debugger:</p>  <p><span class="code">remote.exe /s &quot;&lt;debugger&gt; -server &lt; transport&gt;:&lt;parameters&gt;,IcfEnable &lt;debugger parameters&gt;&quot; &lt; pipename&gt;</span></p>  <p>And to connect to such a remote server you can either use either the remote.exe: </p>  <p><span class="code">remote.exe /c &lt;server name&gt; &lt;pipename&gt;</span></p>  <p>Or the debugger itself (which is preferable as then you have all API and windbg UI richness available): </p>  <p><span class="code">&lt;debugger&gt; –remote &lt;transport&gt;:&lt;parameters&gt;,server=&lt;server name&gt;</span></p>  <p>One beauty of remote.exe is that it can redirect console input/output of an arbitrary application – so even though it’s usually mentioned in connection with debugging, you can effectively use it for task like redirecting diagnostic output of a remote automated task (compiling, installation …).</p>  <h4>Piping console input and output through live kd session</h4>  <p>This is technically a live user mode remote debugging technique, but due it’s specific behavior I’ll discuss it as a separate debugging technique.</p>  <p>&#160;</p>  <h3>Post-mortem debugging</h3>  <p>In order to be able to debug the issue post-mortem, one needs to have a memory dump of process (or system) do debug. There are few ways how to take a memory dump:</p>  <p><strong>Taking memory dump in the Task Manager</strong> – this is least recommendable way, since one cannot influence the information that will be included/excluded in the dump and also on 64bit systems Task Manager takes 64bit dump of 32bit processes which will unnecessary complicate the debugging.</p>  <p><strong>Take the memory dump in live debugging session</strong> – we can use the .dump command for this. You can consult debuggers help for the detailed options, but I usually recommend taking minidump with all advanced options with overwriting possible existing file:</p>  <p><span class="code">.dump /ma /o &lt;dump path with extension&gt; </span></p>  <p><strong>Take the dump with one of the SysInternals tools</strong> – Process Explorer, or probably most preferably <em>ProcDump</em> (it can also take dumps based on several conditions like 1st &amp; 2nd chance exceptions, application hangs etc.)</p>  <h3>Kernel mode debugging</h3>  <p>Kernel mode debugging is a very large topic – so I’ll probably dedicate some post to it in future. Now just let me not that you can either debug real physical machine – over the serial, usb or fire-wire cable or recently also over the TCP/IP (with the introduction of the kdnet) – more on setting up kernel debugging please see the <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/hh439346(v=vs.85).aspx">msdn</a>. You can also debug the virtual machine over the <a href="http://support.microsoft.com/kb/871171">synthetic serial port</a>. But probably the most convenient way how to try kernel debugging (as it doesn't require any setup on the target machine – so you mainly don’t need to reboot!) is <a href="http://technet.microsoft.com/en-us/sysinternals/bb897415.aspx">LiveKd</a>. From very simplistic point of view LiveKd basically opens a memory as a dump file and this way it can trick the kernel debugger to debug the local system. This is especially helpful when we need to debug local user mode process in kernel mode context (e.g. to troubleshoot synchronization issues).</p>  <h3>Live user mode debugging piped through kernel mode session</h3>  <p>There might be situations where local or remote user mode debugging is impossible – e.g. due to very limited resources (machine under load or stress test) or to debug early boot-up processes where no session is yet available to start the console debugger. For those cases one viable way of debugging is by setting up a post-mortem debugger (as described in Live local user mode debugging) and setting it to ‘ntsd –d’, e.g. by running:</p>  <p><span class="code">ntsd –iaec -d</span></p>  <p>That way once the debugger starts it immediately redirects its input and output through kernel mode session (while rest of the OS freezes, unless there is some activity required by debugger – e.g. loading symbols, traversing memory etc.).</p>  <p>Because the kernel mode session needs to be set up and healthy and the user mode debugger just redirects it’s input and output through it – this approach combines disadvantages of kernel mode debugging (problems with maintaining infrastructure for debugging, keeping sessions in sync …) and redirecting input and output (no way of direct interacting with dbgeng.dll API, no advanced UI functionality of windbg) – and so it should be only used in cases where you have no other choice (early bootup processes debugging, processes on machine with limited resources).</p>  <h3>Debugging live user mode process in the kernel mode context</h3>  <p>In some cases the kernel mode debugging session can offer more information then simple usermode session. Let’s have a quick look on a example how can we debug usermode process (e.g. in livekd):</p>  <p>By !process command, we can query currently running processes (second parameter is a byte flag that influences the number of information spewed out):</p>  <pre class="code">  

0: kd&gt; !process 0 0 cmd.exe
PROCESS fffffa8008136740
    SessionId: 1  Cid: 1564    Peb: 7f7d8dd8000  ParentCid: 0c94
    DirBase: 150382000  ObjectTable: fffff8a014733400  HandleCount: 119.
    Image: cmd.exe

PROCESS fffffa800b4c5940
    SessionId: 1  Cid: 12b4    Peb: 7f7d8a26000  ParentCid: 0c94
    DirBase: 3435b000  ObjectTable: fffff8a0123f0e80  HandleCount:  19.
    Image: cmd.exe

PROCESS fffffa8011f9d940
    SessionId: 1  Cid: 1440    Peb: 7f7d835f000  ParentCid: 0c94
    DirBase: c1209000  ObjectTable: 00000000  HandleCount:   0.
    Image: cmd.exe

0: kd&gt; !process 0 0 visio.exe
PROCESS fffffa800d3666c0
    SessionId: 1  Cid: 1558    Peb: 7ea05000  ParentCid: 18cc
    DirBase: 4b4d2000  ObjectTable: fffff8a0133418c0  HandleCount: 222.
    Image: VISIO.EXE

0: kd&gt; !process 0 <span class="highlight">f</span> visio.exe
PROCESS <span class="highlight">fffffa800d3666c0</span>    SessionId: 1  Cid: 1558    Peb: 7ea05000  ParentCid: 18cc
    DirBase: 4b4d2000  ObjectTable: fffff8a0133418c0  HandleCount: 222.
    Image: VISIO.EXE
    VadRoot fffffa800c1d30f0 Vads 216 Clone 0 Private 1531. Modified 240. Locked 0.
    DeviceMap fffff8a001c1cf70
    Token                             fffff8a015a59050
    ElapsedTime                       3 Days 01:38:29.603
    UserTime                          00:00:00.187
    KernelTime                        00:00:02.449
    QuotaPoolUsage[PagedPool]         607592
    QuotaPoolUsage[NonPagedPool]      27328
    Working Set Sizes (now,min,max)  (3892, 50, 345) (15568KB, 200KB, 1380KB)
    PeakWorkingSetSize                6260
    VirtualSize                       307 Mb
    PeakVirtualSize                   327 Mb
    PageFaultCount                    61551
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      2627

        THREAD fffffa8006e6bb00  Cid 1558.1994  Teb: 000000007e8da000 Win32Thread: 0000000000000000 WAIT: (WrQueue) UserMode Non-Alertable
            fffffa8008c74b00  QueueObject
        Not impersonating
        DeviceMap                 fffff8a001c1cf70
        Owning Process            fffffa800d3666c0       Image:         VISIO.EXE
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      3710528        Ticks: 13765657 (2:11:39:05.625)
        Context Switch Count      5              IdealProcessor: 2
        UserTime                  00:00:00.000
        KernelTime                00:00:00.000
        Win32 Start Address 0x00000000728b14aa
        Stack Init fffff88007f2cdd0 Current fffff88007f2c7a0
        Base fffff88007f2d000 Limit fffff88007f27000 Call 0
        Priority 10 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
        Kernel stack not resident.
        Child-SP          RetAddr           Call Site
        fffff880`07f2c7e0 fffff801`98f2b99c nt!KiSwapContext+0x76
        (Inline Function) --------`-------- nt!KiSwapThread+0xf4 (Inline Function @ fffff801`98f2b99c)
        fffff880`07f2c920 fffff801`98f36ddb nt!KiCommitThreadWait+0x23c
        fffff880`07f2c9e0 fffff801`992ceb6c nt!KeRemoveQueueEx+0x26b
        fffff880`07f2ca90 fffff801`992adcb5 nt!IoRemoveIoCompletion+0x4c
        fffff880`07f2cb20 fffff801`98f00d53 nt!NtRemoveIoCompletion+0x135
        fffff880`07f2cbd0 00000000`76fe2ad2 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`07f2cc40)
        00000000`03deec98 00000000`76fe2847 0x76fe2ad2
        00000000`03deeca0 00000023`770e0e48 0x76fe2847
        00000000`03deeca8 00000000`00000023 0x23`770e0e48
        00000000`03deecb0 00000000`00000000 0x23

        THREAD <span class="highlight">fffffa8007fb5080</span>  Cid 1558.168c  Teb: 000000007ea0a000 Win32Thread: 0000000000000000 <span class="highlight">WAIT: (UserRequest)</span> UserMode Non-Alertable
            <span class="highlight">fffffa800bff8800</span>  ProcessObject
        Not impersonating
        DeviceMap                 fffff8a001c1cf70
        Owning Process            fffffa800d3666c0       Image:         VISIO.EXE
        Attached Process          N/A            Image:         N/A
        Wait Start TickCount      3710528        Ticks: 13765657 (2:11:39:05.625)
        Context Switch Count      3              IdealProcessor: 2
        UserTime                  00:00:00.000
        KernelTime                00:00:00.000
        Win32 Start Address 0x000000007450d97d
        Stack Init fffff88006844dd0 Current fffff88006844900
        Base fffff88006845000 Limit fffff8800683f000 Call 0
        Priority 10 BasePriority 8 UnusualBoost 0 ForegroundBoost 0 IoPriority 2 PagePriority 5
        Kernel stack not resident.
        Child-SP          RetAddr           Call Site
        fffff880`06844940 fffff801`98f2b99c nt!KiSwapContext+0x76
        (Inline Function) --------`-------- nt!KiSwapThread+0xf4 (Inline Function @ fffff801`98f2b99c)
        fffff880`06844a80 fffff801`98f27c1f nt!KiCommitThreadWait+0x23c
        fffff880`06844b40 fffff801`992c7df6 nt!KeWaitForSingleObject+0x1cf
        fffff880`06844bd0 fffff801`98f00d53 nt!NtWaitForSingleObject+0xb6
        fffff880`06844c40 00000000`76fe2ad2 nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`06844c40)
        00000000`004ceb28 00000000`76fe2941 0x76fe2ad2
        00000000`004ceb30 00000000`770e2fac 0x76fe2941
        00000000`004ceb38 00000000`00000023 0x770e2fac
        00000000`004ceb40 00000000`00000202 0x23
        00000000`004ceb48 00000000`01fdfd68 0x202
        00000000`004ceb50 00000000`0000002b 0x1fdfd68
        00000000`004ceb58 00000000`00000000 0x2b

</pre>

<p>&#160;</p>

<p>In output we can find many helpful diagnostic information (what’s the state of process, threads, what are they waiting on etc.). We can also switch contexts to particular process/thread (by using OS wide ids of process/thread from the informational outputs of !process command) and debug as we are used to in user mode debugging:</p>

<pre class="code">0: kd&gt; .process <span class="highlight">fffffa800d3666c0</span>
Implicit process is now fffffa80`0d3666c0        

0: kd&gt; .thread <span class="highlight">fffffa8007fb5080</span>Implicit thread is now fffffa80`07fb5080

0: kd&gt; k
  *** Stack trace for last set context - .thread/.cxr resets it
Child-SP          RetAddr           Call Site
fffff880`06844940 fffff801`98f2b99c nt!KiSwapContext+0x76
(Inline Function) --------`-------- nt!KiSwapThread+0xf4
fffff880`06844a80 fffff801`98f27c1f nt!KiCommitThreadWait+0x23c
fffff880`06844b40 fffff801`992c7df6 nt!KeWaitForSingleObject+0x1cf
fffff880`06844bd0 fffff801`98f00d53 nt!NtWaitForSingleObject+0xb6
fffff880`06844c40 00000000`76fe2ad2 nt!KiSystemServiceCopyEnd+0x13
00000000`004ceb28 00000000`76fe2941 0x76fe2ad2
00000000`004ceb30 00000000`770e2fac 0x76fe2941
00000000`004ceb38 00000000`00000023 0x770e2fac
00000000`004ceb40 00000000`00000202 0x23
00000000`004ceb48 00000000`01fdfd68 0x202
00000000`004ceb50 00000000`0000002b 0x1fdfd68
00000000`004ceb58 00000000`00000000 0x2b
</pre>

<p>&#160;</p>

<p>But in addition to user mode debugging we have also all kernel mode debugging commands and access to the whole address space (including other processes).</p>

<pre class="code">0: kd&gt; !object <span class="highlight">fffffa800bff8800</span>
Object: fffffa800bff8800  Type: (fffffa800675f750) Process
    ObjectHeader: fffffa800bff87d0 (new version)
    HandleCount: 6  PointerCount: 1621333

0: kd&gt; !process fffffa800bff8800 0
PROCESS fffffa800bff8800
    SessionId: 1  Cid: 15f8    Peb: 7f722b67000  ParentCid: 1558
    DirBase: 143f0d000  ObjectTable: fffff8a0126addc0  HandleCount: 174.
    Image: <span class="highlight">splwow64.exe</span></pre>

<p> So in this particular case we found that Viso process was waiting on MS spooler service – so shortly hanging Visio was tracked down to network glitches with our network printer.</p>

<p>However debugging in kernel mode debugger can be challenging for many developers and even extensions – many commands are different in kernel mode and some commands even mean different things in user mode and kernel mode – like e.g. the “<strong>~</strong>” command that can be used to switch threads in user mode, but is used for switching of processors in kernel mode – this caused <strong>!analyze</strong> extension to case weird behavior for a long time for user mode breaks in kernel mode context.</p>

<h3>What debugging type is used in current session</h3>

<p>With this many ways of debugging it can be sometimes confusing to find out what type of session are we actually using (especially during the remoting scenarios). There is one helpful command to find out the type of debugging for us:</p>

<pre class="code">0:000&gt; ||
  <br /> .&#160; 0 Live user mode: &lt;Local&gt;</pre>

<pre class="code">0: kd&gt; ||
  <br /> .&#160; 0 64-bit Full kernel dump: C:\WINDOWS\livekd.dmp

</pre>

<p>This way we can for example see that LiveKd actually ‘just’ creates full dump of a memory and then starts debugger to debug that dump.</p>

<p>&#160;</p>

<h2>Preparing session for debugging</h2>

<p>Now I’m finally getting to the actual preparation of the debugging session – this usually means resolving of symbols/binaries/sources and finding owner of code that should perform further investigation. During the kernel mode debugging the first step should be making sure that the session is properly synchronized with the target – but I’m not going to discus this step further in this post.</p>

<h3>Ensuring symbols resolution</h3>

<p>Without the debugging symbols (private preferably), the debugging engine must sometimes ‘guess’ and as a result we cannot fully trust it. It can often happen that location of the crash (AV, unhandled exception …) is pointed to a wrong function (sometimes even module) until all the symbols for modules on the particular stack are resolved. Debugger will usually tell us that missing symbols might cause it to ‘guess’ wrong:</p>

<pre class="code">0:001&gt; k
ChildEBP RetAddr
<span class="highlight">WARNING: Stack unwind information not available. Following frames may be wrong.</span>
004efe54 764b8543 ntdll!DbgBreakPoint
004efe60 770fac69 KERNEL32!BaseThreadInitThunk+0xe
004efea4 770fac3c ntdll!RtlInitializeExceptionChain+0x85
004efebc 00000000 ntdll!RtlInitializeExceptionChain+0x58
</pre>

<p>Usually we’ll have on the stack OS binaries, our binaries and sometimes also third party binaries. Debuggers have a built-in <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff565400(v=vs.85).aspx">command for pointing symbol path to the Microsoft symbol server</a> (to the external one for the shipping builds, and to the internal for the internal only builds):</p>

<pre class="code">3: kd&gt; .symfix
3: kd&gt; .sympath
Symbol search path is: srv*
Expanded Symbol search path is: SRV*http://msdl.microsoft.com/download/symbols
</pre>

<p> Ideally you’ll not need a symbols for the third part components (hopefully they are either reliable, or they are not in the middle of the stack when the crash happens in your component) – as it’s usually hard to get symbols from third parties.</p>

<p>For your symbols (and same would apply if you would happen to be able to get symbols for third party components) you will ideally have them on a <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/ms680693(v=vs.85).aspx">symbol server</a>, in that case you need to prepend path to your symbols server by ‘srv*’ – this indicates that the path will be searched using the ‘index’ (which is a guid that is baked into the binary and symbol file at the time of build – this guid also ensures that only the symbol file coming from the same build as binary will match that binary – unless forcefully chosen to load mismatched symbol file). If the ‘srv*’ is not prepended, the supplied path will be searched as a flat store – each directory will be examined (and potentiall ‘dll’, ‘sym’, ‘exe’ and other similar subdirectories in single level nesting will be also examined) to find the matching symbol file. This can be time consuming task – especially if accidentally applied to a big symbols server. This – incorrectly set up symbol search path - is actually one of the most common reasons for debugger hanging during some task (including Visual Studio debugger).</p>

<p> In the debugger you can set your symbol search path by .sympath command and following is <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff558829(v=vs.85).aspx">the syntax of the symbol path</a>:</p>

<pre class="code">.sympath &lt;local flat symbol path&gt;;cache*&lt;local cache for remote symbol locations&gt;;&lt;remote flat symbol path&gt;;
		srv*&lt;remote symbols server&gt;
</pre>

<p>For example:</p>

<pre class="code">.sympath C:\MyBuildLocation\Symbols;cache*C:\MySymbolsCache;\\CorporateSymbolsServer\Symbols;
		srv*http://msdl.microsoft.com/download/symbols
</pre>

<p> However instead of setting this path every time you start the debugger it’s much more recommendable to set your <strong><em>_NT_SYMBOL_PATH</em></strong> environment variable to that path. As a bonus many other applications can use this – like Process Explorer, Process Monitor etc.</p>

<p>Once your symbols are correctly set, you can try to reload them with <strong>.reload</strong> command (or you may need to force reloading with /f if stripped symbols were already loaded) – you can also point it to specific binaries (and use wildcards):</p>

<pre class="code">0:001&gt; .reload
Reloading current modules
..........................
0:001&gt; k
ChildEBP RetAddr
004efe24 7713dcbc ntdll!DbgBreakPoint
004efe54 764b8543 ntdll!DbgUiRemoteBreakin+0x39
004efe60 770fac69 KERNEL32!BaseThreadInitThunk+0xe
004efea4 770fac3c ntdll!__RtlUserThreadStart+0x72
004efebc 00000000 ntdll!_RtlUserThreadStart+0x1b
</pre>

<h4>Troubleshooting symbols resolution</h4>

<p>Let me simulate symbols resolution problems by setting wrong symbol path:</p>

<pre class="code">0:001&gt; .sympath aaa
Symbol search path is: aaa
Expanded Symbol search path is: aaa
<span class="highlight">WARNING: Inaccessible path: 'aaa'</span>
0:001&gt; .reload /f
Reloading current modules
<span class="highlight">.*** ERROR: Module load completed but symbols could not be loaded for</span> notepad.exe
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\SHCORE.DLL -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\dwmapi.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\uxtheme.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\WINSPOOL.DRV -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\bcryptPrimitives.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\CRYPTBASE.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\SspiCli.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\SHLWAPI.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\combase.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\GDI32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\msvcrt.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\KERNELBASE.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\SHELL32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\ole32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\OLEAUT32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\KERNEL32.DLL -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\COMDLG32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\sechost.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\IMM32.DLL -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\RPCRT4.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\MSCTF.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\ADVAPI32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\SysWOW64\USER32.dll -
.*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ntdll.dll -
</pre>

<p>Already in the basic output we some hints that there are problems with symbols loading and also where might be the issue. But let’s dig deeper – firs we want to verify that there is really problem with loading symbols for a particular module of interest:</p>

<pre class="code">0:001&gt; lmvm ntdll
start    end        module name
770a0000 771f7000   ntdll      (<span class="highlight">export symbols</span>)       C:\WINDOWS\SYSTEM32\ntdll.dll
    Loaded symbol image file: C:\WINDOWS\SYSTEM32\ntdll.dll
    Image path: ntdll.dll
    Image name: ntdll.dll
    Timestamp:        Wed Sep 19 22:32:50 2012 (505AAA82)
    CheckSum:         0015B576
    ImageSize:        00157000
    File version:     6.2.9200.16420
    Product version:  6.2.9200.16420
    File flags:       0 (Mask 3F)
    File OS:          40004 NT Win32
    File type:        2.0 Dll
    File date:        00000000.00000000
    Translations:     0409.04b0
    CompanyName:      Microsoft Corporation
    ProductName:      Microsoft« Windows« Operating System
    InternalName:     ntdll.dll
    OriginalFilename: ntdll.dll
    ProductVersion:   6.2.9200.16420
    FileVersion:      6.2.9200.16420 (win8_gdr.120919-1813)
    FileDescription:  NT Layer DLL
    LegalCopyright:   ⌐ Microsoft Corporation. All rights reserved.
</pre>

<p> And we can see that the (private or public) symbols are not being loaded. Let’s track down why:</p>

<pre class="code">0:001&gt; !sym noisy
noisy mode - symbol prompts off
0:001&gt; .reload /f ntdll.dll
DBGHELP: aaa\wntdll.pdb - file not found
DBGHELP: aaa\dll\wntdll.pdb - file not found
DBGHELP: aaa\symbols\dll\wntdll.pdb - file not found
DBGHELP: C:\WINDOWS\SYSTEM32\wntdll.pdb - file not found
DBGHELP: wntdll.pdb - file not found
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for ntdll.dll -
DBGHELP: ntdll - export symbols
</pre>

<p> Now we see all the paths that are being searched and we can go find why the correct symbol file is not present on any of those paths. Once we fix the issue or/and fix our symbol search path, we can successfully reload symbols.</p>

<p>Sometimes engine doesn’t know which symbol file to load as it doesn’t even have the image loaded (this can for example happen when we debug some minidumps). For this reason we might want to have our images loading set up.</p>

<h3>Executable images loading</h3>

<p> When windows runs your application it loads the executable binaries (.exe, .dll) into memory – at which point we call them <strong>executable images</strong>. As they are in memory, they can be part of memory dump of a application. However some smaller dumps may not have them, also it might happen that the image was unloaded – so there can be situations where the debugger might need to load those from separate location (this will become extremely helpful during managed code debugging from a dump – I’ll write about this in separate post).</p>

<p>Executable image path has same rules as symbols search path – and in fact in many situations we might want to set them to same locations as (despite many people don’t realize this) <strong><em>binaries can be also indexed on symbols server</em></strong>. </p>

<pre class="code">.exepath C:\MyBuildLocation\Binaries;cache*C:\MySymbolsCache;\\CorporateSymbolsServer\Symbols;
		srv*http://msdl.microsoft.com/download/symbols
</pre>

<p> And same as with symbols, it’s more advantageous to have this path set already in environment – we can use <strong><em>_NT_EXECUTABLE_IMAGE_PATH</em></strong> variable for this purpose.</p>

<h3>Source code loading</h3>

<p>Why wouldn’t we make our debugging experience less frustrating by <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff556911(v=vs.85).aspx">debugging with source codes</a>? Debugger engine can extract the source paths from the symbol files. If we would open a symbol file (e.g. ‘foo.pdb’) in simple text editor we would see that it contains locations of source code – those locations are taken at the build time. So if you debug on the same machine as where you build your code, or if your debug machine has same structure of code files in same location as your build machine, the debugger will be able to ‘magically’ bring up the source code file – but it’s not guaranteed that it’s a correct version of that source file (let’s say someone changed it from the time the debugged binary was build). Therefore the most recommendable way of having the best source code debugging experience is to use a source control system and source index your symbols.</p>

<p>Source indexing symbols means that the symbol file will have inside itself information about how to retrieve the file from source control server and which version to retrieve (basically it has section with translation of local paths to the full commands to the source control). You than need to make sure that the debugger has access to the toll for retrieving sources from source control (e.g. p4c.exe for perforce. You can just put it’s location to the path) and that the source path indicates that you want to use <strong>‘source server’</strong>.</p>

<p>To indicate that you want to use source server, set the source path to ‘<strong>srv*</strong>’ string (beware that syntax and meaning is completely different from symbols or executable image path! for source path you just use ‘srv*’ with no other specification and it means that the debugger will attempt to retrieve the source control commands from the symbol files). To use local path point the source path to the flat path with source codes (no recursive search will be performed!). Or you can combine both approaches. Use .srcpath to set your source path:</p>

<pre class="code">.srcpath srv*;&lt;any additional flat source locations&gt;
</pre>

<p>Or if you can use <em>.srcfix</em> command for this (it sets the source path to the ‘srv*’ and any additional path that you pass to the .srcfix+ &lt;path&gt;).</p>

<p>However the most recommendable way is again by using environment variable - <strong><em>_NT_SOUCE_PATH</em></strong></p>

<h4>Source code loading during remote debugging</h4>

<p>As was mentioned earlier – during the basic remote debugging, all commands are being executed on the remote system. This may be problematic in case where you debugging remote issue, however have source files on your local machine, or have the source control utility on your machine. In this situation .srcpath or .srcfix commands will have no effect (which can be confusing). You will need to set<em> <strong>local</strong> source path</em> and/or use <em><strong>local</strong> source server</em>:</p>

<pre class="code">.lsrcfix
.lsrcpath+ &lt;local source codes path&gt;</pre>

<p>This will resolve source codes on your local machine (as opposed to the remote machine actually running the debugging engine), and source codes will finally ‘magically’ load (unless you have other issues that I’ll try to cover in a second). Also keep in mind that those two commands cannot be scripted.</p>

<h4>Troubleshooting source code loading</h4>

<p>Sometimes you’re changing the frames in stack and expect the appropriate source code to appear, but nothing happens. For those reason there is a way to troubleshoot such an issue (I’ve censored some irrelevant information):</p>

<pre class="code">0:000&gt; <span class="highlight">.lines -e</span>
Line number information will be loaded
0:000&gt; kn
 # Child-SP          RetAddr           Call Site
00 00000070`af71f6f0 000007ff`ecc72cc9 ntdll!LdrpDoDebuggerBreak+0x30 [d:\XXXXXX\ntdll\ldrinit.c @ 2737]
01 00000070`af71f730 000007ff`ecc0216a ntdll!LdrpInitializeProcess+0x1927 [d:\XXXXXX\ntdll\ldrinit.c @ 5131]
02 00000070`af71fa30 000007ff`ecbf32ae ntdll!_LdrpInitialize+0xee9a [d:\XXXXXX\ldrinit.c @ 1334]
03 00000070`af71faa0 00000000`00000000 ntdll!LdrInitializeThunk+0xe [d:\XXXXXX\ntdll\ldrstart.c @ 90]

0:000&gt; <span class="highlight">!srcnoisy 3</span>Noisy source output: on
Noisy source server output: on
Filter out everything but source server output: on
0:000&gt; .frame 3
03 00000070`af71faa0 00000000`00000000 ntdll!LdrInitializeThunk+0xe [d:\XXXXXX\ntdll\ldrstart.c @ 90]
DBGENG:  Check plain file:
DBGENG:    'd:\XXXXXX\ntdll\ldrstart.c' - <span class="highlight">not found</span>
0:000&gt; .srcfix
DBGHELP: Symbol Search Path: srv*c:\mysymcache*http://XXXXXX
Source search path is: SRV*
DBGENG:  Scan srcsrv SRV* for:
DBGENG:    '<token>!d:\XXXXXX\ntdll\ldrstart.c'
SRCSRV:  XX.exe -p XXXXXXXXXXXXX.ntdev.microsoft.com:2020 print -o &quot;D:\Debuggers_64bit\src\
			XXXXXXXXXX\ntdll\ldrstart.c\1\ldrstart.c&quot; -q //depot/XXXXXXXXXX/ntdll/ldrstart.c#1
DBGENG:      found file 'd:\XXXXXXXXXX\ntdll\ldrstart.c'
DBGENG:      server path 'SRV*'
DBGENG:      local 'D:\Debuggers_64bit\src\XXXXXXXXXXXX\ntdll\ldrstart.c\1\ldrstart.c'
</token></pre>

<p> So we see that we can use <strong>.lines</strong> command to enable (or disable with -d) displaying of that build time paths baked into the symbol file. Alse we can see that !srcnoisy can switch on more verbose logging on what’s happening during sources loading. Finally we see how is the source file loaded from source control server – provided that symbol file was properly source indexed. Here is what would happen if symbol file wouldn’t be source indexed:</p>

<pre class="code">0:000&gt; .frame 10
SRCSRV:  d:\YYYYYYYYYYY\identityauthority.cpp <span class="highlight">not indexed</span>
</pre>

<p>&#160;</p>

<h2>Finishing the debugging</h2>

<p>Once we are done with debugging session we have several option of ending the session. If we are not debugging live user mode session, we might not care about the way of exiting – simple close of the debugger app or <em>q</em> command might be enough. However when debugging the live user mode process we might want to preserve the process. To end debugging without killing the target application use<em> .detach</em> command or <em>qd</em> (quit and detach) command, or best way might be to start the debugger with <em><strong>–pd</strong></em> option, which will cause that target application will remain running, no matter how you quit your debugging session.</p>]]></content:encoded>
    </item>
    <item>
      <title>Introduction</title>
      <link>https://docs.microsoft.com/archive/blogs/jankrivanek/introduction</link>
      <pubDate>Sat, 20 Oct 2012 22:16:00 GMT</pubDate>
      <dc:creator><![CDATA[Jan Krivanek]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/jankrivanek/2012/10/20/introduction/</guid>
      <description><![CDATA[Hello friends!
I am a software developer in Microsoft Corporation working on an internal testing...]]></description>
      <content:encoded><![CDATA[<h4 style="text-align: justify;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">Hello friends!</span></h4>
<p style="text-align: justify;"><span style="font-family: tahoma,arial,helvetica,sans-serif;">I am a software developer in Microsoft Corporation working on an internal testing platform/framework for testing Windows (externally shipping as the <a title="http://www.microsoft.com/en-us/download/details.aspx?id=857" href="http://blogs.msdn.com/controlpanel/blogs/posteditor.aspx/Windows Logo Toolkit" target="_blank">Windows Logo Toolkit</a>). Being daily in the first line during the thorough &lsquo;attacks&rsquo; of large amounts of various tests;&nbsp;designing high reliability software, hardening legacy code and debugging user mode and kernel mode crashes (from the basic AVs to the most obscure heap corruptions) is bread and butter of our daily work.</span><br /><span style="font-family: tahoma,arial,helvetica,sans-serif;">I decided to share some of the gained experience and knowledge trough the posts on this blog (as the time will allow). I hope this will not only be a future reference for myself, but also a helpful source of information for others. Let&rsquo;s see&hellip;</span></p>
<p style="text-align: justify;"><span style="font-family: tahoma,arial,helvetica,sans-serif;"></span><span style="font-family: Tahoma;" face="Tahoma"></span><br /><span style="font-family: tahoma,arial,helvetica,sans-serif;">But now &ndash; <strong>Welcome on my blog</strong>!!</span></p>]]></content:encoded>
    </item>
  </channel>
</rss>