views:

344

answers:

11

What's your standard way of debugging a problem? This might seem like a pretty broad question with some of you replying 'It depends on the problem' but I think a lot of us debug by instinct and haven't actually tried wording our process. That's why we say 'it depends'.

I was sort of forced to word my process recently because a few developers and I were working an the same problem and we were debugging it in totally different ways. I wanted them to understand what I was trying to do and vice versa.

After some reflection I realized that my way of debugging is actually quite monotonous. I'll first try to be able to reliably replicate the problem (especially on my local machine). Then through a series of elimination (and this is where I think it's problem dependent) try to identify the problem.

The other guys were trying to do it in a totally different way.

So, just wondering what has been working for you guys out there? And what would you say your process is for debugging if you had to formalize it in words?

BTW, we still haven't found out our problem =)

+3  A: 

When I'm up against a bug that I can't get seem to figure out, I like to make a model of the problem. Make a copy of the section of problem code, and start removing features from it, one at a time. Run a unit test against the code after every removal. Through this process your will either remove the feature with the bug (and hence, locate the bug), or you will have isolated the bug down to a core piece of code that contains the essence of the problem. And once you figure out the essence of the problem, its a lot easier to fix.

Spike Williams
+5  A: 

My approach varies based on my familiarity with the system at hand. Typically I do something like:

  1. Replicate the failure, if at all possible.
  2. Examine the fail state to determine the immediate cause of the failure.
  3. If I'm familiar with the system, I may have a good guess about to root cause. If not, I start to mechanically trace the data back through the software while challenging basic assumptions made by the software.
  4. If the problem seems to have a consistent trigger, I may manually walk forward through the code with a debugger while challenging implicit assumptions that the code makes.

Tracing the root cause is, of course, where things can get hairy. This is where having a dump (or better, a live, broken process) can be truly invaluable.

I think that the key point in my debugging process is challenging pre-conceptions and assumptions. The number of times I've found a bug in that component that I or a colleague would swear is working fine is massive.

I've been told by my more intuitive friends and colleagues that I'm quite pedantic when they watch me debug or ask me to help them figure something out. :)

Greg D
+1 for challenging pre-conceptions and assumptions.
fung
+1  A: 

I'm also a big fan of using process of elimination. Ruling out variables tremendously simplifies the debugging task. It's often the very first thing that should to be done.

Another really effective technique is to roll back to your last working version if possible and try again. This can be extremely powerful because it gives you solid footing to proceed more carefully. A variation on this is to get the code to a point where it is working, with less functionality, than not working with more functionality.

Of course, it's very important to not just try things. This increases your despair because it never works. I'd rather make 50 runs to gather information about the bug rather take a wild swing and hope it works.

Jack BeNimble
+1  A: 

I normally start off by forming an hypothesis based on the information I have at hand. Once this is done, I work to prove it to be correct. If it proves to be wrong, I start off with a different hypothesis.

Most of the Multithreaded synchronization issues get solved very easily with this approach.

Also you need to have a good understanding of the debugger you are using and its features. I work on Windows applications and have found windbg to be extremely helpful in finding bugs.

Canopus
I find this only works for experienced developers. When the newbies try to do it it seems more like random shooting and hoping to hit one. I guess mainly because they lack the experience to start with the most plausible hypothesis. So really don't advice this for new hires.
fung
+2  A: 

Reducing the bug to its simplest form often leads to greater understanding of the issue as well adding the benefit of being able to involve others if necessary.

Setting up a quick reproduction scenario to allow for efficient use of your time to test any hypothosis you chose.

Creating tools to dump the environment quickly for comparisons.

Creating and reproducing the bug with logging turned onto the maximum level.

Examining the system logs for anything alarming.

Looking at file dates and timestamps to get a feeling if the problem could be a recent introduction.

Looking through the source repository for recent activity in the relevant modules.

Apply deductive reasoning and apply the Ockham's Razor principles.

Be willing to step back and take a break from the problem.

ojblass
The cruelest bugs are the ones that expose a fundamental design flaw. Those make me cry.
Greg D
A: 

I picked those on the web or some book which I can't recall (it may have been CodingHorror ...)

Debugging 101:

  • Reproduce
  • Progressively Narrow Scope
  • Avoid Debuggers
  • Change Only One Thing At a Time

Psychological Methods:

  • Wooden Indian
  • Don't Speculate
  • Don't be too Quick to Blame the Tools
  • Understand Both Problem and Solution
  • Take a Break
  • Consider Multiple Causes

Bug Prevention Methods:

  • Monitor Your Own Fault Injection Habits
  • Introduce Debugging Aids Early
  • Loose Coupling and Information Hiding
  • Write a Regression Test to Prevent Re occurrence

Technical Methods:

  • Inert Trace Statements
  • Consult the Log Files of Third Party Products
  • Search the web for the Stack Trace
  • Introduce Design By Contract
  • Wipe the Slate Clean
  • Intermittent Bugs
  • Explot Localility
  • Introduce Dummy Implementations and Subclasses
  • Recompile / Relink
  • Probe Boundary Conditions and Special Cases
  • Check Version Dependencies (third party)
  • Check Code that Has Changed Recently
  • Don't Trust the Error Message
  • Graphics Bugs
Gabi Davar
I actually find this answer quite useful. Don't understand why it was flagged -1.
Hexagon
A: 

My method of debugging is different, probably because I am still beginner.

When I encounter logical bug I seem to end up adding more variables to see which values go where and then I go and debug line by line in the piece of code that causing a problem.

Dmitris
A: 

Replicating the problem and generating a repeatable test data set is definitely the first and most important step to debugging.

If I can identify a repeatable bug, I'll typically try and isolate the components involved until I locate the problem. Frequently I'll spend a little time ruling out cases so I can state definitively: The problem is not in component X (or process Y, etc.).

Alex B
+2  A: 

Consider getting hold of the book "Debugging" by David J Agans. The subtitle is "The 9 Indispensable Rules for Finding Even the Most Elusiv Software and Hardware Problems". His list of debugging rules - available in a poster form at the web site (and there's a link for the book, too) is:

  • Understand the system
  • Make it fail
  • Quit thinking and look
  • Divide and conquer
  • Change one thing at a time
  • Keep an audit trail
  • Check the plug
  • Get a fresh view
  • If you didn't fix it, it ain't fixed

The last point is particularly relevant in the software industry.

Jonathan Leffler
A: 

First I try to replicate the error, without being able to replicate the error it is basically impossible in a non-trivial program to guess the problem.

Then if possible, break out the code in a separate standalone project. There are several reasons for this: If the original project is big it quite difficult to debug second it eliminates or highlights any assumptions about the code.

I normally always have another copy of VS open which I use for the debugging parts in mini projects and to test routines which I later add to the main project.

Once having reproduced the error in the separate module the battle is almost won.

Sometimes it is not easy to break out a piece of code so in those cases I use different methods depending on how complex the issue is. In most cases assumptions about data seem to come and bite me so I try to add lots of asserts in the code in order make sure my assumptions are correct. I also disabling code by using #ifdef until the error disappears. Eliminating dependencies to other modules etc... sort of slowly circling in the bug like a vulture ..

I think I don't have really a conscious way of doing it, it varies quite a lot but the general principle is to eliminate the noise around the issue until it is quite obvious what it is. Hope I didn't sound too confusing :)

Anders K.
A: 

I find the best time to "debug" is while you're writing the code. In other words, be defensive. Check return values, liberally use assert, use some kind of reliable logging mechanism and log everything.

To more directly answer the question, the most efficient way for me to debug problems is to read code. Having a log helps you find the relevant code to read quickly. No logging? Spend the time putting it in. It may not seem like you're finding the bug, and you may not be. The logging might help you find another bug though, and eventually once you've gone through enough code, you'll find it....faster than setting up debuggers and trying to reproduce the problem, single stepping, etc.

While debugging I try to think of what the possible problems could be. I've come up with a fairly arbitrary classification system, but it works for me: all bugs fall into one of four categories. Keep in mind here that I'm talking about runtime problems, not compiler or linker errors. The four categories are:

  • dynamic memory allocation
  • stack overflow
  • uninitialized variable
  • logic bug

These categories have been most useful to me with C and C++, but I expect they apply pretty well elsewhere. The logic bug category is a big one (e.g. putting a < b when the correct thing was a <= b), and can include things like failing to synchronize access among threads.

Knowing what I'm looking for (one of these four things) helps a lot in finding it. Finding bugs always seems to be much harder than fixing them.

The actual mechanics for debugging are most often:

  1. do I have an automated test that demonstrates the problem?
    • if not, add a test that fails
  2. change the code so the test passes
  3. make sure all the other tests still pass
  4. check in the change

No automated testing in your environment? No time like the present to set it up. Too hard to organize things so you can test individual pieces of your program? Take the time to make it so. May make it take "too long" to fix this particular bug, but the sooner you start, the faster everything else'll go. Again, you might not fix the particular bug you're looking for but I bet you find and fix others along the way.

dbyron