tags:

views:

451

answers:

19

In chapter 5 of "The Practice of Programming" Brian Kernighan and Rob Pike write:

As a personal choice, we tend not to use debuggers beyond getting a stack trace or the value of a variable or two.

Although I have no empirical data on the topic, I suspect that it's likely that many programmers "live" in a debugger when they have one available in their environment. But I also suspect that there are many highly productive programmers who, like Kernighan and Pike, avoid debuggers as a first-line-of-defense and rely on other techniques.

So, I'd like to ask the stackoverflow community:

If you are the sort of programmer who sees the tools called "debuggers" as more of a last resort (other than getting an initial stack trace), what are your reasons for using other techniques first?

One (1) reason per answer for easier voting please!

I'll also suggest this rule for answering: "I don't know how to use the debugger" is not a valid answer. That's just ignorance. You should understand your alternatives before making a choice!

+6  A: 

I'd prefer to use TDD and add a test that breaks the code. Then it's easy to see where the bug is occurring and to fix it without a debugger and now I've got a test that will prevent that bug.

Different tools for different jobs. Just like you wouldn't use Perl for everything, you're not going to use a Debugger for every bug. Sometimes using a debugger fits a problem and sometimes it doesn't.

Take for example the bug that turned up in one of our products. It was pulling the last window to have focus back to focus after a print method. It couldn't be repo'd when the debugger was attached, but could when it was. This problem eventually was solved with good old fashioned Console.Write() statements.

Gavin Miller
+1  A: 

For the average little silly bug, it's faster for me to read the code over 2 or 3 times, debugging in my mind than to fire up the debugger and get the program into the required state.

Ben S
+1  A: 

I prefer manualy tracing the code and observing logs to debugger. Debugger turns me into a passive observer and makes harder to view the whole picture.

Program execution has at least one timeline, usually more than one in multithreaded environment. Having an idea what's going on few steps before and what can happen after the current line of execution helps me tracking the bug.

Boris Pavlović
+1  A: 

Some bugs may show themselves only in release mode therefore using a debugger may not be possible. In those situations, i often use printf :).

erelender
similarly printf might also interfere with the bug and might cause it not top happen anymore. (for instance if the bug was timing related)
Toad
printf bad! use log4net or the like.
Lucas B
See also "Separate ‘debug’ and ‘release’ builds?" at http://stackoverflow.com/questions/420343
ChrisW
@reinier: True but there is nothing to do about that...@Lucas B: On the contrary, i like printf very much, something left from my student days.
erelender
@erelender : thing is with Printf you have to remove them once you are done, whereas with the logging library you leave the statements in and turn them on at the flick of a switch. So when you are done debugging and send it off to the client and it manages to fail again you can get the same logs very easily. if you miss the syntax you can wrap the library with sprintf or a formatter into something like log.infof().... get the best of both worlds...
Newtopian
+1  A: 

These guys come from the unix era where full fledged IDE's were not at hand. Thats why adding printf is much faster than starting up GDB.

Nowadays setting a breakpoint in visual studio is the fastest way to debug, so everyone uses that

On different platforms like embedded devices, having printfs to a log file or something similar is still the fastest option

Toad
+5  A: 

The reason to use debugger as a last resort?

Because ,usually, I can find the bug much faster by using other techniques than using a debugger.

pierr
A: 

Our software runs on Linux/Solaris/HPUX/AIX/FreeBSD/MacOS, and it seems very hard to get debuggers for some of those platforms, and much easier to just add some extra tracing code.

Douglas Leeder
A: 

When I have a reproducable scenario I definitly use the debugger to track down that bug. Otherwise it is basically used only to get a stack trace from a core dump.

Traces are the basis to start to work on an issue

NB Embedded application in vehicle.

Val
+6  A: 

I think Kernighan was trying to make a point about mindful analysis. Don't dive in amonst the trees without understanding the forest. That said, there are other reasons to prefer different tools than the debugger, as aides to your mind.

My favorite (if that's the right word) example, is memory bugs. In a language like C or C++, misuse of the memory system (double deletes, accessing objects through dead pointers, running off the end of an array) can corrupt the program in such a way that the problem does not manifest itself anywhere near the original cause.

The appropriate approach in these languages is to use practices which eliminate those errors, but failing that, tools are also valuable. When my experience with similar bugs leads me to suspect "this feels like a memory error", I reach for valgrind, before I ever think "debugger".

Now we can begin the argument that automatic memory tools and bounds-checking libraries are debuggers! ;^)~

Don Wakefield
+1  A: 

Two reasons for not using a debugger include:

  • There isn't a debugger installed on the customer machine where the fault is happening
  • You can't afford to stop the (live) process (on the customer machine) to inspect it using a debugger

I don't know if I'm "that kind of programmer" but I don't see what you'd want of a debugger beyond:

  • Break at breakpoint and/or break when an exception is thrown
  • Inspect data and inspect the callstack during a break

I've heard some people suggest that you step through code with a debugger when you write it, as a kind of code inspection, but I don't do that.

ChrisW
+1  A: 

Having to go to the debugger is a likely sign of a deeper problem in my view.

Before firing up the debugger, I ask questions that target deeper problems like:

  • Which test failed? If the answer is, "there is no test," then not having a test for the failure condition is a deeper problem to be fixed first.

  • What information does the exception have? (This assumes an environment with exceptions). If the answer is, "there is no exception," or the exception doesn't have much context, then scanning for places where an exception is swallowed, or adding more context to an exception is a deeper problem to be fixed first.

  • What warnings are produced by the build for that section of the system? If you're not building and analyzing your system with modern tools to find common mistakes, and correcting them before they show up at runtime, then you've got a deeper problem to be fixed first.

  • Do we understand the problem domain enough to reason about what might be happening? If the answer is, "no we're not clear on this," then discussions with subject matter experts who can make the purpose of a piece of the system more clear is in order. Without clearly understood requirements, more bugs will likely come.

Doing these sorts of things usually leads to at least one bug fix, if not several. And these approaches have the highly valuable side-effect of, well, forcing programmers to think about the problem, the whole problem, not just the line of code "where the error occurred."

Certainly there are cases where an error occurs on a single line like not checking for a null pointer/reference, etc. but aren't those "simple" errors the very types of errors that modern IDEs and tools help to eliminate? Just run a lint/static-analysis tool and heed the warnings - you won't get those types of errors anymore. Then you're left with things like design errors that require the reasoning of a human mind - how can a debugger figure that out?

Greg Mattes
+1  A: 

I absolutely use my debugger. When a test I wrote is failing, I often step through the lines, checking my expectations against the actual values shown in the code.

That being said, after years of programming experience, you then to NEED the debugger less and less as you are more able to just "know" why the problem manifested.

Two thing that really make the debugger userfil are: conditional breakpoints and the object inspector. They are by far the most useful debugger features for me.

John Gietzen
+10  A: 

Not using the debugger to F10/F11 through your code can make you a better developer.

During my 1st job I had done a lot of programming in Linux where Visual Studio debugger was not available. Because debugging was hard I've learnt how to analyse my code and really understand how it works. And I became a better developer because of that.

Nowadays I do use the debugger only after I've gone over the code and searched for the "usual suspects" and if I do not understand how my code works without debugging it then I refactor it.

Dror Helper
This is a great example of how avoiding the automated tool forces you to think a bit more. It sounds as though you no longer use the the debugger as a crutch - good for you!
Greg Mattes
+2  A: 

Print statements tend to produce output that you can look at and reason about for quite some time, whereas when using a debugger you are forced to memorize the past, know exactly what is going on at present, and exactly what should happen in the future.

For example, you have an algorithm that changes a variable 100 times. On the 85th time, it got changed to a value that should never have been assigned to it in that case. With a debugger, you are forced to step through that 85 times. Conditional breakpoints may alleviate some of this.

Shin
What about a loop that is called 4,000 times? Printing 4,000 lines is not helpful. The conditional break is much better. If your debugger does not support conditional break, insert an if inside the loop and break there.
dss539
same argument is also good for print lines, but what if the statement failed only once in a while... do you want still want to conditionally break or read through log file ? Personally I much prefer let the thing run, go for a beer, come back, ctrl-f for the log trace and start thinking than sitting in front of it waiting for it to break.
Newtopian
@dss539: use grep, or even write a small script to find the condition you want. Ultimately analysis of a static data set is easier for me.
Shin
+3  A: 

Print statements that get dumped to a file can be operated on textually. grep, sort, sed, and awk can help for complex debugging issues. Also one could write a small program to parse the dumped text if necessary.

Shin
Good point. Having an execution trace can be quite helpful.
dss539
+1  A: 

I rarely use a debugger simply because we do not have one that we can attach to a live system. We can load an image and use the debugger to calculate offsets inside of structures, or translate a program counter to a file & line number. But in general, we code defensively, log errors, and keep a lot of statistics so we have a chance of diagnosing a failure post-hoc.

Having a working debugger, or an emulation environment, would have occasionally saved me days or weeks of twiddle, reproduce, diagnose.

+1  A: 

It depends on the situation for me. Using Visual Studio I tend to use the debugger far more than I do when programming in any other language, or OS. I almost never use it when I program in Linux. It is usually just faster to read over the bit that screwed up and analyze it in my head. I only ever fire up a debugger when it is some kind of strange pointer array related issue (ie: the XXth entry in a pointer array is wrong for some reason) that I can't see at first glance.

That said, in Visual Studio, unless the bug is very obvious, I find that it is just as fast to throw in a breakpoint, and try what I was doing again to see what's happening.

tl;dr: my reason for using a debugger as a last resort, is speed.

David Dietrich
+1  A: 

My favorite feature of the Visual Studio Debugger is the Immediate Window. I'm not sure if this is provided in many other environments, but it is damn helpful sometimes.

A few examples of how:

  • Data coming back from a database isn't casting correctly. Fire up the debugger and try a few casts until you get the right one (oops the int was short or the bool was a byte, etc),

  • Web apps with nested controls (e.g. TemplateFields in a GridView)...I have a reference to a specific control but want to get the grid row that I'm in (or viceversa). I can code a few nested FindControls() and hope for the best, or I can just do it in the immediate window until I find the control that I want.

Every project I've worked on so far (only 1-2 years corporate experience), I've used the debugger/Immediate Window at least once or twice. It may just speak to my inexperience, but I find it's extremely helpful for getting a good understanding of what is happening in a complex system.

IronicMuffin
+1  A: 

I use a debugger when :

  • I'm almost certain it is something stupid that I missed somewhere (variable not initialized, test conditions inverted or some other smack in the forehead god i'm stupid kinna stuff)
  • The test still fails and I just cannot figure out why
  • It is hard to track logs because of frequent context jumps (listeners, callbacks etc) and the loggers are not contextualized (named after the class and not after the context)

In all it is a balance between speed and accuracy. However from experience if I end up spending a lot of time around a piece of code there is a good chance I will have to come back to it, so I add logs and I add tests so I do not have to come back to it, or it I do all the work I have done to understand the code remains and I can build on top.

One reason I do not like debuggers is that all the work I do figuring out how it works is wasted once the debugger is off. If I spend the time learning about a piece of code I want this knowledge to be available the next time I (or someone else) get to it. Adding trace code is a very good way to have "Dynamic comments" that is always there and can be summoned anytime.

At large... pretty much anything that is removed before shipping to the customer I shy away from. If I put a safety net around my system there is no reason my customer cannot benefit from it while using it as I did while programming it. This is especially true if I am the one that has to support it afterward... I hate supporting so I want to make it as painless as humanly possible.

Newtopian