1) Ensuring that the mere presence of a debugger doesn’t affect codegen flags. Eg, in v1.1, if a debugger was attached, we’d jit the code differently (such as disabling optimizations) to make it more debugger-friendly. This can be nice, except that the different code-gen may mean different behavior under a debugger vs. not under a debugger.
2) Debugging Ngen: Because of #1, it was very difficult to load optimized ngen images under a debugger. Issue #1 caused the debugger to specify debuggable code-gen flags, so it would look for a debuggable-ngen configuration, not find it, and thus not load the ngen images. We’ve fixed this by fixing #1, and adding ngen-policy APIs onto the process via ICorDebugProcess2::SetDesiredNGENCompilerFlags.
3) Always track jit maps. In v1.1, you had to fiddle with ini files to tell the jit to track jit-maps for debugging for an app that wasn’t launched under the debugger. In v2.0, the CLR will always track these maps. Fixing this required finding a performant way to track these maps. The challenge here was to store these maps in an efficient manner.
4) Unifying the Launch and Attach case. You don’t want a different debugging experience based off whether you’ve launched an app (eg, debugging some local console app you wrote), or you attached to it at a later time (eg, debugging ASP.Net). With changes #1 + #3 above, the attach case behaves just like the launch case.