Displaying Multiple .NET Objects with WinDBG’s Command Language

While we are getting some nice tools from Microsoft for analyzing our memory, there’s still a lot of gaps where you have to resort to WinDBG and SOS. A perfect example that I’ve run into is looking at variables/types that are in different app domains because the Visual Studio debugging environment has essentially zero support for app domains. That’s normally not a problem for most people, but there’s a lot of things that you just need WinDBG and SOS to figure out. In this article I wanted to show a little trick I use with the WinDBG command language.

If you’re not familiar with WinDBG or SOS, you can watch my Introduction to WinDBG and SOS Commands and Minidump videos on WintellectNOW. Use the code JOHNR-2013 to get two free weeks of viewing pleasure.

The WinDBG command language allows you to write looping and logic statements using commands like .if, .while, .foreach, .printf, etc. You can use the command language with conditional breakpoints or write complicated analysis scripts in it. Like anything else in WinDBG, it’s barely documented and has no debugger, but a little trial and error take you a long way.

A very common situation when looking at a minidump is that you want to look at all of the objects of a particular type. In the usual case you have to copy and paste each address you find with !dumpheap –type <type>. That’s kind of tedious so here’s a quick way to dump out all the objects with the SOSEX !mdt command.

  1. .foreach (addr {!dumpheap -type Namespace.Class -short}) { !mdt addr; .echo }

It’s probably easiest to understand this from the inside out. The !dumpheap –type Namespace.Class –short, returns just the object addresses for Namespace.Class. Those address are assigned to the addr variable. The .foreach enumerates over each line of text in the addr variable and calls !mdt on each individual address. The final .echo places a blank line in the output. Below is an example where I’ll display the FontFamily types in a process.

  1. 0:013> .foreach (addr {!dumpheap -type FontFamily -short}) { !mdt addr; .echo }
  2. 000000b45faff460 (System.Drawing.FontFamily)
  3.     __identity:NULL (System.Object)
  4.     nativeFamily:000000b6aaad8f40 (System.IntPtr)
  5.     createDefaultOnFail:false (System.Boolean)
  6. 000000b45faff630 (System.Drawing.FontFamily)
  7.     __identity:NULL (System.Object)
  8.     nativeFamily:000000b6aaad8f40 (System.IntPtr)
  9.     createDefaultOnFail:false (System.Boolean)

If you run the above command, substituting your own type of course, you’ll see that the WinDBG command language is not the quickest language around. If you dump out a type with a lot of instances, you will see a lot of the BUSY in the command line. The good news is that running the command is far faster than you copy and pasting each address in yourself.

As this is a useful command, here’s a small script you can save to DumpType.txt. Note that $$ are comment characters.

  1. $$ If there’s no first argument, there’s nothing to do.
  2. .if(0 != ${/d:$arg1})
  3. {
  4.     $$ Get each address of the type into the addr variable.
  5.     .foreach (addr {!dumpheap -type ${$arg1} -short})
  6.     {
  7.         $$ Dump out this address using SOSEX.
  8.         !mdt addr;
  9.         $$ Put a line between each item dumped.
  10.         .echo
  11.     }   
  12. }
  13. .else
  14. {
  15.     .printf “nUsage: $$>>a<DumpType.txt typen”
  16. }

To run the script use the WinDBG $$>a< command to read it in and pass the parameter of the type you’d like to dump.

  1. 0:013> $$>a<c:junkDumpType.txt
  2. Usage: $$>>a<DumpType.txt type
  3. 0:013> $$>a<c:junkDumpType.txt FontFamily
  4. 000000b45faff460 (System.Drawing.FontFamily)
  5.     __identity:NULL (System.Object)
  6.     nativeFamily:000000b6aaad8f40 (System.IntPtr)
  7.     createDefaultOnFail:false (System.Boolean)
  8. 000000b45faff630 (System.Drawing.FontFamily)
  9.     __identity:NULL (System.Object)
  10.     nativeFamily:000000b6aaad8f40 (System.IntPtr)
  11.     createDefaultOnFail:false (System.Boolean)
John Robbins

View Comments

  • Great thanks for the script! I have two more snippets in windbg meta language:
    1. Place conditional breakpoint checking if a given caller (Module!ClassA:MemberFunction) is in the call stack:
    bp Module!MyFunctionWithConditionalBreakpoint "r $t0 = 0;.foreach (v { k }) { .if ($spat("v", "*Module!ClassA:MemberFunction*")) { r $t0 = 1;.break } }; .if($t0 = 0) { gc }"
    2. Break only if a given managed exception (eg. System.NullReferenceException) is thrown:
    !sxe -c "!soe System.NullReferenceException 1;.if (@$t1 != 1) { g; }" clr

  • Hi John,
    Long time no see you! :-)
    I just blogged another script to cheat on Minesweeper for Windows 8.1 and since you liked the 2007 version I think you'll like this one as well ;-)
    Thanks,
    Roberto

Recent Posts

How to Navigate Azure Governance

 Cloud management is difficult to do manually, especially if you work with multiple cloud…

5 days ago

Why Azure’s Scalability is Your Key to Business Growth & Efficiency

Azure’s scalable infrastructure is often cited as one of the primary reasons why it's the…

3 weeks ago

Unlocking the Power of AI in your Software Development Life Cycle (SDLC)

https://www.youtube.com/watch?v=wDzCN0d8SeA Watch our "Unlocking the Power of AI in your Software Development Life Cycle (SDLC)"…

1 month ago

The Role of FinOps in Accelerating Business Innovation

FinOps is a strategic approach to managing cloud costs. It combines financial management best practices…

1 month ago

Azure Kubernetes Security Best Practices

Using Kubernetes with Azure combines the power of Kubernetes container orchestration and the cloud capabilities…

2 months ago

Mastering Compliance: The Definitive Guide to Managed Compliance Services

In the intricate landscape of modern business, compliance is both a cornerstone of operational integrity…

2 months ago