Partition of ICorDebug

The ICorDebug API (the API for debugging managed apps) is about 70 total interfaces.  Here is how I’d group the interfaces together, along with my random comments about how various interfaces fit into the big picture.

A quick comment about interface versioning:
1. ICorDebug is a COM-classic unmanaged interface. Most of the interfaces are derived from IUnknown because we wanted to avoid the diamond-inheritance problem when we needed to add version 2 interfaces. I’ve left the “Derives From” column blank if an interface derives from IUnknown.
2. Version 2 interfaces have the previous interface’s name appended with a version number. (eg, “IFoo2”)

I’d roughly partition it into the following groups:

Top-level: ICorDebug + ICorDebug2 are the top-level interfaces which effectively serve as a collection of ICorDebugProcess objects.

Interface Derives from Comments
ICorDebug   Top level object which hands out all other objects. You can create an instance of ICorDebug with the CreateDebuggingInterfaceFromVersion  function.
ICorDebug2   Container for interface versioning data

Callbacks: Managed debug events are dispatched via methods on a callback object implemented by the debugger:

Interface Derives from Comments
ICorDebugManagedCallback   Callback interface to dispatch managed debug events to debugger.
ICorDebugManagedCallback2   new V2 callbacks
ICorDebugUnmanagedCallback   dispatch native DEBUG_EVENT for interop-debugging
ICorDebugMDA   Packages parameters for the Managed Debug Assistant (MDA) callback. This was done because MDAs have some potentially large string parameters that we wanted to allow lazy retrieval of.


Process: This set of interfaces represents running code and includes the APIs related to eventing.

Interface Derives from Comments
ICorDebugController   It has Stop/Go methods related to whether threads are stopped or running such as Stop(), Continue(), IsRunning(), HasQueuedCallbacks(), etc.
This is the base interface for ICorDebugProcess and ICorDebugAppDomain.
ICorDebugProcess ICorDebugController Represents a live process. The Stop/Go methods operate on the entire process, just as their counterparts would in native debugging.
IcorDebugAppDomain ICorDebugController Represents an appdomain within a process. AppDomain  derives from Controller because we wanted the API to support partial-process-debugging on a per-AppDomain basis.

Code / Type Inspection:  Could mostly operate on a static PE image, although there are a few convenience methods for live data.

Interface Derives from Comments
ICorDebugAssembly   Represents an assembly. While assemblies are very important for deployment and versioning issues, they are a largely uninteresting abstraction to the debugger.  Practically for a debugger, an Assembly is a module container.
ICorDebugModule   Represents a module. Modules are very significant to the debugger because that corresponds to raw files. A module is 1:1 with a metadata importer, and associated with a PDB file.
ICorDebugFunction   Represents a method as defined in the IL. ICDFunction is 1:1 with a MethodDef. So there is 1 ICDFunction for List<T>::Add(T item). Edit-and-Continue introduces new IL, and thus new ICDFunction instances.
ICorDebugCode   Represents either IL or native code bytes for a function. This is 1:1 with the raw code. So  if “Add(T item)” instantiated twice for “Add(String item)” and “Add(int item)”, each would get their own ICDCode instance (unless the instances shared the same code bytes).
ICorDebugCode2   Support non-contiguous code bytes. This lets the JIT optimize how basic blocks get mapped to hot pages.
ICorDebugClass   Represents a type (either Reference-Type or Value-Type) as defined in the IL. ICDClass is 1:1 with a TypeDef, and so it does not handle generics. An ICDClass may represent “List<T>”
ICorDebugType   Added in V2 to handle generics. Represents the fully instantiated type. An ICDType may represent “List<int>”.

Execution Control: Execution is the ability to “inspect” a thread’s execution. Practically, this means things like placing breakpoints (F9) and doing stepping (F11 step-in, F10 step-over, S+F11 step-out). ICorDebug’s Execution control only operates within managed code.

Interface Derives from Comments
ICorDebugBreakpoint   Abstract base interface for breakpoints
ICorDebugFunctionBreakpoint ICorDebugBreakpoint Breakpoint somewhere in managed code.
ICorDebugStepper   Interface for step operations within managed code. More on stepping here
ICorDebugStepper2   Added support for Just-My-Code (JMC)
ICorDebugEval   Support for func-eval. From an API perspective, func-eval is definitely execution control: it injects a call into the app, lets the app run, and then waits for an aysynchronous complete event.  From a end-user perspective, func-eval is generally used as an inspection tool (eval property getters and ToString)
ICorDebugEval2   Just like ICorDebugEval but using ICorDebugType to support generics.


Threads + Callstacks: Callstacks are the backbone of the debugger’s inspection functionality. The following interfaces are related to taking a callstack. ICorDebug only exposes debugging managed code, and thus the stacks traces are managed-only.

Interface Derives from Comments
ICorDebugThread   Represents a managed thread, which is a thread that has entered managed code at some point. This includes threads are currently in native code and may not even have any managed frames on their stack. More trivia here.
ICorDebugChain   Collection of frames on the callstack. I personally think this is a stupid abstraction and we should never had had this interface. It’s just one more hassle to get a stack trace.
Each range of unmanaged frames are also grouped together under a single ICDChain instance (with the Managed property set to false). A debugger can manually walk native frames from these chains and then stitch them back in with the managed frames to get a mixed-mode callstack.
ICorDebugFrame   Abstract base interface for a frame on the callstack
ICorDebugILFrame ICorDebugFrame Exposes the IL properties of a frame (such as the IL offset, IL locals).
ICorDebugNativeFrame ICorDebugFrame Exposes the native properties of a managed frame (such as the native offset). This does not expose any information about unmanaged frames.
ICorDebugInternalFrame ICorDebugFrame Added in whidbey to let ICD mark “interesting” spots in the stack such as stubs that may catch or throw exceptions. These are a cleaner way to provide ICDChain functionality.
ICorDebugRegisterSet   Provide an abstract view of a CONTEXT. This allows representing  contexts that  don’t have all registers valid, such as the context on a non-leaf stack frame.
ICorDebugRegisterSet2   Add 64-bit support to ICorDebugRegisterSet


Object Inspection: Object inspection is the part of the API that lets you see the values of the variables throughout the debuggee.   For each interface, I list the “MVP” method that I think must succinctly conveys the purpose of that interface.

Interface Derives from Comments MVP method
ICorDebugValue   Base class for value inspection  
ICorDebugValue2   Addition for generics to get an ICorDebugType GetExactType
ICorDebugGenericValue ICorDebugValue Represents primitive blittable values like int, double, etc. GetValue
ICorDebugObjectValue ICorDebugValue Represents an actual object with fields (reference or value type).  The GetSize() property of this will be the size of the actual object. GetFieldValue
ICorDebugReferenceValue ICorDebugValue Represents a reference value. The GetSize() property of this will be sizeof(void*). DereferenceValue
ICorDebugHandleValue ICorDebugReferenceValue Lets the debugger create a handle to provide object identity. DereferenceValue
ICorDebugHeapValue ICorDebugValue    
ICorDebugHeapValue2     CreateHandle
ICorDebugBoxValue ICorDebugHeapValue Represents a  boxed value GetObject
ICorDebugStringValue ICorDebugHeapValue Represents a System.String. GetString
ICorDebugArrayValue ICorDebugHeapValue Represents an array. GetElementAtPosition


Enumerators: All enumerators in ICorDebug derive from the ICorDebugEnum class and have the same usage semantics. I put the enumerators off into their own group because conceptually, we don’t users to focus on them specifically. For example, in a managed wrappers, all the enumerators could be exposed as IEnumerable<T>, and thus wouldn’t have their own dedicated types. Not all of these enumerators are implemented.

Interface Derives from Comments
ICorDebugObjectEnum ICorDebugEnum  
ICorDebugBreakpointEnum ICorDebugEnum  
ICorDebugStepperEnum ICorDebugEnum  
ICorDebugProcessEnum ICorDebugEnum  
ICorDebugThreadEnum ICorDebugEnum  
ICorDebugFrameEnum ICorDebugEnum  
ICorDebugModuleEnum ICorDebugEnum  
ICorDebugValueEnum ICorDebugEnum  
ICorDebugCodeEnum ICorDebugEnum  
ICorDebugTypeEnum ICorDebugEnum  
ICorDebugAppDomainEnum ICorDebugEnum  
ICorDebugAssemblyEnum ICorDebugEnum  


Deprecated / Never implemented:  These are listed for completeness sake. Since they’re not used, I’ll refrain from commenting about them.

Interface Derives from Comments
 ICorDebugModuleBreakpoint  ICorDebugBreakpoint  
ICorDebugValueBreakpoint ICorDebugBreakpoint  
ICorDebugErrorInfoEnum ICorDebugEnum  
ICorDebugContext ICorDebugObjectValue  


Note that this is not intended to be any form of official documentation. See MSDN for official docs and specs.This is just my random commentary.
I may come back and update this in the future.

Comments (2)

  1. Sameer V says:

    This is helpful 😉

    wish you and other readers a Happy new year.


  2. You can discern a lot of information about an API from what appear to be subtle or irrelevant details…