[plt-scheme] Re: some philosophical musings on debuggers
On Thu, 2004-09-16 at 14:35, Eric Kidd wrote:
> 3) Debuggers are essential when debugging somebody else's code,
> especially if there's a great steaming mass of it.
This and other points have been made in this thread:
http://www.artima.com/weblogs/viewpost.jsp?thread=23476
I extracted some of the more salient and/or entertaining comments.
My notes may be a little disorganized, but I have neither time
nor desire to clean them up. See attached.
Vadim
-------------- next part --------------
-*- outline-layout: (-1 : *) -*-
# For the above variable to be useful, put something like this in your
# .emacs:
# ;; http://www.emacswiki.org/cgi-bin/wiki.pl/AllOut
# (require 'allout)
# (outline-init "ask")
# Author: Vadim Nasardinov
# Since: 2003-12-02
# Version: $Id: debugging.txt,v 1.6 2004/09/17 17:36:47 vadim Exp $
* Intro
In his Nov 29, 2003 blog entry titled "Debuggers are a wasteful
timesink" (http://www.artima.com/weblogs/viewpost.jsp?thread=23476),
Robert C. Martin writes
...for all their power, debuggers have done more to damage
software development than help it.
...
Since I started using Test Driven Development in 1999, I have not
found a serious use for a debugger. The kinds of bugs I have to
troubleshoot are easily isolated by my unit tests, and can be
quickly found through inspection and a few judiciously placed
print statements.
In the ensuing discussion
(http://www.artima.com/forums/flat.jsp?forum=106&thread=23476),
two camps can be identified.
Those who agree with Uncle Bob tend to argue that TDD is a better
way to develop bug-free software.
Those who disagree point out that most programmers do maintenance
rather than from-scratch development. Given a large existing
system that needs a-fixin', a good debugger may often be the
fastest way to pinpoint the problem.
My impression is that those in the second camp understand the
first camp's argument, when properly scoped to where it actually
applies, i.e. during development. The first camp does not seem to
understand the counterargument. They don't seem to have come to
grips with the concept that 90% of programmers do maintenance of
stuff they haven't written, not development.
* Cons of using a debugger
.. Debugging sessions are transient.
On the other hand, code instrumentation with logging statements is
repeatable.
Possible fix: scriptable debuggers.
.. Debuggers encourage sloppy code.
Use case: you write a goodly chunk of code first, then try to get
it working by stepping through it in a debugger. Could be rather
inefficient.
Possible fix: don't do it.
* Pros of using a debugger
.. You can debug code for which you don't have the source,
e.g. third-party libraries.
* Quotes
.. Manish Jethani
http://www.artima.com/profile.jsp?user=15573
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=0&msRange=15
> Since I started using Test Driven Development in 1999, I have
> not found a serious use for a debugger.
I'm sure, because you're doing development, not debugging. I
hardly ever used a debugger, if at all, when I was writing my own
code.
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=30&msRange=15
You sound like the programmer who writes code, and I happen to be
the guy who has to debug your code 5 years later. Am I supposed to
"wish" you had written better code? Nopes, I have a job to do, so
I get on to just the right tool, the debugger, because someone out
there has a business (or a hospital, or a bank, ...) that depends
on the buggy software you wrote 5 years back.
Also, software outlives "good coding pratices". What was good a
few years back is considered bad taste now.
...
What you really mean is, "don't jump on to the debugger too early,
because I've myself wasted a lot of time doing that." Yes, one
needs to know how and--more importantly--when to use the debugger.
.. Ashley Herring
http://www.artima.com/profile.jsp?user=19579
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=45&msRange=15
* The term "Debugger" is a bit of a misnomer, because it is also
an interactive learning tool for exploring the way a program
works. Hence, more often than not I've fired up a program in a
debugger, not to debug, but to understand how it works. This is
particularly important when you inherit somebody else's
codebase!
* "You can understand code just by reading it". Bullshit. Quite
simply, utter bullshit. Why? Code describes the static structure
of a process. A debugger allows you to look at the dynamic
behavior of a process in action. No matter how smart you think
you are, the number of states a non-trivial program can be in is
vastly more than the mind can comprehend by "reading the
code". How often does a innocuous looking method turn out to
have a bug in it that only occurs as a result of an seemly
unexpected sequence of inputs? Looking at the code, you don't
even consider analysing how it would have behaved in that
situation, because it "will never happen" thinks the
developer. Then you fire up the debugger and surprise, surprise,
it *does and did happen*. Why? Because the developer is not the
user, and will frequently overlook common input
situations. Debuggers provide a way to understand what is
occurring in these unexpected, but commonly encountered
situations.
Keep in mind, a "bug" is rarely as obvious as finding something
like an incorrectly assigned reference - ie, something an
"experienced" developer will pick up through a code
review. Typically a program "bug" is not a code bug, but a
design bug that did not take into account a certain
scenario. The code looks fine, and may be 100% correct for the
purpose it was designed for, but the "bug" is that there is a
scenario that the design of the code never took into account.
* This above situation - programs behaving unexpectedly due to
input unexpected by the developer, but in reality a common input
in production/"the real world" - is why unit tests by themselves
are not the answer. Unit tests are written by developers to test
the *expected* behavior of their programs. Unit tests are a
great QA & development tool - they improve the quality of the
code, but they are not a 100% replacement for a debugger. Why?
Programs do crash, and when they do, it's not necessarily
because of bad design, or lack of unit testing, but because
something unexpected has occurred - something so unexpected,
there was no unit test for it.
* Unit tests are exactly that: a test on a self-contained
unit. Programs are made up of a multitude of interacting
units. The number of interactions in any non trivial system is
so large that for it is a practical impossibility to write unit
tests that consider every possible scenario.
* Programs crash, have bugs, run slow etc, not because a developer
has deliberately coded them to behave like that, but because the
actual way the program behaves does not match the conceptual
model the developer has of how the program should behave. A
debugger provides a way of verifying that your conceptual model
matches the actual behavior of the program.
Whilst I agree with the original poster that debugging can be a drug
and a lazy first stop analysis, I disagree that it's use should be
discouraged. If a problem occurs, I tend to analyse the code, form a
hypothesis of what is going wrong, then fire up the debugger to test
that hypothesis, *before* engaging in any extensive refactoring. The
idea of rewriting the "offending" code before running the original
code through a debugger is just ludicrous - what if what you think was
the "offending" code was actually not the real problem. Too bad, you
have just spend 4 wasted hours recoding the wrong code, when 5 minutes
with a debugger would have verified if you had correctly identifier
the problem.
.. Michael Feathers
http://www.artima.com/profile.jsp?user=15831
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=45&msRange=15
> Refactoring does not catch bugs. Refactored code is
> functionally identical to the code started with.
It is deeper than that. When you TDD you end up with incredibly
decoupled designs. It is one thing to talk about decoupling when
you design, yet another to end up with one. If you want to see
whether a non-TDDed design is decoupled try to write a test for an
arbitrary class. It is usually harder than you might imagine.
When you can get each class into a test harness, it is pretty easy
to check for bad behavior, you write tests to nail it down. Far
easier than working with an application that is a monolithic slab.
.. Greg
http://www.artima.com/profile.jsp?user=14640
. : http://www.artima.com/forums/flat.jsp?forum=106&thread=23476&start=60&msRange=15
In an application that uses libraries or is a client server app
talking to something that you didn't write inspecting your code
will not do any good. I've seen a lot of times when upgrades have
broken perfectly functional code because of changes in libraries
and api's that could only be identified with the use of a
debugger.
* Debugging Scandal (CACM, Apr 1997, vol. 40)
http://doi.acm.org/10.1145/248448.248455
http://web.media.mit.edu/~lieber/Lieberary/Softviz/CACM-Debugging/CACM-Debugging-Intro.html
Introduction to the Special Issue on the Debugging Scandal
.. War Stories
http://doi.acm.org/10.1145/248448.248456
http://web.media.mit.edu/~lieber/Lieberary/Softviz/CACM-Debugging/Hairiest.html
http://kmi.open.ac.uk/people/marc/papers/CACM-97-Hairiest-bug.doc
/My hairiest bug war stories/
Marc Eisenstadt, Open Univ., Milton Keynes, UK
ftp://kmi-ftp.open.ac.uk/pub/bugtales
http://www.adahome.com/articles/1997-05/bugtales/
http://www.adahome.com/articles/1997-05/am_bugs.html
"Ada Kills Hairy Bugs" by John W. McCormick
After several iterations of summarising the data, it became
apparent that it would be necessary to say something about (i) /why
a bug was hard to find/ (which might or might not be related to the
underlying cause), and (ii) /how it was found/ (which might or
might not be related to the underlying cause and the reason for the
difficulty) in addition to (iii) /the root cause/ (what really went
wrong)
Regarding techniques for bug finding (second dimension), Katz &
Anderson [5] reported a variety of bug-location strategies among
experienced Lisp subjects in a laboratory setting involving small
(10-line) programs. They distinguished among (i) strategies which
detected a heuristic mapping between a bug's manifestation and its
origin, (ii) those which relied on a hand simulation of execution,
and (iii) those which resorted to some kind of causal
reasoning. Goal-driven reasoning (either heuristic mapping or
causal reasoning) was predominant among subjects who were debugging
their own code, whereas data-driven reasoning (typically hand
simulation) was predominant among subjects who were debugging other
programmers' code.
An alternative characterisation of bugs was provided by Knuth's
analyses [6], which uncovered the following nine
(problem-independent) categories: A= algorithm awry; B= blunder or
botch; D= data structure debacle; F= forgotten function; L=
Language liability, i.e. misuse or misunderstanding of the
tools/language/hardware ("imperfectly knowing the tools"); M=
Mismatch between modules ("imperfectly knowing the specifications",
e.g. interface errors involving functions called with reversed
arguments); R= Reinforcement of robustness (e.g. handling erroneous
input); S= surprise scenario (bad bugs which forced design change,
unforeseen interactions); T= Trivial typo.
The informants reported four major bug-catching techniques, as
follows:
- gather data
o step & study
o wrap & profile
o print & peruse
o dump & diff
o conditional break & inspect
o specialist profile tool
- "inspeculation" (inspection, simulation, speculation)
- expert recognised cliche
- controlled experiments
.. Debugging and the Experience of Immediacy
David Ungar, Sun Microsystems
Henry Lieberman, Massachusetts Institute of Technology, Cambridge
Christopher Fry, PowerScout Corp., Boston, MA
http://doi.acm.org/10.1145/248448.248457
http://web.media.mit.edu/~lieber/Lieberary/Softviz/CACM-Debugging/Immediacy/Immediacy.html
The separation between cause and effect can occur in at least three
dimensions: time, space and semantics, thus designers can strive
for three kinds of immediacy that are important for debugging:
temporal immediacy, spatial immediacy and semantic immediacy.
Many C++ systems take minutes to change class definitions in a
large program, forcing the programmer to waste mental effort
devising workarounds, or leading to either boredom (leaving the
flow state) or distraction (reading e-mail, etc.).
.. Software visualization for debugging
Ron Baecker, et al.
http://doi.acm.org/10.1145/248448.248458
http://web.media.mit.edu/~lieber/Lieberary/Softviz/CACM-Debugging/SoftViz/SoftViz.html
http://kmdi.utoronto.ca/RMB/papers/p22.pdf
LogoMedia facilitates the visualization of programs with sounds as
well as images. Connected to a MIDI synthesizer, it can be used to
orchestrate acoustic feedback (Kramer, 1994) that informs
programmers of key control and data flow events. Auralization has
been used in software visualization (e.g., Brown and Hershberger,
1991) to augment, enhance, or replace graphical or textual
portrayals for several reasons:
* Links
.. Working With Legacy Code (a book in progress)
http://groups.yahoo.com/group/welc/
.. Linus doesn't like single-stepping
http://www.ussg.iu.edu/hypermail/linux/kernel/0009.0/1148.html
I don't like debuggers. Never have, probably never will. I use gdb
all the time, but I tend to use it not as a debugger, but as a
disassembler on steroids that you can program.
* Bibliography
.. Knuth, D. E. The Errors of TeX. Software-Practice and Experience,
19, 7 (1989), 607-685.