Write your own GC Heap Viewer for the .Net Compact Framework

The last few versions of the Remote Performance Monitor enable you to view snapshots of the GC heap on demand.  The view of the heap presented by RPM is oriented around finding managed memory leaks.  Specifically, the data is organized so that it's easy to see which GC root is responsible for keeping a given object instance alive. 

We hope the views are well organized and easy to understand, but if you find they don't suite your needs for any reason you can write your own tool to analyze and display the heap data.  For example, maybe you want a fancier graphical representation, or you want a view tailored to a scenario other than finding memory leaks.

You can't currently plug your new UI directly into RPM but you can save the heap snapshots to a file and create your own tool to view the data.

In this post I'll briefly describe how to save the heap data then I'll provide the details of the file format.

Saving a GC Log

Saving a snapshot of the GC heap to a file is easy: Given an open view, just select the "Save" option from the "File" menu:

save

You'll also be prompted to save any unsaved views when you close them.

The GC Log File Format

GC heap dump files are text files that include information for every object present in the GC heap at the time the heap dump was generated using RPM. 

Each line in the text file contains one record.  There are 5 different types of records:

  • AppDomain record. Identifies the application domain for which a dump file applies.
  • Type record. Describes a type present in the heap.
  • Object record. Describes an instance of a type present in the heap.
  • Root record. Describes a root instance.
  • End AppDomain record. Marks the end of the dump file.

 

Each record contains a number of elements separated by spaces.  The first element in each line indicates the record type.  Here's a portion of a dump file:

Note: This example is intended to show format only - it's likely not semantically accurate.

a 2 bubblecs.exe

t 2 System.RuntimeType

o 7c79f 2 64

t 1 System.NullReferenceException

o 7cb42 2 64

o 1ce004 1 18

t 3 System.OutOfMemoryException

o 1ce056 1d 118 1ce11f 1ce14c 1ce079 1ce113 1ce116 1ce267 1ce26a 1ce26d

r 22c81b 5 0

o 22c81e 1b 24

r 22c81e 5 0

o 22c823 1b 28

r 22c823 5 0

c bubblecs.exe

 

AppDomain Record

An AppDomain record initiates a section of the file pertaining to a particular application domain.  Within that section is all the type, object and root information for the managed objects in that application domain.  All AppDomain records are closed with a corresponding End AppDomain record.

The AppDomain record has 4 elements:

  • The letter "a". Identifies the record as an application record.
  • Version number. A number used to track the file format of the dump file itself.  In the current releases this number will always be "2".
  • AppDomain Name. The name of the application domain from which this dump file was generated.  This is typically the name of the exe that was run in the app domain.
  • Timestamp. The time at which the heap dump was take.  RPM obtains this number by calling the WinCE API GetTickCount.

 

Example:

a 2 NHLSchedule.exe 444d20df

 

Type Record

Type records identify types in the GC heap.  Each type record contains 3 elements:

  • The letter "t". Identifies the record as a type record.
  • Type ID. A unique numerical identifier for this type.  This identifier is used to tie object instances to their types (see description of Object Records below).
  • Type name.   The fully qualified name of the type.

 

Example:

t a1 Western.Pacific.SanJoseSharks

Note:

  • Type identifiers are not guaranteed to be unique across dump files.  For example, the identifier for the "SanJoseSharks" type in the example above isn't guaranteed to be a1 in a different dump taken from the same application at a different point in time.

Object Record

Object records describe specific instances of types in the GC heap.  Object records have a variable number of elements.  The first 4 elements are required:

  • The letter "o". Identifies the record as an object record.
  • Object ID. A unique numerical identifier for this object. 
  • Type ID. The identifier for the type of this object.
  • Object size. The size in bytes for this instance.

In addition to these required elements, object records will have a variable number of additional elements describing the instances that the object references.

  • Referenced Object IDs. The identifiers for all objects that this object references. 

 

An example with referenced object ids:

o 1c15d2 3 1c 1c15db 1c15d8 1c15d5

Notes:

  • Object identifiers are not guaranteed to be unique across dump files.  This point is important for tools writers:  It is not possible to develop tools that identify trends across dump files because a given object instance in one dump isn't guaranteed to have the same identifier in a later dump of the same heap.
  • The type record corresponding to a particular object record is not guaranteed to precede the object record in the dump file.

Root Record

Root records identify GC roots.  Each root record contains 4 required elements:

  • The letter "r".   Identifies the record as a root record.
  • Object ID. The identifier of the object that is this root.
  • Root kind. The reason this object instance is a root.  One of the following values:

1 The object instance is a local variable.

2 The object instance is on the finalizer queue waiting for its finalizer to be run.  After the finalizer is run the object will be collected during the next GC.

3 A GC handle exists which refers to the object instance (i.e System.Runtime.InteropServices.GCHandle)

4 The object instance is a static variable.

5 The object instance is a root specific to the Compact Framework's GC implementation.  Examples include interned strings or class descriptions.

0 The object instance is rooted internally by the Compact Framework. Examples include object instances for application domains, assemblies, exceptions and so on.

  • Root flags. A set of flags providing more information about the root:

0x1. The root is pinned.  There are several cases in which a root may be pinned.  For example, the root may refer to managed objects that have been passed out to native code via PInvoke or COM interop, the root may be referenced by a GCHandle created with a GCHandleType of Pinned or a pointer in unsafe code points to the root. 

The .Net Compact Framework CLR may also pin objects on your behalf as it runs your application.  The CLR will pin an object if it places a reference to that object in a register or refers to that object from the stack.  An object will also be pinned if a local variable or method argument points to a field within the object.  The CLR doesn't pin objects very often, but if one of the more obvious reasons don't explain why an object is pinned, it may be because of one of these "hidden" cases.

0x2. A GCHandle whose GCHandleType is Weak refers to the object.

0x4. The object is pointed to by a pointer in unsafe code or has a local variable or method argument points to a field within the object.

If the root kind is static, the root record will have an additional element:

  • Root container.   The identifier of the type the static variable is contained in.

Here's a root record with the additional root container element:

r b2753 4 0 13c

 

End AppDomain Record

End AppDomain records close the section initiated by a corresponding AppDomain record.   End AppDomain records have three elements:

  • The letter "c". Identifies the record as an end record
  • Application Name.   The name of the application from which this dump file was generated.  This name matches the name in the application record at the beginning of the file.
  • Timestamp.   The timestamp from the corresponding AppDomain record.

 

Thanks,

Steven

This posting is provided "AS IS" with no warranties, and confers no rights.