Don’t have your non-debugger app use the debugging services just to get some cool functionality.
The managed debugging services are not just another library that picks up where BCL/ Reflection left off.
The BCL + reflection provides some basic ability to inspect your own process, including:
- getting a current thread’s stacktrace, including names of each function on the stack.
- looking up a method by string name at runtime.
- dynamically invoking methods.
- enumerating types within an assembly.
The managed debugging services (eg, ICorDebug, MDbg) provide some very cool additional functionality along these lines, including:
- The ability to lookup local variables by name, and get and set them.
- The ability to get source filenames for functions.
- Sniffing interesting events such as exceptions.
- The ability to change the current instruction pointer within a function
- The ability to enumerate all threads, modules, assemblies, and appdomains within a process.
Every so often, somebody wants to add something like the above functionality to their app, finds that BCL/Reflection/etc doesn’t do it, and then wants to pursue it via the debugging APIs.
Specifically, the debugging APIs are for diagnostic tools. Don’t use the debugging APIs for non-diagnostic production behavior.
Here are some reasons why you shouldn’t use the debugging APIs for non-debugger functionality in production cases :
- It may break in optimized code. That local that you’re looking for may get optimized out. That function caller your sniffing for may have been inlined. That generic parameter you want to find may get code-folded away.
- A process can’t debug itself. So you at least need a 2nd process to serve as a debugger. Now you need to communicate between 2 processes. That has some complexities, including bridging the gap between ICorDebugValue and System.Object.
- Only 1 debugger can be attached to a process. So if your app spins up a debugger to attach to itself, you couldn’t spin up a “real” debugger (like VS) to debug your app.
- Running under a debugger can change performance characteristics. PSA- Pinvokes may be 100x slower under the debugger
- It creates a new versioning boundary. And the debugger versioning story is different than the BCL’s, and so an app using the debugging services on itself would get sucked into the worst of both worlds.
- And it just adds extra complexity. You immediately add a 2nd process, at least 3 more threads, another versioning boundary, and a lot more moving parts…
Same reasoning applies for other tool APIs like the profiler.
Now that said, what are good uses of the debugger APIs?