A bug is any discrepancy between
intended program behavior and the way it would actually work, under any feasible
circumstance. This definition is both subjective, because it involves human
intention, and hypothetical, in the sense that a bug can be there even if one of
those circumstances hasn't yet occurred.
Bugs can enter at any stage of the
software-development process. There can be discrepancies between the customer's
intentions and the written specifications, between the programmer's
understanding of the specifications and the customer's, or between the
programmer's intentions and the code he/she writes because of a misunderstanding
either features of the language or of modules produced by other programmers.
These bugs are present before the first line of the program has been entered
into the editor. Therefore, as a programmer, you should make sure that the
specifications make sense and that you understand them. Also, you should attempt
to understand your tools, both your programming language and the software
written by others that is involved in your project.
Finding your bug is a process of confirming many things you believe are true -- until you find one which is NOT true.
From information theory, we know that the
test that gives the most information is the one that cuts the possibilities in
half. Unfortunately, most students work from definitive hypotheses, i.e., ones
that say, ``The cause of the bug must be such and such.'' They perform tests
which, if the hypothesis is correct, fix the bug, but, if the hypothesis is
false, give no information. This is an inefficient search strategy. (Try to
imagine how you would find a particular number between 1 and 1000 by asking
questions of the form, ``Is it ...?'' A much more efficient approach would be
the standard binary search.)
More than half the bugs introduced by
professional programmers are introduced during the debugging process. Similarly,
students introduce many bugs and destroy the structure of their code in the
process of making changes associated with such testing their hypotheses. It is
important to use CVS carefully and frequently so that you can back out of any
changes done to test a particular hypothesis.
The binary search principle:
First try to confirm that everything is OK at about 1/2 of your program. If so, try at 3/4, if not, try at 1/4.
Creating a stack trace can sometimes be helpful to figure out how did you get to a certain point in the program:
java.lang.Exception e = new java.lang.Exception();
e.printStackTrace();
In class, we demonstrated debugging using Eclipse and we'll have a tutorial on how to use JUnit. You can download them, free, for Unix and WinNT, but you need a separate JDK, which you can download from Sun. All of this stuff is installed on all Owlnet machines, both those running WinNT and Unix (but you'll need to be running Solaris 8 to make Eclipse work). The there's also JBuilder on the Owlnet.
The example code we showed in class is in the comp314 home directory on Owlnet (/home/comp314). The packages that we used are below that (e.g., /home/comp314/comp314/util/Debug.java and /home/comp314/junit/framework/Assert.java hold the classes for comp314.util.Debug and junit.framework.Assert). You probably want to configure /home/comp314 to be in your classpath.
In class, we showed:
If you have a debugging print method, such as FrameBuffer.print(), you can run this any time using the trick above, and you then get to have a pretty view into the guts of your program.
Another useful technique, particularly when your data structures get more complex, is to build sanity checking methods into your classes. For example, if you're implementing a heap, you might write a sanity checker that verifies the heap property and either returns nothing or has an assertion failure. If you thought your program was misbehaving because the heap was broken, you could call the heap sanity checker from the debugger, using the above "Display" trick, to see if your heap is, in fact, a heap. You could also use this same method as part of a unit test that does a number of heap operations and calls the sanity checker between each of them.
Finally, while we didn't spend any time in class on it, you will find your programs easier to debug when you stick to standard practices for how you format your code. Even if you claim that you have some degerate coding style "burned in" to your fingers, it's often worth the effort to program in a "standard" fashion. This makes your code reusable by other people later on. Please go read Sun's Java Coding Conventions and do your best to follow them.
One last note: the Unix world has standardized that a "tab" character is eight spaces long, and that code should be indented by four spaces for each step. In the PC world, they often let a tab be four spaces long, which tends to make code look ugly if it was formatted for standard Unix conventions. In Comp314, we plan to follow the Unix conventions. This means you may need to reconfigure Eclipse. Under Workbench -> Preferences, open the Java -> Code Formatter option panel. Click the "Style" tab, then set the number of spaces representing a tab to "4" and uncheck the box for "Indentation is represented by a tab." Next, go to Java -> Editor option panel, select the "General" tab, and set the "Displayed tab width" to "8". This will get you standard Unix indentation and display that will let your files move back and forth with Emacs and to your graders.
Yes, Eclipse is complex. But, it's free, it's fast, and it does seem to work, which is more than I can say for NetBeans.
As programs become large, finding bugs and debugging become a time consuming job. Debugging is an art that can be learned and developed. However, it requires plenty of experience in writing and debugging programs. The structured, top down approach to writing programs we discussed in class is one valuable tool for producing quality, working programs. However, there is no substitute for extensive programming experience and the best way to gain programming experience is to write, test, and debug programs; write, test, and debug programs; write, test, and debug programs; etc. etc.
Certain debugging guidelines;