The Debugger Rules


I alluded to some debugger cardinal rules in a prior post. However, they may well be something that debugger writers know, but haven’t written down. To remedy, I’ll start the list here.

My first two rules:

1) Never lie to the user. Telling them incorrect information is an unpardonable sin, punishable by having to debug the user’s programs for them.

2) Don’t change the behavior of the target app. This may not be always technically feasible, but minimize the effect as much as humanly possible. It drives me nuts when something operates differently under the debugger when the debugger didn’t have to muck with the process. Tool like profilers and automatic error detection (e.g., BoundsChecker) should abide by this rule as well.

Russ Osterlund adds two more:

3) Never appear “weaker” than the target app.  (Classic examples of this are the VB debugger and the VS.NET managed debugger that prevent one from stepping into system code or disallow one from viewing valid “memory” addresses.)

4) Never make the user work harder than they need to — debugging is tough enough without the main instrument of the effort getting in the way.

Russ’s PEBrowse Interactive does a great job of adhering to #4. Context is everything. Russ’s program has killer context menus that make it trivial to do just what you need to do. My dream debugger would be Visual Studio with Russ’s context commands.

Anybody else got any inviolable debugger rules to add?


Comments (8)

  1. Mike Stall says:

    I fully agree with #1,#2,#4 – I lamented about our failings in #2 regarding managed debuggers (http://blogs.msdn.com/jmstall/archive/2004/10/02/236942.aspx ) and also elaborated on what we’re doing in v2.0 to fix it.

    #3 is a little more sketchy if you’re not native debugging. Some debuggers only debug a subset of the target, and thus would appear weaker by-design. This may be:

    – for security reasons to protect sensitive info in core parts of the app (such as passwords)

    – it may be to support partial-process debugging (eg, if you’re debugging just the scripting engine within a host)

    – it may be to support multi-user debugging a single proecss (to prevent users from debugging each other) (although there’s not a standard model for this in either managed or debugging APIs yet).

  2. > Don’t change the behavior of the target app.

    > This may not be always technically feasible,

    > but minimize the effect as much as humanly

    > possible.

    It is easy for a debugger to disable the IsDebuggerPresent() bit in the debuggee’s context, yet no debuggers (that I know of) do this by default. Do you suggest that this is something that debuggers should do?

  3. Uwe Keim says:

    Cool Website from Russ! Seems to be the "sysinternals for the programmer" :-)

  4. Craig Neth says:

    1.) Never, ever, ever crash. This is hard, but you have to do the work to make your stuff as bulletproof as possible. Having the debugger crash after hours of hard work getting close to a bug is enough to make you want to take up turnip farming.

    2.) It should never be hard to see the current registers, the current instruction stream, and a memory window. It’s amazing how many debuggers make this basic information hard/clumsy to obtain.

  5. Skywing says:

    I’ve ran into some really weird problems with #2. Actually, it’s a Windows API thing, but it’s related.

    You see, I had this weird bug where a particular function would mysteriously fail sometimes, but whenever I tried to attach a debugger the problem would never repro. Worse, even simply starting up something like DbgView would stop the problem from happening alltogether!

    The code went something like this…:

    SomeFunction();

    OutputDebugString("Some informative debug messagen");

    if(GetLastError() != ERROR_SUCCESS)

    Fail_This_Function_With_Error();

    After many hours of debugging, I finally discovered what was happening: When there was no debugger attached to the process, and nothing had created the DBWIN mutex, OutputDebugString would modify the thread’s last error state (overwriting it with a failure value) because it tried (and failed) to open the DBWIN mutex. If you attached a debugger, it didn’t try to do this, and if you started a program that created the DBWIN mutex (e.g. DbgView) then it would overwrite the last error state with a success status.

    What’s worse is that the particular code in question would not always run — it was a corner case that only happened if a particular I/O operation did not complete immediately, but pended.

    I refer to this as my debugger-proof bug.

  6. Mike Stall wrote:

    > it may be to support partial-process

    > debugging (eg, if you’re debugging just the

    > scripting engine within a host)

    Arrgghh!!! This is why Matts rules exist. I can’t tell you how many people have asked if BoundsChecker could tell them where they were leaking memory in their JavaScript of VBScript code. Unfortunately, we exhibit the opposite flaw; we see the underpinnings and don’t allow the user to see into the script code. So we can tell you exactly where the memory was being leaked from, but not where in your script. Supporting this was a goal of the BC 7.0 rewrite, but never got implemented due to limited time and resources. Sigh…