tags:

views:

1058

answers:

26

Possible Duplicate:
Debugging techniques

How can I improve my debugging skills? I am thinking in the context of C++ under UNIX, C#, and in general.

Please suggest how I can improve in these areas in terms of:

  • Approaches to take, where to start, and how to proceed.
  • Tools to use, and how use them effectively.
  • Recommended material (books, articles) to read and lectures to watch.
A: 

You will probably learn best from your own mistakes, which is quite a long term thing unfortunatly.

Ben Shelock
That's what I have been doing until now, but you can expedite the learning curve by using a well-known and general tips, good strategies, even from a reading a book... It is like chess, you can either play at the same level, without improving. Or you can learn strategy and analyze more experienced players' game, thus improving your game
+7  A: 

The most important technique for debugging is to not think you know more than you do. Nothing is unimportant until proven so. Nothing "is guaranteed to work."

Unit tests are probably the second most important thing. If you don't have unit tests, write one that consistently reproduces the bug (if you don't already have a suite of unit tests, this will probably be an integration test). Leave the test in your build process to catch any regressions.

After that, the "binary chop" is a pretty good technique: you set a breakpoint at two high-level points in the code, examine the state of the program, then move those breakpoints progressively closer together (optimally cutting the code in half) until you've found the problem.

kdgregory
i never thought about applying the bisection method on debugging. :)
wilhelmtell
I've done that on many occasions - using the emacs-gdb mode - works like a charm.
viksit
+1 for writing a test to reproduce the bug. Never put your self into a position where you have to begin debugging from scratch if you encounter the same bug.
Falaina
+2  A: 
  1. Practice - while you might not ever encounter a particular problem more than once, you will definitely encounter families of issues that will start to 'smell' the same.

  2. Learn to speed-read - the faster you can read your logging output, or the docs for your libraries, the faster you'll be able to understand what's actually going on.

DDaviesBrackett
+6  A: 

Usually what I do when I have no idea of what's going on about an error:

  • Check for syntax errors. Typically missing ; or other simple mistakes like this

  • Check for simple logic errors (should I be trying to access table[size] or table[size-1])

  • Check if the functions involved are getting called. This can be done by adding debug print messages or using a debugger. It depends on the language and the environment you have

  • Check algorithm logic. Try to browse throught everything like you were explaining to someone what's going on. It's actually better to really explain it to someone.

  • Get someone to take a quick look at your code. Maybe you missed a simple error in steps 1 and 2?

  • Double check that you're modifying the correct file

  • Double check your includes and if something out of your code could be breaking it

  • Check if it could be a data issue

  • Cry

  • Go on Stackoverflow

But this is just a big idea of ONE methodology that surely will not work in all of the cases.

The fact is that you will mostly learn from practice. Once you've seen a bug 10 times or spend a day fixing a parsing error, I can tell you that you'll remember it and it will take you less effort to fix it the next time you run into it.

It is also nice to have unit tests ready to run or get a more TDD approach

marcgg
+1 - for "Cry" __
Frank V
+10 for Go on Stackoverflow :)))))
+7  A: 

Some methods:

  • Reduce complexity. Try and isolate the issue. This way, you don't waste time on sections of the application that are known to work. The easiest way is to find junctions to rule out portions. Is the bug in the database layer or logic layer? Reduce.

  • Learn the debugger for your platform. Learn how to set up break points. Learn how to watch variables. Once you've reduced the bug to its most isolated state, and still can't figure out what's happening, then step through your code. Line by line, inspect the state of your variables and environment. Continue until you see what's wrong.

  • Get other eyes on it. Sometimes, when you're in the thick of it, vision gets myopic. You start missing things you would seen otherwise. Getting another pair of eyes on it means you can get a fresh perspective.

  • Write unit tests. If you're not using unit tests, you should. Every time you fix a bug -- or even BEFORE you fix a bug -- write a unit test that will fail if the bug is present. This way, you will know if there's ever a regression.

  • If at any time you feel helpless, and can't get someone else to look at it, stop debugging. Work on something else for a while. In my experience, the worst mindset to have when debugging is a morose one.

thedz
+1 for mention of the last point. I've done this lots of times. I've found that you can really solve a problem by forgetting about it and an answer will come to you while doing something else. Though, my answers usually come when I go to watch T.V. and see something on T.V. that gives me an idea and I go to try it out. :P
Zack
+1  A: 

I learned alot from some videos on MSDN regarding debugging but that was a while ago and I don't have the link anymore. However, here's one video I was able to find

MSDN Webcast: MSDN geekSpeak: Debugging With John Robbins (Level 200)

Look through MSDN some more and try and find more, I know they are out there.

There are some cool tools and macros out there, one that was mentioned would put a break point at the beginning of every method in a given .cs file. Any macro you find will probably be tied to a specific version of VS.net (or whatever you're using) so I'd need to know that info.

Allen
John also has a book: Debugging .Net 2.0 Applications from Microsoft Press.
Chris Brandsma
A: 

If you think of debbuging skills as a neurological / cognitive ability, it might help to give some perspective. That said, like sports, language or any other cognitive skill, the only way to improve is constant practice. My own debugging skills vastly improved after working on a project of atrocious spaghetti code for a year. Every problem that you could imagine was present. Through practice, I developed a keen intuition for finding and diagnosing problems.

As for a practical how here are a few recommendations:

  1. If you have the luxury of working on a clean project at work, why not volunteer on an open source project? Work on some of their more difficult bugs. I'm sure there are ample opportunities out there to hone your skills.
  2. Study the various debug patterns / tools: logging, debuggers, break-points and unit testing.
Elijah
+1 I have NEVER look at debugging as "If you think of debbuging skills as a neurological / cognitive ability"
+1  A: 

If you're using a modern, exciting, dynamic language, be careful of evaluating things during debugging sessions. Sometimes this executes your code in ways that have side effects, leaving you tearing your hair out because something "impossible" is happening.

Example in .NET - properties, which you can write your own get/set code for, are evaluated by the Variables window of the debugger. So the debugger executes your custom getter every time you step through a line of code.

Daniel Earwicker
+2  A: 

For C++, knowing some assembly language will help you with some tricky debugging problems. Matt Pietrek's 'Just Enough Assembly Language to Get By' is a good reference if you are on x86.

mattnewport
If I could vote twice on this, I would -- there is absolutely nothing I have had more use for in hard-core debugging sessions than the very brief knowledge of assembly language bestowed by that article.There's a part two also: http://www.microsoft.com/msj/0698/hood0698.aspxI think the big win here is understanding what code the compiler should generate for a given construct.
Kim Gräsman
+4  A: 

Zen.

It's probably an unpopular notion to promote on a site like SO that is filled with people who live and breathe by their rational faculties. But there are so many things in life that benefit by just looking at what is in front of you. No preconceptions.

Everyone has their own toolkit of debugging techniques that can be useful. But don't under-value a clear mind. The longest journeys into a forest of dead ends begins because you are sure you know where you are going and don't pay attention to what is in front of your face.

Duck
A: 

Read this Teach Yourself Programming in Ten Years

To solve problems you have to know how to program correctly. More importantly, learn from mistakes.

"Remember how only works is not enough, you have to know how does not work too."

Thomas Edison failed almost 2,000 times before he was able to make his incandescent light work. He always said that he was just one step closer to success for almost 2,000 times. He just studied and researched more each time and then applied what he had learned previous attempts.

[EDITED]
The ability to discover problems is a personal characteristic, is in the blood, because it requires talent for observation and analysis and much patience to run tests thousands of times.

lsalamon
+1  A: 

There are some excellent books on debugging. I learned a ton from this one: http://www.amazon.com/Science-Debugging-Yuan-Hsieh/dp/1932111182/ref=sr_1_36?ie=UTF8&s=books&qid=1248288729&sr=8-36#

HLGEM
+1 finally someone posted a book, after we have enough material in the answers, I will sum them up in the question.
A: 

One suggestion given to me as an undergrad is to keep track of every mistake I discover I've made, and then try not to make those mistakes again.

It might be discouraging to have a huge list of everything you've ever screwed up but imagine how good you'd be if you never made the same mistake twice?

theycallmemorty
+1  A: 

Being good at debugging is a skill that takes a lot of practice. If you avoid debugging, you'll never get any good at it. That said... producing well designed code makes for easier debugging, and is probably the best thing to start with:

  • Learn to write modular code. A good top level design will keep individual changes in isolated parts of your code that are easier to test.

  • To benefit from the above, you must TEST OFTEN. Don't add more than one or two things before you test it out (or run your unit tests, but they're no substitute for running it yourself)

  • Make sure you're using source control, so you can see what you've actually changed between versions. A lot of inexperienced developers end up changing stuff as they debug, without quite understanding it... and end up trashing all kinds of various bits of the application hunting a single bug. Once they 'fix' the bug, they leave behind a lot of other unrelated changes and debug code that (probably) breaks something else. So... once you fix something, compare your code to the last check in and clean it up!

  • Debugging is not wasting time. You're developing a skill like any other. The pains of debugging will slowly teach you better more defensive ways to design your apps in the first place.

darron
A: 

I can not express the importance of a good test unit framework and the will to write test cases before trying to do anything else in a not so simple program.

Milan
+1  A: 
Graphics Noob
A: 

Something else to avoid is thinking in absolutes. When you think to yourself "I know this is true" without verifying you're asking for trouble. The fact that you are debugging indicates that something is not as you expect it to be, so it pays to question everything.

This is amplified if you are debugging a system that has large external components such as an embedded system with a hardware interface, a system that interacts with a suite of modules, etc...

dls
A: 

Aside from what has already been said, find yourself a good logging library. For C# development, log4net is a good, solid framework (please google 'log4net' as I cannot post more than one hyperlink). For Java development, log4j is good (google 'log4j'), and an added bonus is that learning how to use log4net is transferable as log4j (or vice-versa), as log4net is just a .NET port of Java's log4j.

Once you have learned how, and more importantly when, to inject logging into your application, when you see a crash, you can just take a look at your log file/database table/console and gain great insight into the state of the application before it crashed, and possible be able to tell exactly why it crashed.

It may also be helpful to develop some Test Driven Design practices. If you can program your code while setting up tests along the way, you can prevent a lot of runtime errors and will have much less of a need to debug. A side effect of these practices is also much more decoupled code which will make the code more maintainable in the long run, and easier to understand while debugging.

Hope that helps!

Mike Gates
A: 

Andreas Zeller just updated his seminal book Why Programs Fail

A: 

One thing I do that I think helps alot is to keep notes of my analysis. I will keep a running log of what I've tried, what the result was and any conclusions. I put this information in any related bugs. This has helped alot when bugs that were fixed come back. Maybe I misunderstood the bug, or maybe it will point to a trend that needs to be addressed. When I'm stuck I reread the notes to see if I missed anything.

For tricky bugs, you aren't going to stumble across the bug right directly. Some good intermediate goals are to:

  • isolate the problem as much as possible
  • find/build a consistent repro
  • revert the code in source control until the problem goes away (usually I do divide and conquer rather across the range of changesets the regression may have occurred in)
Frank Schwieterman
+1  A: 

I have noticed that the performance difference between a good debugger (person, not software) or troubleshooter and a less effective debugger/troubleshooter is not say 10% but more like a factor of 10. I'm sure you can learn a lot just by reading the answers here at SO and also improve by training, but I believe that some people are gifted in a way that makes it easier for them to debug or troubleshoot just as some people have an easier time learning a foreign language or playing an instrument.

After this rather broad and somewhat subjective statement about debugging as an art I provide this advice about debugging C++:

Get some basic understanding about how the C++ compiler emits machine code for your platform. I have many times stepped through machine code and followed pointers through memory to finally discover the source of a particular hard to find bug in some C++ code. You can start by writing a small Hello World application and instruct the compiler to emit assembly code for you to study. Understanding concepts like the CPU stack, little and big endian, pointers, pointers to pointers and vtables can be useful.

Martin Liversage
+1  A: 

Here's a debugging war story for you on Linux with GDB.

There was this application which consumed 100% CPU right after startup, and forevermore. Classical infinite recursion, right? Well, for sure. But apart from taking 100% CPU, the rest of the classical telltale signs of infinite recursion were absent:

  1. No eventual crash due to stack space exhaustion
  2. No change in the backtrace when sampled at various points during runtime.

I finally sat down and single-stepped into the function that was consistently at the end of the backtrace. It was trivial, just calling the base class implementation and returning the value static_casted, like this:

SomeOtherDerived * Derived::foo() const {
    return static_cast<SomeOtherDerived*>( Base::foo() );
}

trivial... But when I did (single-step into the function) gdb would never return. It would hang there with 100% CPU, just like the application did without GDB attached...

I decided to take a good night's sleep over that one...

(if you want to venture a guess, stop reading here, spoiler below :)

When I came back next morning, I looked at the function body and the scales fell off my eyes. I had written

SomeOtherDerived * Derived::foo() const {
    return static_cast<SomeOtherDerived*>( Derived::foo() );
}

That explained it: the function is trivial; esp. there are no stack variables (not even hidden ones), so there's no stack space used for each invocation. In addition, there's not even a new stack frame per call, which is why gdb got stuck when single-stepping.

(Of course, Derived was named much more similarly to Base than in this example).

Bottomline: experience is trump :)

+1 thanks for sharing it with us
A: 

For C/C++ I usually work within the Visual Studio. With that said, I simply make use of the breakpoints and debug that way. It's extremely helpful to using the stepping process and watching variables.

In Java, I use the Eclipse IDE, and it's extremely helpful with debugging.

As for PHP, the best methods are the exit and die and the print_r functions. Simply die with a message or output of an array.

As for tips, the best ones I can give is:

  • Using the built in search function to find variables and where they go. Some IDE/Editors highlight variables used throughout the page. Ex. in notepad+ it highlights all occurrences of a variable in the file.
  • Use the highlighter for brackets. In many IDE/Editors, it highlights the starting and ending of brackets. This is extremely useful for messy if/switch statements and classes.
  • Make use of the debugger
  • Put in Exit and outputs
A: 

I agree with dz.

Reduce Complexity

In fact, if you need to debug legacy code, I recommend that you refactor the surrounding code to enhance readability and decrease complexity.

When you do this, you begin to understand the intent behind the code, and the logic flaw becomes more readily apparent.

  • After your debugging efforts are complete, you don't have to check in your refactorings.
  • Some, in the spirit of fixing broken windows, would argue that you absolutely should check-in these refactorings (with sufficient unit tests backing them up of course).
    • If time allows, then I also believe that you should check in these refactorings; but often it doesn't.
Jim G.
A: 

For anything that I can't find by reviewing the code, my main goal is to get the error to reproduce itself first in a consistent manner. This can be done by improving unit tests, hardcoding inputs, placing a breakpoint in a specific instruction and adjusting values, etc. I usually try to reproduce the error with as little human interaction as possible (partially because later I tend to do a divide-and-conquer run until I narrow down the issue, this may take more than one or two runs, so the less I have to manipulate each run, the faster I am at finding the problem). The other reason is that I'm a butter fingers, and sometimes hit "go" when I didn't mean to, so getting back to where I was quickly is important to me.

For the really nasty bugs, I will usually use the memory view, call stack, and watches on specific objects. Memory view is extremely helpful to me. I will often find the address of an object that I think is questionable (either at construction or close to the error) and keep it in memory view, often with the view expanded larger than the space the object actually occupies in memory. This helps me identify things like buffer overruns or if someone is modifying/using a copy of an object versus the real object or just the wrong reference in general.

Having a mental history of the type of things that have changed recently really helps when figuring out why something that used to work, doesn't.

FP
+2  A: 
  • Use a debugger. You have to get familiar with these common actions:

    • stepping (in, out, over)
    • setting break points
    • running/pausing
    • checking different threads execution stacks
    • how to bring up memory maps
    • how to look at variables
  • Practice! I debug code all day and I can usually determine what's going wrong in a split second...not because I read a book but because I practice figuring out problems all the time.

  • There are different techniques to solve problems when debugging. You usually do them naturally but a few are:

    • Divide and conquer - comment out code to see if it improves the situation, eliminate that piece as the problem or drill down if it is.
    • Stepping - step through the code looking at important variable values to understand the situation.
    • Breakpointing - set multiple break points to quickly get to important areas of code.
    • Memento - watch the memory mapping of a particular address to see how/why it is being modified
  • Use your brain!

  • Take a break. If you have been hammering at a problem for a while, take a break from it, laugh, take a walk, eat food! This usually helps you create a different take on the problem when you come back and allows you're brain to relax.

  • Take notes in a notebook of setup and problems/fixes encountered. This could help in the future if you encounter a problem again.

Chap