Search This Blog

Showing posts with label WinDbg. Show all posts
Showing posts with label WinDbg. Show all posts

Monday, 2 October 2017

Some small contributions to your debugging experience


A couple of issues have kept me bugging for years whenever I debugged managed code (sos.dll) in Windbg, so finally, I decided to do something about them exploiting the fact that .NET Core is fully hosted on GitHub. Unfortunately the source of the desktop .NET Framework is still not exposed there, so submitting fixes and reporting issues is much more difficult, if not impossible for that version.

One problem was really annoying if you got used to clicking on DML output as the !DumpArray sos command emitted invalid DML for value-typed list elements. The output was really confusing as no error message was shown and the element type was displayed incorrectly.

0:007> !DumpArray /d 02b16574
Name:        WindowsFormsApplication2_SOS_bug.Test[]
MethodTable: 011c82ec
EEClass:     011c8290
Size:        412(0x19c) bytes
Array:       Rank 1, Number of elements 100, Type VALUETYPE
Element Methodtable: 011c824c
[0] 02b1657c => **wrong DML generated**

Fortunately, the fix was very simple.

Another problem that I fixed was with the !HeapStat command as it emitted difficult to read output. The percentage column was not padded properly.

Heap             Gen0         Gen1         Gen2          LOH
Heap0         5067096       941836    231688916     27653832

Free space:                                                 Percentage
Heap0          108724         6660     41310400      2438136SOH: 17% LOH:  8%

Unrooted objects:                                           Percentage
Heap0         4699412       755996       312864            0SOH:  2% LOH:  0%

I also opened an issue for the famous “first sos command throws an exception error”. 
As it turned out this had already been worked around for .NET Core 2.0.

Too bad these will not be fixed for the full .Net Framework automatically…

Saturday, 12 November 2016

Revealing the native memory pressure of WPF Bitmaps


A couple of months ago, I bumped into an interesting memory leak with regards to the ElementHost WinForms control that is the primary means to host WPF content in any WinForms environment. I was able to trace back the root cause to a known issue, that I just call "the memory pressure bug". Alois did a great job in summarizing the story.
 
The basic idea to monitor native memory pressure of managed objects has been there for years in .NET, but for WPF bitmaps they didn't do a very good job at it. As a result, the GC had no idea that a collection should be induced if - for instance - many wpf bitmaps held large amounts of native resources. This introduced all kinds of problems you can think of, like virtual address space fragmentation and OutOfMemory exceptions coming from both the managed and the native world. While investigating the issue I came up with a handy WinDbg script, that can tell you how much the native overhead is for your wpf bitmaps in terms of memory usage.

[bmp.txt]
$$ Run with $$>a<"bmp.txt"
$$ Created by Tamas Vass (2016)
$$ This script displays the GC memory pressure of System.Windows.Media.Imaging.BitmapSourceSafeMILHandle objects
$$  that basically represent the amount of unmanaged memory needed by them (x86 only)

$$ MT of BitmapSourceSafeHandle
r @$t3=0

$$ Running !dumpheap -type System.Windows.Media.Imaging.BitmapSourceSafeMILHandle -short seems to fail sporadically
$$  but !dumpheap -mt <mtaddr> -short is stable, so let's fetch the Method Table address and use that
.foreach /pS 7 /ps 1 (i {!name2ee PresentationCore.dll!System.Windows.Media.Imaging.BitmapSourceSafeMILHandle})
{
    r @$t3=${i}
    .printf "BitmapSourceSafeHandle MT: %x", @$t3
    .break
}

$$ Count of valid
r @$t0=0

$$ Count of invalid
r @$t1=0

$$ Size in bytes
r @$t2=0

$$ Print header
.printf "\n\nAddress -> Size in bytes"

.foreach (var {!DumpHeap /d -mt @$t3 -short})
{
    .if ($vvalid(poi(${var}+0x10),4)==1)
    {
        r @$t0=@$t0+1
        r @$t2=@$t2+dwo((poi(${var}+0x10))+4)
        .printf "\n%x -> %u", ${var}, qwo((poi(${var}+0x10))+4)
    }
    .else
    {
        r @$t1=@$t1+1
        .printf "\n%x -> NULL", ${var}
    }
}

.printf "\n\nTotal: %u", @$t0+@$t1
.printf "\n |- Valid: %u", @$t0
.printf "\n |- Invalid: %u", @$t1

.foreach /pS 1 /ps 1 (i {?? @$t2 / 1048576.0f})
{   
    .printf "\n\nTotal size: %u bytes (${i} MB)", @$t2

    .break
}

.printf "\n"

If you're running a .NET version older than 4.6.2, then you can easily reproduce the leak by following these steps:
1. Create a WinForms app and dock an ElementHost in the middle of your Form
2. In code-behind, add any WPF content to the ElementHost, by setting its Child property
3. Start the application
4. Resize your Form a few times (the closer the Form to being full screen, the better)

Resizing causes the ElementHost, to create a wpf bitmap of its rendered background. Don't ask me why, this is what happens. Using the script above and performing a couple of window resizes I got this output:

0:015> $$>a<"bmp.txt"
BitmapSourceSafeHandle MT: 547519ac

Address -> Size in bytes
2dd8e24 -> NULL
2dd8e50 -> NULL
2dd8e9c -> NULL
<removed 610 entries for brevity>
2dfdcf0 -> 6371508
303fa68 -> NULL
303fa80 -> 6371508

Total: 616
 |- Valid: 180
 |- Invalid: 436

Total size: 1190832240 bytes (1135.67 MB)

Nice, isn't it? I had 1135 MB of native garbage and the GC had absolutely no idea about it. Of course eventually, when garbage collection occurs these objects will go away, but as they have a finalizer, they require 2 collections and their finalizers run in-between, but more on that in an other article of mine.

Fortunately, .NET 4.6.2 solves this issue in a nice way - by maintaining and taking into account the native memory pressure when determining the need for a collection. See GC.AddMemoryPressure for further details.