windbgsossosex

search objects with size larger then a threshold


One of class has many object present in .NET heap as discovered through following sos command.

!dumpheap -stat  -type MyClass
Statistics:
              MT    Count    TotalSize    Class Name
00007ff8e6253494     1700     164123  MyNameSpace.MyClass

I need to find the instances of those objects that have ObjSize greater then 5 MB. I know I can list out objsize of all 1700 instances of MyClass using following.

.foreach (res {!DumpHeap -short -MT 00007ff8e6253494 }) {.if ( (!objsize res) > 41943040) {.echo res; !objsize res}}

With the script above, I don't get any results although there are object instances greater than 5MB. I think problem may be that output of objsize is follows

20288 (0x4f40) bytes

Its a string which make it harder to compare against any threshold. How can I get this script to only list objects that has objsize larger then 5MB?


Solution

  • Creating complex scripts in WinDbg is quite error prone. In such situations, I switch to PyKd, which is a WinDbg extension that uses Python.

    In the following, I'll only cover the missing piece in your puzzle, which is the parts that does not work:

    .if ( (!objsize res) > 41943040) {.echo res; !objsize res}
    

    Here's my starting point:

    0:009> !dumpheap -min 2000
             Address               MT     Size
    00000087c6041fe8 000007f81ea5f058    10158     
    00000087d6021018 000007f81ea3f1b8     8736     
    00000087d6023658 000007f81ea3f1b8     8192     
    00000087d6025658 000007f81ea3f1b8    16352     
    00000087d6029638 000007f81ea3f1b8    32672  
    

    You can write a script like this (no error handling!)

    from pykd import *
    import re
    import sys
    objsizeStr = dbgCommand("!objsize "+sys.argv[1])
    number = re.search("= (.*)\(0x", objsizeStr)
    size = int(number.group(1))
    if size > 10000:
        print sys.argv[1], size
    

    and use it within your loop:

    0:009> .foreach (res {!dumpheap -short -min 2000}) { !py c:\tmp\size.py ${res}}
    00000087c6041fe8 10160
    00000087d6021018 37248
    00000087d6023658 27360
    00000087d6025658 54488
    00000087d6029638 53680
    

    Note how the size of !objsize differs from that of !dumpheap. Just for cross-checking:

    0:009> !objsize 00000087d6023658
    sizeof(00000087d6023658) = 27360 (0x6ae0) bytes (System.Object[])
    

    See also this answer on how to improve the script using expr() so that you can pass expressions etc. The way I did it now outputs the size in decimal, but that's not explicit. Maybe you want to output a 0n prefix to make it clear.