views:

603

answers:

15

Hi,

I know there are many popular and useful Design Patters.

Are there something like them for debugging scenarios? Maybe not patterns but methodologies which are categorized and that can be used repeatedly for similar cases.

Any idea?

thanks.

+3  A: 

When I am just shooting in the dark debugging I take the binary search approach. I comment out half of my code or half of a method, something along those lines, then I focus on the on the uncommented half. If the problem still exists, I comment out another half. And so on.

Bob
This sounds very context-specific, and won't help you at all when source modification is not an option (which is often the case).
Chris Cleeland
I am at a loss as to when I would be "debugging" when source modification is not an option. If I don't have the source, I can't debug it, although I can produce a nice bug report.
skiphoppy
often bugs occur in a context where source modification is not an option, because bugs occur in most often in real systems rather than in contrived test scenarios. one might be inclined to reply that "well, that means that you don't have sufficient unit tests", but unit tests cannot catch everything especially in complex interconnected systems.Deployed systems generally do not allow source modification, esp. as they scale larger and larger. Also, modifying source can change the nature of what you're debugging--printfs lead to differently locking patterns, more code = different layouts, etc
Chris Cleeland
also, I literally meant "source modification"; I still expect to have source code available for inspection, and also expect to have some means for gaining access to on-the-fly state information from the entity being debugged.
Chris Cleeland
This methodology isn't for contrived test scenerios, this can be used after you have identified a consistently reproducible bug in a development/debugging environment. You are not adding printfs and you are not adding more code, you are removing code to identify the smallest case where the bug still exists. It is easier to debug less code than it is to debug a lot of code. So yes, this is context specific, to debugging.
Bob
I see what you're saying, Chris. My approach is always to first try to replicate a bug on my system, where I do have source access. If that fails, I try to make my system as close as possible to the one failing: pull down their database, the exact same build of the software they have, perhaps even switch to their operating system, etc. But if that fails (and it has for me, sometimes), then I'm stuck debugging on a production system where I can't modify source! And you are right, in such a scenario, you can't use this debugging tip.
skiphoppy
+1  A: 

There are a few more formal patterns to eliminate specific bugs:

  • Eliminate Noise pattern
  • Re-occurring bug pattern
  • Time Specific bug pattern

However, I think most of your debugging decisions and workflow are going to be already designed by the environment you use.

marr75
+5  A: 

My approach is to use the scientific method:

  1. Gather Data on what is happening, try a lot of different inputs and see what outputs I'm getting
  2. Develop a hypothesis on what is going on
  3. Test said hypothesis, and I'm not right, then go back to step 1 and repeat.
GSto
+1 for the hypothesis method. For really tricky debugging situations I've used this approach. Sometimes coming up with several different hypotheses and then develop a test to exercise each, using process of elimination to find which gives the best explanation of what's going on.
the_mandrill
+4  A: 

If you have a piece of code that used to work, and now exhibits a bug, and a full version history, a binary search through your history can be very useful. You pick a point midway between the working and non-working commit, and you compile that and test. If that commit exhibits the bug, you know it started here or earlier, so you go back midway between here and the known good commit and test again; otherwise, you know the bug started later, so you you go forward midway between here and the known bad commit, and test there. You keep following this process until you find out which commit introduced the bug, and then you look at what changed, and there's a good chance the problem will be obvious.

git bisect is a spectacular tool for just this purpose. But you could theoretically do the same with a bunch of tarballs, if that's all you have.

Of course, this won't work if the bug has been in and out of the tree multiple times. And it probably won't be very helpful if your commits aren't very fine grained.

skiphoppy
+3  A: 

I like to think that Unit Testing is a debugging pattern. If you can reproduce the problem, you can write a unit test to make it a lot easier to debug.

Once you have the "top-level" unit test that you use to debug the problem, you can always create more failing unit tests at lower and lower levels in your code to help you focus in on the bug while adding unit tests that will be useful long-term in your project.

Mark
+8  A: 

I'll add one more debugging pattern that seems fairly obvious, but hasn't been said yet:

Reduce the bug to the smallest case possible, and then use that as your unit test for any proposed fix.

Broam
absolutely! the only problem I always run into is that there's a lot of debugging that has to occur in the systems I work with before I can get down to a "smallest case".There are also some situations where no matter how hard you try you just can't force the precipitating condition(s). This is especially prevalent in systems which respond to external stimuli such as network applications or real-time controls.
Chris Cleeland
+1  A: 

Fault isolation is one. Does the problem occur on all the OSes, or is it related to one OS only ?

Proceed by dichotomy to determine the location and the cause of the problem.

philippe
+3  A: 

The way I debug is the way I solve problems. I use the cartesian method.

There's is 4 rules :

  • To accept nothing as true that is not recognized by the reason as clear and distinct;
  • To analyze complex ideas by breaking them down into their simple constitutive elements, which reason can intuitively apprehend;
  • To reconstruct, beginning with simple ideas and working synthetically to the complex;
  • To make an accurate and complete enumeration of the data of the problem, using in this step both the methods of induction and deduction.

Or, say differently :

  • Accept as true only what is indubitable.
  • Divide every question into manageable parts.
  • Begin with the simplest issues and ascend to the more complex.
  • Review frequently enough to retain the whole argument at once.

You only have to adapt theses rules in the context of programming.

If I had to resume, I'd say take the problem/bug to it's simple expression. Do it iteratively.

Melursus
you do realise that the notion of 'clear and distinct' is anything but clear and distinct in Descartes philosophies?In fact one could say that its led to hundreds of years of arguments.
John Nicholas
I agree. But that's a debate about philosophy. Nothingness, I think that his method (let say the last 3 steps) is a very good way to solve problems
Melursus
A: 

Just a few I've come across:

  • When two identical systems are generating different results, verify that (in order of likelihood):
    • The versions of all components are, in fact, identical between the two systems,
    • The config is identical between the two systems, and
    • There is no residual data on one system that was not present on the other.
  • gdb and gcc parse code better than I do. Let software do its job so you can do yours.
  • When data comes out one end of a process different from what you're expecting, verify data all along the process to verify that it is as expected, rather than focusing on a function in the process down-stream from the real problem.
  • Do not focus on a specific piece of code if you haven't verified that it is the cause of the bug.
  • Get more sleep. Alert debugging is always more effective.

I've got more on my web site under Software Development -> Debugging if you're interested.

Robert Gowland
+7  A: 

I agree with the others about Unit Testing as a "Pattern" for preventing bugs. additionally I would like to quote the following steps from Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems:

  • 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

And last, on the more practical side, Dimitry Vostokov has gathered some very nice debugging patterns in his book and website.

Moshe Levi
If you didn't fix it, it ain't fixed, should be an answer all its own! If it miraculously got better, or you tried some voodoo or cargo cult change to the code without a hypothesis about what was wrong or a test to prove your hypothesis, you'll just keep debugging it again, maybe even for years.
skiphoppy
+6  A: 

Here are a few that work for me:

  • Step away from the problem. Similar to the "get more sleep", sometimes stepping away from the problem and focusing on something completely different (e.g., go work out) helps provide clarity when you resume work on the problem.
  • Explain the problem to my wife. Well, it doesn't have to be my wife specifically, but somebody who is not familiar with the problem, the system, or anything. This will force you to have to bring assumptions to the surface, explain how the system really works, perhaps even go back to the code to verify what you're saying. I've often had significant breakthroughs after this sort of exchange.
Chris Cleeland
The "explain the problem to my wife" approach has often been very helpful to me. Someone unfamiliar with the component or system, and often someone not even familiar with programming, will be a great audience who will force me to say things I otherwise wouldn't have said, and therefore look at things I might have missed.
skiphoppy
My team leader had the benefit from this approach many times in the past. Usually when there is something wrong that he could not figure out, he calls me next to him, starts to explain what he did on the code, and in the middle of his explanation he stops suddenly saying that "he is idiot!" or something like that and fixes the problem. I even do not need to talk. Concerning the "explain the problem to my wife" suggestion, I wonder if it works vice versa as well. When I have a problem with my wife, next time I should try the "explain to your colleague" pattern :)
burak ozdogan
Get more sleep, is a good thing as well. Actually it is something even the sports people do. The hard training atheletes are suggested to give at least 3-4 weeks off in a year in which term they even do not read, think or watch about the sports they do. This is called renaissance effect. you give up a problem -or move or technique- for some time. And when you are back to trying it once more, you see that you can do it much more easily.
burak ozdogan
+1  A: 

This is not really a debugging technique, but I think we have to mention a debugging precondition which, if not met, will greatly complicate your work.

You can't really start meaningful debugging until the bug is reproducible, with a step by step recipe. If you get a bad bug report, you may wind up having to discern that recipe yourself, but if you are supporting someone, you should let them know that you figuring out the recipe will take longer than them doing it for you and may even be impossible. A useful bug report has to answer the three questions of what I call the bug report formula: 1) what did you do? 2) what did you expect to happen? 3) what happened instead?

Of course, some bugs are Heisenbugs, apparently transient. You should still try to get something resembling a statement like "If I do the following, it looks like about 10% of the time this undesirable result happens."

Once you have the recipe, the next step is often boiling down to a minimal test case, as others have mentioned.

skiphoppy
Not only reproducible, but in a reasonable time span. I once had to track down a bug that *usually* only happened after about 45 minutes into a run of a CPU-intensive program. Eventually I did something (don't recall) to drop that to <5 minutes before I managed to get a handle on the cause of the bug.
Carter Galle
There is also the possibility that that bug might be reproduced in more than one way. I think this should be also taken into consideration.
burak ozdogan
Yes, if it's more than one way, then you have two conceptual bug reports, and two test cases. Both should be boiled down to a minimal test case. You can do one and then go all the way to fix it and see if the other is an issue, or you can minimize both first and then try to fix.
skiphoppy
A: 

Others (Xynth, Broam, Moshe) have mentioned the need to get a minimal test case, hopefully one which can be inserted into your unit test suite. I agree. Once you can make the bug happen, start paring away extra factors, or even code (as Bob suggested), testing at each step to see if the bug went away. If it's in cron, run it manually. If it's run from the GUI, try it from a command-line or a simple test suite.

If you are having trouble, the opposite approach is often useful: create a tiny, minimal program that does a couple of things the buggy routine does. Test it and see if you have the bug. Then, step by step, try to write a working program that does what the buggy routine is supposed to do. At some point, you may see the bug exhibited, in which case you now have your test case. Or, you may get all the way to a successful routine. In this case, start transforming that routine, line by line, into an exact replica of your code, testing along the way to see when the bug appears.

skiphoppy
A: 

Specializing this to OpenGL fragment shader debugging, which is very opaque. Dumbing stuff down is good and necessary, but checking the output is trickier than regular application programming, so you need some tricks:

  • Render different parts in r,g,b, using step() if you need to check precise value changes. (Good for detecting floating point errors such as 1.001 or -0.001)
  • Learn to interpret rgb as a normalized xyz vector.
  • Remove texture samples, plot texture coordinates as colors.
  • Consider rendering different variables in different parts of the screen and have a free-floating camera.
Marcus Lindblom
A: 

Thanks for all ideas. They were really helpful. As far as I understand there is not a certain list of patterns like the Design Patterns when it comes to fixing bugs. Maybe after some point everybody has his own way.

One of the approaches I use is, if the process looks complicated, to sit and try to see if I can do some re-factoring the code. This gives me a chance to refresh my mind on the code's behavior, to have better understanding and to make it maybe even more maintainable. Extract method is my favorite to have readable logical units.

One of my favorite thing is, if a value of a property is changed in an unexpected way, I put a break-point on the setter of that property. I do not know if it is possible to do it with implicit property decelerations (bool myProperty {get; set;}).

Maybe it would be better to start with having a BUG catalog showing all the types of bugs categorized with descriptions.

thanks, burak ozdogan

burak ozdogan