Func-eval abort is evil


Func-eval is evil. Func-eval abort is even worse. For those coming in late, Func-eval is when the debugger hijacks a thread and has it evaluate some function such as a property-getter or to-string.   Func-eval abort is when that evaluation hangs, and then the debugger aborts it (similar to Thread.Abort). 

Visual Studio sets up a timer when it does automatic func-evals, and will automatically issue an abort after a few seconds.

For example, say you have a very very evil property like the one below.

    class Evil
    {
        public string EvilProperty
        {
            get
            {
                Console.WriteLine("start");
                Thread.Sleep(1000 * 10);
                Console.WriteLine("done");
                return "finished!";
            }
        }
    }

If you allocate an Evil object in VS, and then view it in the watch window, VS will automatically func-eval the property getters (if func-eval is enabled). If the eval takes too long (which that Sleep will ensure in this case), it will abort the func-eval. It looks like this:

 

(screenshots courtesy of Windows Live Writer! )

ICorDebug?
For debugger authors implementing func-eval support, the corresponding APIs are ICorDebugEval::Abort and ICorDebugEval2::RudeAbort. Like most ICorDebug operations, these are asynchronous. Issue them, call Continue(), and then wait for an Abort Complete (EvalComplete/EvalException) event.

So why is it evil?

Some reasons why abort is evil and should not be a backbone of any debugging strategy:

  1. Abort may not always be possible. For example, the CLR can’t abort a thread in native code. This can be a very common scenario for winforms properties that do a SendMessage.
  2. Abort has all the same problems of Thread.Abort and more. For example, it can leave the managed user state in an undefined position. For example, if you abort a thread in the middle of updating some user data structure (eg,a hash), that structure may then be left in an inconsistent state.
  3. At a abstract level, we all recognize that func-evals change program behavior; and aborts are even worse. Func-eval at least executes an entire function atomically. Abort executes a random subset of the function, and can thus have very significant unpredictable behavior changing affects.
  4. Rude-abort doesn’t run backout code, and may leave the thread in an undefined state. For example, it may leave locks orphaned causing strange deadlocks later on.
  5. If abort becomes very common, these previous issues may lead to a lot of “My code breaks under the debugger, it’s a debugger bug”.
  6. Use features the way they were intended. Abort was supposed to be a mitigation for stray func-evals, not a mainline scenario. 
Comments (6)

  1. Matt Davis says:

    So, was this a hypothetical caution, or the result of a recent "you’re trying to do WHAT?!"?

  2. David Levine says:

    Even though a property looks like a field it can really be a very complex function call (especially when using lazy initialization, network or database calls, etc.). Throwing an abort into this can be problematic.

    Is there an attribute (e.g. DebuggerBrowsable?) that a property can be decorated with to tell the debugger to not perform a func-eval, and perhaps substitute some other display string? That would give the developer some means of telling the debugger to avoid evaluating "evil" (or perhaps lazy) properties.

    Conversely, if there were a Debugger.InFuncEval() API then a property getter could transition from "evil" to "smart" depending on its context.

  3. David –

    Yes, the DebuggerDisplay attribute does exactly that (avoid func-eval).

    See http://msdn2.microsoft.com/en-us/library/x810d419.aspx

    Re Debugger.InFuncEval. There isn’t any, partially because that could really be used for evil. Fortunately, the attribute above help mitigate that.

    That exact question came up in the forums at:

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=760398&SiteID=1,

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=892808&SiteID=1

  4. Last update: June 13 , 2007 Document version 0.6 Preface If you have something to add, or want to take

  5. Common INFO: · const is "baked" into the assembly. If you have to change the const value in

  6. This section describes the common .NET tips which don't relates to the specific category. INFO: