NetInverse Developers Blog

November 14, 2009
Category: Debugging — Tags: , , , — admin @ 11:59 pm

Managed Memory Leak will be reported by an OutOfMemoryException exception thrown by the CLR. There are a few reasons will result in it.

1) Too many objects are alive.
2) Object handle leak. Use !sos.objsize to list handles.
3) Heap fragmentation. Use !sos.dumpheap to get excessive GC Heap fragmentation report.

Category: .Net — Tags: , , , — admin @ 11:42 pm

There are two types of heap corruptions: 1) NT Heap Corruption 2) GC Heap Corruption.

GCSTRESS is a checked or debugging build with some registry keys set used to debug the GC Heap Corruption. It forces the garbage collection to occur very oftern to shake out a bug.

April 22, 2009
Category: CLR, Debugging — Tags: , , , , , — admin @ 12:08 am

An object’s CLR internal structure is:

[DWORD: SyncBlock][DWORD: MethodTable Pointer][DWORD: Reference type pointer]…[Value of Value Type field]…

Object Header: [DWORD: SyncBlock]
Object Pointer: [DWORD: MethodTable Pointer][DWORD: Reference type pointer]…[Value of Value Type field]…

Every Object is preceded by an ObjHeader (at a negative offset). The ObjHeader has an index to a SyncBlock.

Sample C# code for exploring CLR object’s internal structure

namespace ObjectInternal
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    class Program
    {
        static void Main(string[] args)
        {
            Product p = new Product();
            p.Price = 99;
            p.Index = 25;
            p.Name = "Super Product";
            p.Cat = new Cateogry();
        }
    }

    class Product
    {
        public int Price { get; set; }
        public byte Index { get; set; }
        public string Name { get; set; }
        public Cateogry Cat { get; set; }
    }

    public class Cateogry
    {
        public string Name;
    }
}

Sample output from SOS.dll debugger extension:

.load sos
extension C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll loaded

!dumpstackobjects
PDB symbol for mscorwks.dll not loaded
OS Thread Id: 0xbb4 (2996)
ESP/REG  Object   Name
0012f0d4 012c2c10 System.Object[]    (System.String[])
0012f20c 012c2c10 System.Object[]    (System.String[])
0012f218 012c2c4c ObjectInternal.Product
0012f21c 012c2c64 ObjectInternal.Cateogry
0012f438 012c2c64 ObjectInternal.Cateogry
0012f43c 012c2c4c ObjectInternal.Product
0012f440 012c2c4c ObjectInternal.Product
0012f444 012c2c10 System.Object[]    (System.String[])
0012f534 012c2c10 System.Object[]    (System.String[])
0012f6e0 012c2c10 System.Object[]    (System.String[])
0012f708 012c2c10 System.Object[]    (System.String[])

!dumpobj 012c2c4c
Name: ObjectInternal.Product
MethodTable: 00933138
EEClass: 00931384
Size: 24(0x18) bytes
 (C:\temp\ObjectInternal\ObjectInternal\bin\Debug\ObjectInternal.exe)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79332c4c  4000001        c         System.Int32  1 instance       99 <Price>k__BackingField
79333520  4000002       10          System.Byte  1 instance       25 <Index>k__BackingField
79330a00  4000003        4        System.String  0 instance 012c2c20 <Name>k__BackingField
009331b0  4000004        8 ...Internal.Cateogry  0 instance 012c2c64 <Cat>k__BackingField

!dumpobj 012c2c20
Name: System.String
MethodTable: 79330a00
EEClass: 790ed64c
Size: 44(0x2c) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: Super Product
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79332c4c  4000096        4         System.Int32  1 instance       14 m_arrayLength
79332c4c  4000097        8         System.Int32  1 instance       13 m_stringLength
793316e0  4000098        c          System.Char  1 instance       53 m_firstChar
79330a00  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  0015d370:012c1198 <<
79331630  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  0015d370:012c1790 <<

Physical memory layout of CLR objects:

CLR Object's Internal Structure

CLR Object's Internal Structure

We use !dumpobj to examine Product object instance, which is located at address: 0×012c2c4c. You can see that: field Name(String “Super Product”)’s address is 0×012c2c20, MethodTable is 0×79330a00. Field Cateogry’s address is 0×012c2c64 and MethodTable is 0×009331b0. Value types are directly stored as 0×63(99) and 0×19(25).

April 18, 2009
Category: Debugging — Tags: , , , , — admin @ 9:40 pm

You can use !DumpStackObjects and !DumpObj to explore an object’s internal structure.

CLR Internal - string object's internal structure

CLR Internal - string object's internal structure

A string object’s CLR internal structure is:

[DWORD: SyncBlock][DWORD: MethodTable Pointer][DWORD: length as array][DWORD: length as string][WCHAR: 1st char]…[WCHAR: NULL]

From above screenshot, you can see that the SyncBlock: 80000000 Method table pointer is: 79330a00, m_arrayLength: 9, m_stringLength: 8, m_firstChar, …

!dumpobj 012c2b2c
Name: System.String
MethodTable: 79330a00
EEClass: 790ed64c
Size: 34(0x22) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: abcdefgh
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79332c4c  4000096        4         System.Int32  1 instance        9 m_arrayLength
79332c4c  4000097        8         System.Int32  1 instance        8 m_stringLength
793316e0  4000098        c          System.Char  1 instance       61 m_firstChar
79330a00  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  0015d318:012c1198 <<
79331630  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  0015d318:012c1774 <<
April 17, 2009
Category: Debugging — Tags: , , , , — admin @ 9:21 pm

SOS Command: !DumpArray

	[-start <startIndex>]
	[-length <length>]
	[-details]
	[-nofields]
	<array object address>

This command allows you to examine elements of an array object. The arguments in detail:

-start <startIndex>: optional, only supported for single dimension array. Specify from which index the command shows the elements.
-length <length>: optional, only supported for single dimension array. Specify how many elements to show.
-details: optional. Ask the command to print out details of the element using !DumpObj and !DumpVC format.
-nofields: optional, only takes effect when -detail is used. Do not print fields of the elements. Useful for array of objects like String.

April 11, 2009
Category: Debugging — Tags: , , , , — admin @ 8:30 pm

SOS Command: !GCHandles [-perdomain]

!GCHandles provides statistics about GCHandles in the process. Sometimes the source of a memory leak is a GCHandle leak. For example, code might keep a 50 Megabyte array alive because a strong GCHandle points to it, and the handle was discarded without freeing it.

The most common handles are “Strong Handles,” which keep the object they point to alive until the handle is explicitly freed. “Pinned Handles” are used to prevent the garbage collector from moving an object during collection. These should be used sparingly, and for short periods of time. If you don’t follow that precept, the gc heap can become very fragmented.

If you run with the -perdomain option, you will get the same output broken down by AppDomain. Here is sample output from a very simple program:

!GCHandles
GC Handle Statistics:
Strong Handles: 15
Pinned Handles: 4
Async Pinned Handles: 0
Ref Count Handles: 0
Weak Long Handles: 0
Weak Short Handles: 1
Other Handles: 0
Statistics:
      MT    Count    TotalSize Class Name
7933061c        1           12 System.Object
793310cc        1           28 System.SharedStatics
79331f4c        2           48 System.Reflection.Assembly
79330d44        1           72 System.ExecutionEngineException
79330cb4        1           72 System.StackOverflowException
79330c24        1           72 System.OutOfMemoryException
793311e0        1          100 System.AppDomain
79330fd4        2          112 System.Threading.Thread
793326c4        4          144 System.Security.PermissionSet
79330dd4        2          144 System.Threading.ThreadAbortException
793041d0        4         8736 System.Object[]
Total 20 objects

See also: !GCHandleLeaks

This command is an aid in tracking down GCHandle leaks. It searches all of memory for any references to the Strong and Pinned GCHandles in the process, and reports what it found. If a handle is found, you’ll see the address of the reference. This might be a stack address or a field within an object, for example. If a handle is not found in memory, you’ll get notification of that too.

The command has diagnostic output which doesn’t need to be repeated here. One thing to keep in mind is that anytime you search all of memory for a value, you can get false positives because even though the value was found, it might be garbage in that no code knows about the address. You can also get false negatives because a user is free to pass that GCHandle to unmanaged code that might store the handle in a strange way (shifting bits, for example). For example, a GCHandle valuetype is stored on the stack with the low bit set if it points to a Pinned handle. So !GCHandleLeaks ignores the low bit in it’s searches.

That said, if a serious leak is going on, you’ll get a ever-growing set of handle addresses that couldn’t be found.

!gchandleleaks
-------------------------------------------------------------------------------
GCHandleLeaks will report any GCHandles that couldn't be found in memory.
Strong and Pinned GCHandles are reported at this time. You can safely abort the
memory scan with Control-C or Control-Break.
-------------------------------------------------------------------------------
Found 19 handles:
009111a8	009111ac	009111b4	009111bc
009111c0	009111cc	009111d0	009111d4
009111d8	009111dc	009111e0	009111e4
009111e8	009111f8	009111fc	009113f0
009113f4	009113f8	009113fc
Searching memory
Reference found in stress log will be ignored
Error during command: Warning. Extension is using a callback which Visual Studio does not implement.

------------------------------------------------------------------------------
Some handles were not found. If the number of not-found handles grows over the
lifetime of your application, you may have a GCHandle leak. This will cause
the GC Heap to grow larger as objects are being kept alive, referenced only
by the orphaned handle. If the number doesn't grow over time, note that there
may be some noise in this output, as an unmanaged application may be storing
the handle in a non-standard way, perhaps with some bits flipped. The memory
scan wouldn't be able to find those.
------------------------------------------------------------------------------
Didn't find 19 handles:
009111a8	009111ac	009111b4	009111bc
009111c0	009111cc	009111d0	009111d4
009111d8	009111dc	009111e0	009111e4
009111e8	009111f8	009111fc	009113f0
009113f4	009113f8	009113fc
Category: Debugging — Tags: , , , , — admin @ 8:21 pm

SOS Command: !DumpIL

        <Managed DynamicMethod object> |
        <DynamicMethodDesc pointer> |
        <MethodDesc pointer>

!DumpIL prints the IL code associated with a managed method. We added this function specifically to debug DynamicMethod code which was constructed on the fly. Happily it works for non-dynamic code as well.

You can use it in three ways:

  1. If you have a System.Reflection.Emit.DynamicMethod object, just pass the pointer as the first argument.
  2. If you have a DynamicMethodDesc pointer you can use that to print the IL associated with the dynamic method.
  3. If you have an ordinary MethodDesc, you can see the IL for that as well, just pass it as the first argument.

Note that dynamic IL is constructed a bit differently. Rather than referring to metadata tokens, the IL points to objects in a managed object array. Here is a simple example of the output for a dynamic method:

  0:000> !dumpil b741dc

This is dynamic IL. Exception info is not reported at this time. If a token is unresolved, run “!do <addr>” on the addr given in parenthesis. You can also look at the token table yourself, by running “!DumpArray 00b77388″.

  IL_0000: ldstr 70000002 "Inside invoked method "
  IL_0005: call 6000003 System.Console.WriteLine(System.String)
  IL_000a: ldc.i4.1
  IL_000b: newarr 2000004 "System.Int32"
  IL_0010: stloc.0
  IL_0011: ldloc.0
  IL_0012: ret
Category: Debugging — Tags: , , , , — admin @ 8:13 pm

SOS Command: !DumpSig <sigaddr> <moduleaddr>

Category: Debugging — Tags: , , , , — admin @ 8:11 pm

SOS Command: !DumpMethodSig <sigaddr> <moduleaddr>

Category: Debugging — Tags: , , , , — admin @ 8:07 pm

SOS Command: !DumpAssembly <Assembly address>

Example output:

0:000>!dumpdomain
...
0:000> !dumpassembly 1ca248
Parent Domain: 0014f000
Name: C:pubunittest.exe
ClassLoader: 001ca060
  Module Name
001caa50 C:pubunittest.exe

An assembly can consist of multiple modules, and those will be listed. You can get an Assembly address from the output of !DumpDomain.

Older Posts »

©2009 NetInverse. All rights reserved. Powered by WordPress