Are there any Debugging Patterns? [closed]
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 categoriz开发者_开发知识库ed and that can be used repeatedly for similar cases.
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.
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.
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.
My approach is to use the scientific method:
- Gather Data on what is happening, try a lot of different inputs and see what outputs I'm getting
- Develop a hypothesis on what is going on
- Test said hypothesis, and I'm not right, then go back to step 1 and repeat.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
Dmitry Vostokov has been cataloging many debugging patterns at http://www.dumpanalysis.org/
His writes about many of them in his blog http://www.dumpanalysis.org/blog/
While mostly dealing with debugging on Windows platforms, he has started to blog about gdb on Mac OS.
精彩评论