I mentioned earlier that you can debug Reflection.Emit code. Unfortunately, Ref.Emit code can't be unloaded unless you unload the entire appdomain. I wanted to lay out the current landscape, and then get feedback about possible solutions.
In Whidbey, we added Light Weight CodeGen (LCG), which are dynamically generated methods that can be garbage collected. This fixes the unloading problem with Ref.Emit code. Unfortunately, LCG methods aren't really debuggable. You can't currently associate them with source, and they're basically hidden when debugging. In Whidbey, they show up as an “InternalFrame” marker in the callstack, which has no information other than the annotation that there is some dynamic code on the stack. If a debug event fires (such as an exception / Debug.break statement) is in dynamic method, we will stop, and then it’s nice to have that internal Frame on the callstack. But nothing is really debuggable about it in Whidbey.
In other words, with ICorDebug (and thus debuggers like VS that use that):
- You can’t set breakpoints in dynamic methods (LCG)
- You can’t see the IL of dynamic methods
- You can’t see the locals / arguments of dynamic methods
- You can’t map dynamic methods back to source code
- You can’t step into or out of dynamic methods (it’s like native code when managed-only debugging)
The current workarounds are:
1) Use windbg and SOS. See demo here
2) Use Ref.Emit for code that you want to be debuggable.
3) Use other tricks, like Haibo's IL visualizer.
But ultimately in Whidbey (.NET 2.0), you're forced to choose between debuggability (use Ref.Emit or normal code) vs. fine-grained unloadability beyond just appdomain unloading (use LCG).
There's a few ways the CLR can solve this, including (but not limited to):
1. Make LCG debuggable, particularly adding the ability to associate source information with LCG methods, like you do with normal Ref.Emit when you call MarkSequencePoint.
2. Make Ref.Emit code collectable, ala LCG.
Do folks out there have any preference in solution?
I realize there's also a large dial about what is considered debuggable. Eg, source-level debuggability, vs. assembly level vs. IL level. (I could do a whole other blog entry about that). Any comments on where the dial should be set are welcome too.