LoadClass events are usually meaningless

ICorDebug notifies a debugger when a managed class is loaded via LoadClass debug event. For dynamic-modules, this is useful because it tells you that you just baked a new type and so may have new code to bind breakpoints in (see here for debugging ref-emit).

But for everything else, this is useless. LoadClass doesn’t really provide much value beyond LoadModule. For example:

  1. LoadClass does not mean that any of the methods in the class are jitted.

  2. LoadClass does not mean that the cctor is run.  (cctors are run lazily)

  3. LoadClass does not mean that the class statics are initialized (those are also initialized lazily)

  4. LoadClass events usually don’t even come. A debugger has to explicitly subscribe to them (EnableClassLoadCallbacks) for non-dynamic modules. And they don’t even fire for ngen modules. So clearly they can’t be doing anything too essential.

  5. LoadClass just gives you an ICorDebugClass back, but you could just as easily get that calling ICorDebugModule::GetClassFromToken at the LoadModule callback.

Basically, LoadClass just means that the CLR “touched” the class. ICorDebugClass is a pretty anemic interface that doesn’t let you inspect very much. It’s possible if there were more powerful inspection methods on ICDClass that they may lend meaning to LoadClass. For example, maybe when the CLR fires a LoadClass event, internally it did class layout and could now tell you what the offsets of each field are. But since ICorDebug doesn’t let you directly get field offsets, that’s a mute point.

Furthermore, since LoadClass didn’t have any clear semantics to preserve, there was not a clear path for how it should be extended to handle generics in V2.

The moral of the story: Don’t have APIs that don’t actually mean anything.

Comments (1)

  1. When you attach to a managed debuggee (via ICorDebug::DebugActiveProcess), ICorDebug generates a set