Debugging Run-Time Errors without a Debugger


For this example, we'll use a program that calculates the sum of the numbers entered to it. Download the program's source code sum2.cpp. Once it is downloaded, compile it...

% g++ -o total sum2.cpp

The program should compile without any errors or warnings.

Now, run the executable with some data:

% total
Enter numbers to sum below (separated by spaces).
Enter 0 (zero) as the last number.
5 6 7 0
Segmentation fault

You'll notice that the program produces a nice Segmentation fault. This calls for using debugging techniques!

Debugging run-time errors can be done by using 2 general techniques:

Technique 1:
Narrow down where in the program the error occurs.
Technique 2:
Get more information about what is happening in the program.

One way to implement both of these techniques is to use cout to provide information while the program is running.


Aside: Be careful, however, since cout output may not appear until endl is printed. In other words, if you don't see the output of a particular cout statement during run-time, you can't tell if the program performed that statement, unless endl was used. If you do use endl, and you don't see the output, you may conclude the program never got that far.

Alternatively, you can send output to cerr, whose output is printed immediately (cerr is where error messages are typically sent).


Technique 1: Narrowing Down Location of Error

We know we got as far as the first prompt ("Enter numbers..."), but not as far as the last output line:
cout << " is " << sum << endl;


Note: We're not sure if it did the statements:
cout << "The sum of ";
PrintValues(values, howmany);
Although both of those produce output, they don't use endl, so we can't tell if the crash occurred before or after them.

That information narrows down the crash some. To narrow down the location of the problem even more, let's add 2 cout statements. Bring up the code in an editor and add some debugging statements:

cout << "point A" << endl;
do {
  // Read the next number.
  cin >> values[howmany];

  // Increment the count.
  howmany++;  // Should error check "howmany"!

} while (values[howmany-1] != 0);

float sum = SumValues(values, howmany);

cout << "The sum of ";
PrintValues(values, howmany);
cout << "point B" << endl;

(Don't forget the endls.) After editing, save the source code and then recompile the program:

% g++ -o total sum2.cpp

Now, run the executable again:

% total
Enter numbers to sum below (separated by spaces).
Enter 0 (zero) as the last number.
point A
5 6 7 0
Segmentation fault

We see the point A output, but not the one for point B. This confirms our suspicion that the problem is between point A and point B.

Now, we can narrow down the range by moving one (or both) of the couts. Let's move B above the call to PrintValues():

cout << "point A" << endl;
do {
  // Read the next number.
  cin >> values[howmany];

  // Increment the count.
  howmany++;  // Should error check "howmany"!

} while (values[howmany-1] != 0);

float sum = SumValues(values, howmany);
cout << "point B" << endl;

cout << "The sum of ";
PrintValues(values, howmany);

Save this change, recompile, and run the executable again:

% g++ -o total sum2.cpp
% total
Enter numbers to sum below (separated by spaces).
Enter 0 (zero) as the last number.
point A
5 6 7 0
Segmentation fault

Again, point A gets printed, but not point B. So, we know that PrintValues() was not the culprit (else, we'd see point B before the Segmentation fault!)

Let's move B again...this time above the call to SumValues():

cout << "point A" << endl;
do {
  // Read the next number.
  cin >> values[howmany];

  // Increment the count.
  howmany++;  // Should error check "howmany"!

} while (values[howmany-1] != 0);
cout << "point B" << endl;

float sum = SumValues(values, howmany);

Save this change, recompile, and run the executable again:

% g++ -o total sum2.cpp
% total
Enter numbers to sum below (separated by spaces).
Enter 0 (zero) as the last number.
point A
5 6 7 0
Segmentation fault

Once more, point A gets printed, but not point B. So, we know that SumValues() was not the culprit. The problem must be somewhere in the loop (in main()).

Technique 2: More Information

There are only a few statements in the loop, plus the condition it tests. We remember that Segmentation faults can occurs with bad array indices, so let's now add some additional debugging output:
cout << "point A" << endl;
do {
  cout << "howmany = " << howmany << endl;
  // Read the next number.
  cin >> values[howmany];

  // Increment the count.
  howmany++;  // Should error check "howmany"!

} while (values[howmany-1] != 0);
cout << "point B" << endl;

Save this change, recompile, and run the executable again:

% g++ -o total sum2.cpp
% total
Enter numbers to sum below (separated by spaces).
Enter 0 (zero) as the last number.
point A
howmany = -276903488
...

Ugh! The index howmany should not be that!

What is the problem?

Go ahead and fix the error. Leave in the debugging output until the program works perfectly (it may still be useful). Save your changes, recompile the program, and run it again.

% g++ -o total sum2.cpp
% total
Enter numbers to sum below (separated by spaces).
Enter 0 (zero) as the last number.
point A
howmany = 0
5 6 7 0
howmany = 1
howmany = 2
howmany = 3
point B
The sum of 5 + 6 + 7 + 0 is 13

The Segmentation fault has been eliminated!! However, now we have a logic error, since the sum should be 18.

Again, we need more information! Suggest a plan for finding the error!!


BU CAS CS - Debugging Run-Time Errors without a Debugger
Copyright © 1993-2000 by Robert I. Pitts <rip at bu dot edu>. All Rights Reserved.