Reputation: 32966
What are the best general practice debugging tricks? Not the ones tied to a specific platform, language, etc., but the ones that you find you use regardless of what you're doing and with what system.
Upvotes: 2
Views: 3259
Reputation:
I'd argue that there's no one or two things that are the best, but rather a process that you build up over time through experience. I personally make sure to have unit tests, a great interactive debugger and print outs.
If I don't have a unit test for a bug I add a unit test that effectively nails down the bug and then trace through that. The process of putting the unit test together is exploratory and helps you to find the exact set of circumstances that cause the bug to occur.
Upvotes: 0
Reputation: 9725
Simple good old logging is the most powerful debugging technique ever. If a language supports conditional compilation then all the logging and assertions could be excluded from the release build. If it does not - you could still use a preprocessor externally.
Nicely structured logging and comprehensive assertions also serves the purpose of better self-documenting your code.
If you use your logging and assertions wisely, you won't ever need an interactive debugger.
Upvotes: 0
Reputation: 32966
I'll start by providing one I think is superb:
The hard to find bugs generally occur under circumstances that are relatively rare. After all, if the program re-boots your system every time you run it, you will usually fix that error. Some of these rare cases are caused by executing a code path that has never been executed before. When your program first starts, you will generally create a number of objects. You have a code path you take if those constructors throw exceptions, but have they ever failed?
If not, you have an accident waiting to happen. What does your program actually do when that particular constructor fails? As long as you have not tested a code path, you have not fully tested your program. Be sure that you exercise every code path. If you have an "if () ... else ..." and you have always executed the "else" part, then the "if" part is a bug waiting to happen.
There is a very simple way to handle this, and do so in a manner that will help you as you first run your code. The beauty of using traps is it helps you immediately, so that you will quickly find that you automatically put them in as you write code.
This is composed of two methods, trap() and trap(bool) – I have included Java, C++, & assembler examples at the, but for illustration I will use C# here. What a trap does is drop you into the debugger when you hit one. First the example, then why this is so useful
// Open an XML file
XmlReader reader;
if (string.IsNullOrEmpty(username)) {
Trap.Trap();
reader = XmlReader.Create(filename, xmlSettings);
} else {
Trap.Trap(!hasDomain);
Trap.Trap(hasDomain);
XmlUrlResolver resolver = new XmlZipResolver();
resolver.Credentials = hasDomain ? new NetworkCredential(user, password, domain) : new NetworkCredential(user, password);
xmlSettings.XmlResolver = resolver;
reader = XmlReader.Create(filename, xmlSettings);
}
The Immediate Pay-Off
Why is this a big help from the start? Because it helps you single step through each part of your code once. In the code above you probably test at first with no username so you'll fall in the debugger on the first trap. You mark it and then step over the Create. You may single step a couple of lines after to make sure the xml is good, then you go.
Note: The way I mark a trap I hit is to put ** at the start of that line. When I finish a debug session a simple compile will show me every place I added the *. Because I add the * to the same line, line numbers stay the same and the debugger matches your source line correctly (if you delete the line with the trap when you hit it the debugger will be off by one line anywhere further down in that file).
Ok, so you've been working on this module for a couple of days, it's all working well and you decide to try an XML file that requires credentials. When you do the debugger will stop at the top of the else. You then step through the code opening the XML file with credentials. The beauty of this approach is you don't need to remember that you haven't walked through these 4 lines of code. You don't need to go find those 4 lines of code. It just drops you there in the debugger. I think this is one of the most powerful debugging tools I use because I now easily single step through every line of my code – and find enough issues from this practice to make it well worth it.
The Secondary Pay-Off
This practice also provides a second great pay-off. All the traps removed initially are either very common code paths or paths specifically hit due to unit testing. So when you are "done" with a module and all your tests are written, any remaining traps are indicators of tests that remain to be written. You now have a very easy way to identify remaining unit tests that need to be written.
And the traps are implemented so they only run on a debug build. So you can ship code with traps in it (we shouldn't, but we all do) and it will have no impact on the release version.
Trap code for C#, Java, C++, & assembler available at One of the Most Powerful Debugging Practices
Upvotes: 2