Func-eval is evil
Function evaluation (aka “Func-eval”, “property evaluation”, “prop eval”) is the ability for a debugger to have the debuggee call a random function when stopped at a breakpoint. For eg, this lets you call functions from VS’s immediate window.
Func-eval is extremely useful. VS heavily uses it in inspection to evaluate property getters and ToString(). I think VS visualizers (in v2.0) may do even more funcevals.
But Funceval is also extremely dangerous. And that puts us in a real dilemma.
In order to funceval, the debugger needs to hijack a thread and then resume the debuggee under the covers so that it can run the function. The debugger gets notified when the function completes and then restores that thread to its pre-funceval state.
Why is funceval evil?
1) It poses some complicated UI problems for a debugger. For example:
a. Should the debugger suspend all the other threads or not? If it does, then if the func-eval thread blocks on another thread, it hangs. If it does not, then those other threads can run during the func-eval and cause tremendous side effects. Visual Studio happens to suspend all other threads.
b. How should the debugger handle things like hitting a breakpoint in a function called indirectly by the funceval? Should it stop at it or ignore it? If it stops, the debugger needs to the UI to handle “nested break states”. On the other hand, users expect their breakpoints to be hit, especially if the function has side-effects. VS 2003 skipped the breakpoints, and VS 2005 handles nested break states for certain funcevals (such as ones explicitly initiated from the immediate window).
2) The function may change state underneath you. For example:
a. what if that read-only property has some side-effect (e.g. goes and initializes a cache)? Steve Steiner (VS debugger dev) has a great example here.
b. An evil function may have bigger changes such as taking (but not releasing) locks or even creating new threads.
c. What if the function causes a Garbage collection? That could move around all the objects in the process (which is one reason we added object identity)
3) The function may hang, and that may cause the debugger to hang waiting for the evaluation to complete. The CLR has mechanisms to try and abort a func-eval, but that’s also very risky business and not always possible. Even if it does abort, the user still is blocked for a little bit. And if the abort can’t fully restore state, the debuggee may be permanently corrupted. Reasons it may hang include:
a. what if the function goes into an infinite loop? (This can usually be aborted if in managed code)
b. what if the function p-invokes out into native code and then blocks there? (This can’t be aborted until the thread comes back into managed code).
c. what if the function tries to make a cross thread call? This is very common for imported COM objects that need to marshal requests back to a single owning thread. VS suspends all other threads, including message pump threads, to prevent them from causing side effects during the funceval.
d. what if the function blocks on a lock owned by another (suspended) thread? This can also happen if the function tries to do a garbage collection (which requires bringing all threads in the process to a cooperative suspension) and another thread is not cooperating (likely because it was suspended at a unsafe place for GCs)?
4) Funceval will introduce code paths that could not normally happen, and that may cause unexpected behavior. What if a function is called at a place where it wasn’t designed to be called?
a. For example, a read-only property may assert that certain state is valid. If you stop at a breakpoint in the ctor before that state is set and expand the this pointer, VS will still call that property. This can do things like cause asserts in the getter to fire.
b. What if you go up the stack and then inspect things that cause more func-evals? This could lead to some ugly reentrancy issues.
Any debugger that implicitly does funceval must provide a way to turn it off. Gregg explains how to disable it in VS:
Property evaluation can be turned of by disabling the option 'Tools->Options->Debugging->General->Allow property evaluation in variables windows'.
He also elaborates that a debugger can be smart about doing the right funcevals if it takes into account things like public/private, just-my-code status, and information from the project system.