NetInverse Developers Blog

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.

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

SOS Command: !ThreadPool

This command lists basic information about the ThreadPool, including the number of work requests in the queue, number of completion port threads, and number of timers.

!ThreadPool
CPU utilization 0%
Worker Thread: Total: 0 Running: 0 Idle: 0 MaxLimit: 0 MinLimit: 0
Work Request in Queue: 0
--------------------------------------
Number of Timers: 0
--------------------------------------
Completion Port Thread:Total: 0 Free: 0 MaxFree: 0 CurrentLimit: 0 MaxLimit: 1000 MinLimit: 0
Category: Debugging — Tags: , , , , — admin @ 5:13 pm

SOS Command: !DumpModule [-mt] <Module address>

You can get a Module address from !DumpDomain, !DumpAssembly and other functions. Here is sample output:

0:000> !dumpmodule 1caa50
Name: C:pubunittest.exe
Attributes: PEFile
Assembly: 001ca248
LoaderHeap: 001cab3c
TypeDefToMethodTableMap: 03ec0010
TypeRefToMethodTableMap: 03ec0024
MethodDefToDescMap: 03ec0064
FieldDefToDescMap: 03ec00a4
MemberRefToDescMap: 03ec00e8
FileReferencesMap: 03ec0128
AssemblyReferencesMap: 03ec012c
MetaData start address: 00402230 (1888 bytes)

The Maps listed map metadata tokens to CLR data structures. Without going into too much detail, you can examine memory at those addresses to find the appropriate structures. For example, the TypeDefToMethodTableMap above can be examined:

0:000> dd 3ec0010
03ec0010  00000000 00000000 0090320c 0090375c
03ec0020  009038ec ...

This means TypeDef token 2 maps to a MethodTable with the value 0090320c. You can run !DumpMT to verify that. The MethodDefToDescMap takes a MethodDef token and maps it to a MethodDesc, which can be passed to !DumpMD.

There is a new option “-mt”, which will display the types defined in a module, and the types referenced by the module. For example:

0:000> !dumpmodule -mt 1aa580
Name: C:pubunittest.exe
...<etc>...
MetaData start address: 0040220c (1696 bytes)

Types defined in this module
      MT    TypeDef Name
------------------------------------------------------------------------------
030d115c 0x02000002 Funny
030d1228 0x02000003 Mainy

Types referenced in this module
      MT    TypeRef Name
------------------------------------------------------------------------------
030b6420 0x01000001 System.ValueType
030b5cb0 0x01000002 System.Object
030fceb4 0x01000003 System.Exception
0334e374 0x0100000c System.Console
03167a50 0x0100000e System.Runtime.InteropServices.GCHandle
0336a048 0x0100000f System.GC
Category: Debugging — Tags: , , , , — admin @ 5:03 pm

SOS Command: !Token2EE <module name> <token>

This function allows you to turn a metadata token into a MethodTable or MethodDesc. Here is an example showing class tokens being resolved:

!token2ee unittest.exe 02000003

Module: 001caa38
Token: 0x02000003
MethodTable: 0090375c
EEClass: 03ee1ae0
Name: Bank

!token2ee image00400000 02000004
Module: 001caa38
Token: 0x02000004
MethodTable: 009038ec
EEClass: 03ee1b84
Name: Customer

The module you are “browsing” with Token2EE needs to be loaded in the process. This function doesn’t see much use, especially since a tool like ILDASM can show the mapping between metadata tokens and types/methods in a friendlier way. But it could be handy sometimes.

You can pass “*” for <module name> to find what that token maps to in every loaded managed module. <module name> can also be the debugger’s name for a module, such as mscorlib or image00400000.

Category: Debugging — Tags: , , , , — admin @ 4:56 pm

SOS Command: !DumpMD <MethodDesc address>

This command lists information about a MethodDesc. You can use !IP2MD to turn a code address in a managed function into a MethodDesc:

!DumpMD 00933008
Method Name: FindProduct.Program.AppendProduct(System.Collections.Generic.List`1<FindProduct.Product>)
Class: 00931300
MethodTable: 00933024
mdToken: 06000002
Module: 00932c5c
IsJitted: yes
CodeAddr: 00ff00e8

If IsJitted is “yes,” you can run !U on the CodeAddr pointer to see a disassembly of the JITTED code. You can also call !DumpClass, !DumpMT, !DumpModule on the Class, MethodTable and Module fields above.

Category: Debugging — Tags: , , , , — admin @ 4:19 pm

SOS Command: !DumpClass <EEClass address>

The EEClass is a data structure associated with an object type. !DumpClass will show attributes, as well as list the fields of the type. The output is similar to !DumpObj. Although static field values will be displayed, non-static values won’t because you need an instance of an object for that.

You can get an EEClass to look at from !DumpMT, !DumpObj, !Name2EE, and !Token2EE among others.

!dumpclass 00931300
Class Name: FindProduct.Program
mdToken: 02000002 (C:svnNetInverseFindProductbinDebugFindProduct.exe)
Parent Class: 790c3ef0
Module: 00932c5c
Method Table: 00933024
Vtable Slots: 4
Total Method Slots: 5
Class Attributes: 100000
NumInstanceFields: 0
NumStaticFields: 0
« Newer PostsOlder Posts »

©2009 NetInverse. All rights reserved. Powered by WordPress