Dump all values of string type from managed heap to a file - WinDbg

Viewed 571

I have a process dump from a process (.Net Framework) that shows an OutOfMemoryException. I have looked into the process dump using "!dumpheap -stat" and figured out that the top memory consumption is by "System.String" type.

                MT    Count    TotalSize   Class Name  
  00007ffb081f97f8 10608868   1287368622   System.String*

To debug further, I want to see the value of all (10608868) these strings. I tried with the following command to see the values:

.foreach (address  {!DumpHeap -type System.String -short }) {!do ${address} }

and this command outputs:

enter image description here

But I just want to print all the String values alone into a file and the file content should look like:

{"result":[{"user":{"abc..."
{"result":[{"user":{"zyz..."
string3
string4

What is the full command to be used in WinDbg to get this done?

3 Answers

Download NetExt.

Let's say you put the NetExt extension on c:\exts\x64 (change the folder in the sample if you put it somewhere else)

Open your dump file and run this:

0:000> .load c:\exts\x64\netext
netext version 2.1.62.5000 Jan 29 2021
License and usage can be seen here: !whelp license
Check Latest version: !wupdate
For help, type !whelp (or in WinDBG run: '.browse !whelp')
Questions and Feedback: https://github.com/rodneyviana/netext/issues 
Copyright (c) 2014-2015 Rodney Viana (http://blogs.msdn.com/b/rodneyviana) 
Type: !windex -tree or ~*e!wstack to get started

0:000> !windex
Starting indexing at 10:51:12 AM
Indexing finished at 10:51:14 AM
8,581,399 Bytes in 132,086 Objects
Index took 00:00:01

0:000> .logopen c:\temp\stringsoutput.txt
Opened log file 'c:\temp\stringsoutput.txt'

0:000> !wfrom -nofield -nospace -type System.String select $string()

D:\home\site\wwwroot\
D:\home\site\wwwroot\Contoso.Sample.Web.exe.config
PARTIAL_TRUST_VISIBLE_ASSEMBLIES
Contoso.Sample.Web.exe
RELPATH
CACHE_BASE
APPBASE
DEV_PATH
DISALLOW_APP_REDIRECTS
DISALLOW_APP_BASE_PROBING
(...)

0:000> .logclose
Closing open log file c:\temp\stringsoutput.txt

Some variations:

  1. Also add the string address:

    !wfrom -nofield -nospace -type System.String select $addr()," ",$string()

  2. Only dump string larger than 5,000 bytes with addresses:

    !wfrom -nofield -nospace -type System.String where (m_stringLength > 0n5000) select $addr()," ",$string()

  3. Only print the size of the string and address if the string is larger than 5,000 bytes:

    !wfrom -nofield -nospace -type System.String where (m_stringLength > 0n5000) select $addr()," ",m_stringLength

well you may write a javascript like this

:\>type foo.js
function log (instr) {
        host.diagnostics.debugLog(instr +"\n");
}
function dust ()
{
        var cmdstr = ".foreach (place { !DumpHeap -type String -short  } ) { !do -nofields /d place }";
        var objs = host.namespace.Debugger.Utility.Control.ExecuteCommand(cmdstr);
        var res = [];
        var k = 0;
        for (i of objs)
        {
                if(i.includes("String:")==true)
                {
                        log(i);
                        k++
                }
        }
        log("Number of System.String = " + k.toString());
}

and execute like this

:\>set cd
cdbx86="c:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe"

:\>file chcon.dmp
chcon.dmp: Mini DuMP crash report, 17 streams, Thu May 13 10:11:26 2021, 0x441826 type

:\>%cdbx86% -c "!loadby sos clr;!sosflush;.scriptload .\foo.js; dx @$scriptContents.dust();q" -z chcon.dmp | awk "/Reading/,/quit/"
0:006> cdb: Reading initial command '!loadby sos clr;!sosflush;.scriptload .\foo.js; dx @$scriptContents.dust();q'      
JavaScript script successfully loaded from 'source\repos\chcon\chcon\bin\x86\Release\foo.js'
String:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
String:      chcon.exe.config
String:      RELPATH
String:      ;
String:      DYNAMIC_BASE
String:      PRIVATE_BINPATH
String:      SHADOW_COPY_DIRS
String:      CACHE_BASE
String:      APPBASE
String:      DEV_PATH
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
String:      .NETFramework,Version=v4.6.1
String:      This is a global Test String
String:      TestOne
String:      Testtwo
String:      ?
String:      codepages.nlp
String:      capacity
String:      length
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
String:      offset
Number of System.String = 173
@$scriptContents.dust()
quit:

You were quite close. Just get the offset of the string field m_firstChar (which happens to be c for my bitness and .NET version; it'll not change often) and use du instead of !do:

.foreach (address  {!DumpHeap -type System.String -short }) {du ${address}+c }

Use .logopen and .logclose to write into a file. That might need a bit of post-processing for the first and last lines.

Related