Debuggers

C

Chris Dollin

Richard said:
Another thing about using a debugger is that its more "interactive". You
really do study the code more when you manually step line by line and
watch the locals pane change.

Yes, the few times I've had to do that tedious process in Eclipse are
part of why I don't feel much affection for debuggers.
 
C

Chris Dollin

Richard said:
You mentioned peer reviews.

Strictly, Jacob mentioned code review and I followed up (I'm not
distinguishing "code" from "peer" here); and I specifically said
"the calling procedures". It is /you/ who provided all the details
and then claimed that /I/ was proposing to review "every single
function", etc.

That's putting words into my mouth.
When looking for a bug in a foreign system then you would have to find
which function to "peer review" and then its calling function and then
.. etc.

Jacob said:

| 1: Consider this code:
|
| void dowork(char *buffer,size_t siz)
| {
| for (int i=0; i<siz;i++) {
| buffer=0;
| }
| }
|
| Not any amount of code review will catch the case where
| siz is wrong because the calling procedure had a memory
| overwrite. And standard C doesn't have ANY way of telling
| if a pointer is OK or not.

There's no mention here of post-hoc debugging a live system,
or how big it is, or how long it's been active. (I'll forgive
Jacob his "Not any amount of", because, well, Jacob.)
4 hours, and 12 years are all /your/ inventions. I presume you're not
actually trying to do this.

No. I never mentioned peer reviews. You did here:

,----
| > jacob navia wrote:
| >
| >> 1: Consider this code:
| >>
| >> void dowork(char *buffer,size_t siz)
| >> {
| >> for (int i=0; i<siz;i++) {
| >> buffer=0;
| >> }
| >> }
| >>
| >> Not any amount of code review will catch the case where
| >> siz is wrong because the calling procedure had a memory
| >> overwrite.
| >
| > Not even code review that reviews the calling procedures?
`----

Peer review is not done to debug released code as far as my experience
goes.


Jacob didn't say -- and I took him, reasonably I though, not to
be saying -- that this was "released code". He just claimed that
"any amount of" code review wouldn't catch a problem with this code.
Either one's response is, duh, if you only look at the place where
there's no bug you won't find the bug, or you wonder about looking
at the /rest/ of the code, or at least some of it.

[There's a book on static analysis for security I've acquired
recently but not started to read yet. Maybe this will be a
motivation bump ... although really at Easter I want to /rest/.
And play games. And sort out my filing. And finish my Who/Torchwood
DVDs. And mow the lawn. And fix the gate. And empty the garage.
And ... ARGH TOO MUCH RUN AWAY (fx:carrier)]
FWIW, I don't think we are poles apart here. You use a debugger but not
as much as I would.

For me, in C, it's been "rarely, and mostly to get a stacktrace".
For you, it seems to be "routinely". Is that fair?
 
C

Chris Dollin

Eric said:
jacob navia wrote:

What has that to do with debuggers? Nothing that I can see.

It supports Jacob's claim that there are unseen bugs in programs, ie,
bugs that were not spotted when the code was written (and, once hopes,
read at least once); a new tool found things that people missed.

[It found them without having to execute the program, and without
manual intervention too. /That's/ what I imagine replacing
debuggers with -- tools that spot problems in advance, using some
kind of abstract interpretation, powerful type systems, and test
cases / examples. I imagine Jacob would label such a thing "a
debugger", and he might have a case for that.]

--
"Well begun is half done." - Proverb
"Six impossible things before breakfast." /Alice in Wonderland/

Hewlett-Packard Limited Cain Road, Bracknell, registered no:
registered office: Berks RG12 1HN 690597 England
 
R

Richard Heathfield

Morris Dovey said:
jacob said:
jacob said:
1: Consider this code:

void dowork(char *buffer,size_t siz)
{
for (int i=0; i<siz;i++) {
buffer=0;
}
}


And yes, code review can make bugs surface!

REPLACE

int i,
by

size_t i

(!!!!!!!!!!!!!!)


ROFL (Good catch!)


Better catch: simply replace the whole thing with:

void dowork(char *buffer, size_t siz)
{
memset(buffer, 0, siz);
}
 
S

santosh

Richard said:
Morris Dovey said:
jacob said:
jacob navia wrote:
1: Consider this code:

void dowork(char *buffer,size_t siz)
{
for (int i=0; i<siz;i++) {
buffer=0;
}
}

And yes, code review can make bugs surface!

REPLACE

int i,
by

size_t i

(!!!!!!!!!!!!!!)


ROFL (Good catch!)


Better catch: simply replace the whole thing with:

void dowork(char *buffer, size_t siz)
{
memset(buffer, 0, siz);
}


Even:

memset(buffer, 0, siz);

would do. :)
 
J

jacob navia

santosh said:
Richard said:
Morris Dovey said:
jacob navia wrote:
jacob navia wrote:
1: Consider this code:

void dowork(char *buffer,size_t siz)
{
for (int i=0; i<siz;i++) {
buffer=0;
}
}
And yes, code review can make bugs surface!

REPLACE

int i,
by

size_t i

(!!!!!!!!!!!!!!)
ROFL (Good catch!)

Better catch: simply replace the whole thing with:

void dowork(char *buffer, size_t siz)
{
memset(buffer, 0, siz);
}


Even:

memset(buffer, 0, siz);

would do. :)



That was an EXAMPLE obviously. The basic problem is that you
can't control all the parameters validity
 
S

santosh

jacob said:
santosh said:
Richard Heathfield wrote:
jacob navia wrote:
1: Consider this code:

void dowork(char *buffer,size_t siz)
{
for (int i=0; i<siz;i++) {
buffer=0;
}
} [ ... ]
Better catch: simply replace the whole thing with:

void dowork(char *buffer, size_t siz)
{
memset(buffer, 0, siz);
}


Even:

memset(buffer, 0, siz);

would do. :)

That was an EXAMPLE obviously. The basic problem is that you
can't control all the parameters validity


You can't do this in C without some form of runtime interpreting, as far
as I can see.
 
I

Ian Collins

Chris said:
/I'm/ certainly not claiming "no interaction" -- although I would
certainly start by reading the code to get some idea of its shape
(depends how much over 2000 lines we're talking about, of course).


Nowadays I'd write test cases for that. Tweaking in the debugger
doesn't leave a reusable trail. (I could be wrong about that -- does
it?)
The test case is by for the better solution, it automates what was
previously done by stepping through code in a debugger. It also can be
run every time any change is made to the code base, minimising the risk
of regression.

The code I see these days fall into to categories, legacy code without
unit tests and recent code with the tests. My approach to fixing a bug
in the former is to bring it up to date with enough tests to understand
its working and safely refactor or change it without introducing new bugs.

Once code has tests, introduce a failing test to expose (and document
for audit) the bug, fix it and move on.

If the tests are done correctly, finding the bug tends to be relatively
simple through inspection. If it isn't and a debugger is required, I
see this as a sign the tests are inadequate and look to improve them.
 
R

Richard

Ian Collins said:
The test case is by for the better solution, it automates what was

If the test case exists. They often dont.
previously done by stepping through code in a debugger.

A test case equivalent was hardly ever done by stepping though in a
debugger. Unit testing or module testing was done in other ways.

But as usual, the criteria are being changed once more to suit the
"debuggers are not unnecessary" band.
 
I

Ian Collins

jacob said:
1: Consider this code:

void dowork(char *buffer,size_t siz)
{
for (int i=0; i<siz;i++) {
buffer=0;
}
}

Not any amount of code review will catch the case where
siz is wrong because the calling procedure had a memory
overwrite. And standard C doesn't have ANY way of telling
if a pointer is OK or not.

Given that this can be reduced to a call to memset, it becomes obvious
that the bug isn't in the code presented, but at the point where the
code is called.
3: Debugging is a different activity than code review/code reading
since it tries to catch the bugs when they ALREADY ARE INSIDE
the code base.

And here a debugger is essential.
Now that's the contentious part. It's pretty obvious that some people
prefer to use a debugger and others have different techniques. What
works for you, or me, may not work for someone else.

To say that a particular tool is essential simply isn't true. Helpful
maybe, but certainly not essential.

I've used debuggers (both hardware and software) for years, I even
kitted out teams with a particular tool set due to the strength of its
debugger. These days I rely on fine grained unit tests and try and
avoid the debugger where possible. If I have to use it, I reflect on
why and strive to improve my process.


As I've said here before, spend some time (at least a year) working in a
language without a debugger, you will very quickly adapt to life without it.
I remember when ANSI introduced prototypes into the C89/90 standard,
and HOW MANY BUGS WE CATCHED in the development team I worked at
that time. We just never knew about those bugs!!!

I had the same experience forcing a C project through an early C++ compiler!
 
C

CBFalconer

Richard said:
Morris Dovey said:
jacob said:
jacob navia wrote:

1: Consider this code:

void dowork(char *buffer,size_t siz) {
for (int i=0; i<siz;i++) {
buffer=0;
}
}

And yes, code review can make bugs surface!

REPLACE

int i,
by

size_t i


ROFL (Good catch!)


Better catch: simply replace the whole thing with:

void dowork(char *buffer, size_t siz) {
memset(buffer, 0, siz);
}


Best, I think, is:

#define dowork(/* char* */ b, /* size_t */ sz) memset(b, 0, sz)
 
C

CBFalconer

Chris said:
Eric said:
jacob navia wrote:

What has that to do with debuggers? Nothing that I can see.

It supports Jacob's claim that there are unseen bugs in programs, ie,
bugs that were not spotted when the code was written (and, once hopes,
read at least once); a new tool found things that people missed.

[It found them without having to execute the program, and without
manual intervention too. /That's/ what I imagine replacing
debuggers with -- tools that spot problems in advance, using some
kind of abstract interpretation, powerful type systems, and test
cases / examples. I imagine Jacob would label such a thing "a
debugger", and he might have a case for that.]

I would call them Pascal or Ada.
 
D

David Tiktin

We know from my last installments, that there are some people here
that go around telling "there is no stack in C", because the word
"stack" doesn't appear in the C standard.

Apparently, more or less the same people, start telling that
it is enough to read the code to be able to debug a program,
without using such low level tools like a debugger.

I chimed in on a recent thread to say that the C abstract machine
clearly describes a LIFO structure which is known in Computer Science
circles as a "stack," and that to claim "there is no stack in C"
without recognizing this is unhelpful. On the other hand, I rarely
use a debugger. So maybe I have a perspective on this that you would
find worth thinking about ;-)

As an aside, I thought about posting in the thread you mentioned, but
there seems to be so much *heat* on this topic that I thought better
of it. But now I see below that you are sort of calling *me* a liar
by proxy, so, *without heat*, I just want to say a few things.
Now, question for the people that keep a small part of common
sense here:

Is *reading* a program the *same* as *DEBUGGING* a program?

If you mean by "reading," "reading with understanding," then I'd
guess I'd have to answer "yes" to this. When I read code and can't
understand why something is done or why something is done the way it
is, I've oftern found a problematic section of code which contains
bugs. I basically execute the code mentally as I read it. If you
mean to ask whether you can debug a program just by reading the
source code, the answer is also (obviously) yes. I have often found
that an algorithm was incorrectly implemented, just by looking at the
source code of the implementation.
Are serious people here willing to believe this stories of people
debugging huge code bases without a debugger?

The code base I work on has on the order of 800,000 SLOC, which I
think is pretty "huge" for one person. And I repeat, I *rarely* use
a debugger. I'm not some super programmer, and I don't claim to be.
But I learn from my mistakes, and since I've made so many over the
years, I've learned a great deal! (And I try not to use an IDE
either!) Please don't call me a liar. Just read on.
I stay by my position:

A code base bigger than 1500 lines is no longer debuggable in an
abstract sense just by reading code. It needs a debugger.

Well, this isn't easy to address squarely. I said the code base I
work on has about 800,000 source lines of code, but I don't try to
debug it all at once! In fact, the way I program, I would say I try
not to address more than 1500 lines at a time. I modularize
*ruthlessly*, and 1500 lines is a pretty good target for the maximum
size of a module. So I keep the target code small. I also write
pretty thorough module tests so that my assurance of module
correctness can be pretty high, once it passes all the tests. I'm
also very careful about module coupling. I like to keep modules as
independent as possible so that they can be tested in isolation. And
I maintain pretty severe abstraction barriers between modules, so
that one module may depend on another, but never on the internal
details of another. So I have a large body of thoroughly tested code
which is built into libraries that can be used over and over again.
If there's a problem in a program that uses a linked list, I know
it's not inside my linked list module. It may be in the way the
linked list API is used, but not inside the module itself. This is
an enormous advantage in locating where in the code a bug is hiding.
I often just don't have far to look!

I could go on about my coding habits and the tricks I've picked up
over the years (defensive programming, anyone?), but the only real
point I'm making is that I *have* picked up a lot of tricks over the
years and I have a certain discipline I follow to compensate for
myown shortcomings. I know the common mistakes and take extra care
not to make them. Test twice, commit once. And I'm almost obsessive
in coding for clarity, above all else. I know I'm not smart enogh to
debug clever code. So I don't write clever code!

Again, I do all this, not because I am a super programmer, but
because I know I'm not. I know my own limitations, and I have
developed coping strategies. In particular, I have to keep things
simple so that I can understand them fully. I have a pretty keen
sense of when code is "too clever by half," and I avoid writing code
like that. KISS. DRY. Test, test, test. There are lots of things
you can do which keep any given debugging task to managable
proportions.
Note that we suppose a real-time, event-driven code execution,
where you can have faulty libraries, faulty interrupts servicing
programs, etc. ANYTHING.

I write lots of low level code including device drivers, networking
code including protocol stacks with state machines, GUI code (event
driven), multithreaded code, and code of all sorts for embedded
systems. I routinely write code in a portable subset of C which
compiles cleanly and runs correctly on Win32, Linux, Solaris and on
the open source RTOS called eCos, both user space and kernel code
(and yes, that includes writing ISRs). In fact, I think the variety
of platforms and environments I work in is one reason I don't use a
debugger very often. I would have to learn to use a half dozen of
them and then remember which is which and how to what in each. It's
just beyond me to master so many different, complex tools!

But here's the thing I've found about debugging in my own case. When
I don't reflexively reach for a tool when something "bad" happens
with my code, I can often figure out exactly what the problem is
without even looking at the code, if I really understand the code in
the first place. What did it do? What did I expect it to do? If
what it did was not what I expected, why would it do that? Oh... It
doesn't have to be code I wrote, but that certainly helps ;-) If
it's code someone else wrote, I have to start by trying to understand
it. And for me, that means *reading* the code, not stepping through
it in a debugger. When I was just learning to program, I found that
helpful. Now that I know how to read code, I just don't.

Now please be clear that I am not saying anything bad about
debuggers! I'm willing to accept that it's a limitation of some kind
on my part that I don't have the patience to use them. In the 3
cases I remember where my "bug" was caused by incorrect code
generation by a compiler, I was eventually only able to isolate the
problem with a debugger, in one case using SoftICE deep inside the
Windows NT kernel. So I'm not against debuggers! Extreme cases call
for extreme measures! Thank heavens for debuggers! When you *need*
one, you *really* need one! But because my first thought is not to
reach for a debugger, I rarely find it necessary to do so.

The standard disclaimers apply. Your mileage obviously varies.

Dave
 
J

jacob navia

Hi Mr Tirkin

First, thanks for your contribution. It was a very interesting read.

David said:
[snip]
Now, question for the people that keep a small part of common
sense here:

Is *reading* a program the *same* as *DEBUGGING* a program?

If you mean by "reading," "reading with understanding," then I'd
guess I'd have to answer "yes" to this. When I read code and can't
understand why something is done or why something is done the way it
is, I've oftern found a problematic section of code which contains
bugs. I basically execute the code mentally as I read it. If you
mean to ask whether you can debug a program just by reading the
source code, the answer is also (obviously) yes. I have often found
that an algorithm was incorrectly implemented, just by looking at the
source code of the implementation.

This is of course possible, I will never deny this. But, as you read
lines and lines of code, your attention suffers, and you *think* you
understand what the code is doing but you actually don't because you
failed to see that

if (smp < smpGoal)

should have been

if (smp <= smpGoal)

and you are doomed.

Maybe getting into a bit of psychology, we observe that the brain, with
ALL its power, can't do things that a lowly computer can easily do.

That is why BRAINS make computers in the first place!!!

Because they can do things that brains can't. And one of them is that
they have the capacity of doing things, and repeating them "ad nauseum"
without ever getting fed up with it.

Brains can't do that. They just CAN'T. It is not a matter of will, it
is a fact of the circuit (the brain's circuit). It gets tired easily
with repetitive tasks and just shuts down. This is a reflex that is
common to all circuits based on neural networks: they learn to ignore
repetitive things and concentrate into CHANGE.

You THINK you understand the code, but after 867 lines you MISS the
deadly detail that will kill you later on.

Specially if there is a lot of repetition and not much redundancy.

C is not designed to be read by normal people. You *can* read it of
course if you are a programmer, but it is missing all the normal
parts of language that makes a human language easier to read and
understand. C must be understood by the programmer of course ( at
least the one that writes it for the first time ) but it ALSO has
to be understood by the machine. This makes C repetitive, and hard
to swallow in big quantities.

Let's not make a fool of ourselves. I *know* that you can read a
lot of code, and I *do* read a lot of code. But I know that I can
pass 20 times over

strcmp(foo, "foo");

without noticing that it was Foo and not foo what should have been
written there.

The best proof of what I am saying is that (as you know) BUGS
exist. And they exist BECAUSE only BRAINS make mistakes and
introduce errors when building software.

What I am trying to point out is that automatic tools for following
the code when it is running are a VERY useful tool for understanding
foreign code.

Obviously you have to know how to read C, if not, even the best
debugger is useless. But I would NOT rely so much in the capacity
of the brain. It is just not *done* for that kind of task.

And after writing that, I take it away of course. There are
people that can compute the 12th root of a 25 digit number in
a few minutes or sometime less. I heard in the radio last month of a
championship being held somewhere, where all those people did
*amazing* feats.

But those people are rare exceptions. Let's face it: most of us
can't compute pow(66334445543,1.0/12.0) in our heads.

And I know that if I have to, I can do without any debugger, because
I know the code I am working with, its failure modes, what I have
changed lately, etc. But I know that if I have a debugger
it is better (and MUCH faster) to use it.

I stick to my limit, and I find a confirmation of this in your sentence:

> In fact, the way I program, I would say I try
> not to address more than 1500 lines at a time. I modularize
> *ruthlessly*, and 1500 lines is a pretty good target for the maximum
> size of a module.


That's the point I am making. 1500-2000 lines is the maximum
the brain can handle.

But your post is so interesting (at least for me) in that you bring
many good ideas about why modularity is necessary, and how to structure
the code to preventively avoid bugs. It is a very welcome contribution
in a discussion (and it is my fault too) that we should have in a
much more *relaxed* manner. I agree that I make to much polemic
sometimes.

Excuse me.
 
B

Bartc

And after writing that, I take it away of course. There are
people that can compute the 12th root of a 25 digit number in
a few minutes or sometime less. I heard in the radio last month of a
championship being held somewhere, where all those people did
*amazing* feats.

But those people are rare exceptions. Let's face it: most of us
can't compute pow(66334445543,1.0/12.0) in our heads.

So this wasn't you then:

"Frenchman calculates 13th root of 200-digit number"
http://www.theregister.co.uk/2007/12/11/mathlete_record/
 
R

Richard Heathfield

Richard said:
Well he put his head up over "mediocrity" - maybe it's a CLC regular's
cousin? I got suspicious it might be when I saw his name - Reeshar
Field sur Le Heath.

Well, since you've been out of the bin, you've been aggressive, hostile,
and ill-mannered. Aggressive is okay (although not as good as gentle).
Hostile is okay (although not as good as friendly). Ill-mannered, even, is
okay (although not as good as polite).

But now you're being Just Plain Puerile, and your "contributions" to this
debate (which, up until now, seem to have consisted almost entirely of
disbelief that anyone could perform the commonplace task of debugging
without always resorting to a debugger) have now descended into name
games.

If that is the best you can do when almost everyone around you is
presenting reasoned explanations of obvious points, then it seems my
earlier assessment of your value to this newsgroup - effectively zero -
was correct, and I was mistaken to let you out of the killfile.

That mistake is easily fixed.
 
C

Chris Dollin

CBFalconer said:
Chris Dollin wrote:
[It found them without having to execute the program, and without
manual intervention too. /That's/ what I imagine replacing
debuggers with -- tools that spot problems in advance, using some
kind of abstract interpretation, powerful type systems, and test
cases / examples. I imagine Jacob would label such a thing "a
debugger", and he might have a case for that.]

I would call them Pascal or Ada.

Neither Pascal nor Ada has a type system I'd call "powerful".
 
D

Dik T. Winter

> Once thing that some people seem to ignore is that MANY (or most) bugs
> do not actually crash the program. The program just gives erroneous
> results. There is no stack trace until you use a HW break point to catch
> the offending run time state.

But where to put the break? And what if the erroneous result occurs on
the 5000-th time only, but you do not know that?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,054
Latest member
LucyCarper

Latest Threads

Top