Anda di halaman 1dari 15

1

THE ART OF DEBUGGING


BASIC DEBUGGING PRINCIPLES
IN THE CONTEXT OF JAVA

Table of Contents

Introduction 2

Terminology 3
Breakpoints 3
Debugging Mode Overview 4
Debugging Flow Controls 5
The Call Stack 6
Viewing/Editing Variable Values 7

FAQs 8

A Small Example with Bubble Sort 9




2

Introduction
(SKIP if you already know what debugging is)

So you spend hours working on a coding project or assignment and finally are ready to see
your program in action! You compile your code check! No compilation errors great! Then
you run your code and, alassomething has gone awry. The program works but it doesnt
quite get the job done!

Often times a programmer is faced with such a scenario many times throughout the
development process. Semantic (logic) errors are much more subtle and difficult to locate
compared to flagrant syntax errors that even the compiler can spot. So, besides merely
staring at every line of code brute-force in a linear fashion, is there a better, perhaps more
systematic approach to locating these slippery, semantic errors? Yes!

Debugging is a methodical process of finding and reducing the number of bugs, or defects, in
a computer program or a piece of electronic hardware, thus making it behave as expected
(Wikipedia). Simply put, debugging code is a process that can be carried out in several ways
the most basic of which is to insert print statements into the code wherever problems are
suspected to originate to test values and behavior at that particular point in the program. But
this can get messy, lead to false conclusions, or even introduce new problems! We can do
better!

F U N F A C T
Now, while the term debugging originates from back in the 1940s when computer
scientists like Admiral Grace Hopper had to actually and literally de-bug, or remove a moth
intrusion from early room-sized computers, debugging today can be accomplished much
more efficientlyand cleanly!

This debugging tutorial will introduce the concept of software debugging in the context of the
Java programming language and the included debugging software of two popular Java IDEs
Eclipse and jGRASP. This guide will also define some basic and commonly shared debugging
terminology that is applicable to any software development situation. It is important to note,
however, that debugging can be done in any programming language and is usually particular
to the nuances of whatever IDE is being usedthat is, every IDE will have its own debugging
environment, but most of these will share several similarities.

Alrightnow lets get started! Fire up Eclipse, jGRASP, or your favorite IDE and follow along!

Note: this guide is written in the context of Java and the mentioned IDEs, so if you the reader
are currently working in a different environment, you are on your own in regards to figuring
out the specifics of your IDEs debugging features. However, the majority of the concepts
remain the same across platformsso it shouldnt be too difficult to figure out independently!

3

Terminology
(for a quick run-down if you just want the specific how-tos

So once you have your IDE running, your home screen should look something like this:

Eclipse jGRASP


The red box indicates the debug button which takes you into the debugging environment.
The blue box indicates a breakpoint.

Before entering the debugging mode, youll need some source code to work withhere is the
code used in the above screens:

public class Fibonacci
{
public static int fibonacci(int n)
{
if (n == 1 || n == 0)
return 1;
return fibonacci(n-1) + fibonacci(n-2);
}

public static void main(String[] args)
{
for (int i=0; i < 10; i++)
System.out.print(fibonacci(i) + " ");
}
}

You should recognize this as an algorithm for producing Fibonacci numbers!
More info: http://en.wikipedia.org/wiki/Fibonacci_number

Once you have the source code loaded, youll need to set whats called a breakpoint before
debugging can begin. Do this by double-clicking (or single-clicking) on the side bar to the left of
the text editor on the desired line to place a breakpoint (as indicated by the blue boxes in the
above images).


4

A breakpoint tells the debugger to pause execution of code at a specified line. This intentional
pause should be strategically placed in your code where trouble seems to be happening. Any
number of breakpoints may be set anywhere! At a breakpoint, you can check current variable
values, see the call stack, and even change data in memory before proceeding.

This can prove very helpful to see the status of your program in the middle of its execution as
opposed to trying to decipher what happened after it executes entirely (without the aid of a
debugger). Here, a breakpoint is just set on the first line of the main method for demonstrative
purposes. We can now trace the execution line-by-line by entering the debug mode. Click on
the debug button (indicated by the red boxes in the above images).

Your screen should now look something like this:



Welcome to the debugging mode! This is the command center in which you have complete
control over the execution of your code. Note how the line where the breakpoint was set is
now highlighted, indicating that the debugger has paused execution at this spoti.e. this line of
code is yet to be executed. We are now free to continue execution at whatever pace we
chooseline by line, by complete methods, or even by setting new breakpoints to jump to!

The yellow box indicates the debugging flow controls.
The red box indicates the call stack.
The blue box indicates the current values of variables existing in memory for the program at
this point. They can be manually changed!


5

A closer look at the debugging flow controls

Lets look at the basic flow controls offered in most IDEs:

Eclipse jGRASP


List of common/shared controls:

Control What it does
Step Over Execute exactly one line of code, skipping any method calls on this line
(methods still get executedjust not shown)
Step in[to] Same as above except now the debugger steps into any method calls
(including constructors). Essentially the smallest increment available.
Step Return/Out Return from a method which has been stepped into. The remainder of
the code that was skipped by returning is still executed.
Resume Continue the debugging process until the next breakpoint, or until the
end of the debugging process.

Specific controls:

Stop: Ends the debugging process. Also found in jGRASP on the bottom to the left of the
console window labeled END.

Step to Cursor (jGRASP only): runs to the current line the cursor is at in the active window.
All breakpoints are ignored during this operation.

Now, play around with these controls to get familiar with how they control the flow of execution!
Note how the highlighted line changes with respect to each control, how the variable values
also update with respect to your programs instructions, and how print statements appear in
the console at the appropriate times.

Tip: Eclipse has keyboard shortcuts (hotkeys) for these controls! Hover your mouse over the
buttons to see a tooltip containing the associated hotkey.

Note: only the basic controls are covered here, which should be enough to get you started!
Check your IDEs documentation for more advanced flow control options.


6

A closer look at the call stack
And the variable values viewer!

Every method call (including recursive ones) gets pushed onto the call stack. This is how the
order of all method calls are managed when returning from finished methods. The stack data
structure is beyond the scope of this document, so here is a link to more information:
http://en.wikipedia.org/wiki/Stack_(data_structure)

Eclipse and jGRASP call stacks & variables viewer:





After stepping through some recursive Fibonacci calls, you can see the call stack filling up with
all the recursive calls in the order each was called. Note how the call to main is at the bottom
of the stack, as it was the first method called in the execution of the program.

Tip: In Eclipse, you can select a method in the call stack and click Step Over to pass through
all method calls above it. This is very useful if you have many redundant method calls.
7

The variables viewer lets you see the values of variables update in memory as you debug/step
through your program. Note that only variables in the current method (as selected in the call
stack) are shown in the variables window. Also note how you can select different method calls
in the call stack to view that methods variables. For instance, when the Fibonacci methods are
selected, you see the variable n that was passed in via fibonaccis parameter. When the main
method is selected, you see the local for-loop variable i used to keep index and the command
line arguments String[] args.

If you so desire, you can even change the variables values in runtime during the middle of the
debugging process! Do so by right-clicking (or equivalent) on a variable and select the
appropriate option. A dialog window will pop up prompting you for a new value:




Thats about it! Now, you have all the knowledge tools to start debugging any of your code.
Play around with all of these great debugging features to get used to themthey come in
handy when you cant pinpoint a certain bug. Plus, sometimes its just fun to watch your
algorithm/code execute line-by-line! Its hard to appreciate all the orchestration that goes on
when you merely hit Run and your entire program finishes in 0.1 seconds!

So, in conclusion, debugging is a systematic approach to locating bugs in your code. With the
help of sophisticated modern software debuggers, you can see what's going on with your code
line-by-line. This makes it much easier to find bugs and errors as it enables you to focus your
attention to individual lines and sections of your code rather than trying to process everything
all at once to find errors. It sure beats using print statements everywhere! Take advantage of
this fantastic modern programming privilege to help speed up your software development!

8

FAQs

Q: I cant find the buttons/windows shown in the screenshots!
A: Your IDE environment perspective could be set-up differently than the one shown in this
tutorial. However, if you havent significantly customized your environments layout from the
default, your screens should look similar to the screenshots. All the debugging tools are shown
by default. If at any time they are missing, restore the default perspective/layout or check in
your IDEs window options.

Q: As I step through my program while debugging, a bunch of unknown methods sometimes
appear on the call stack that are missing source code.
A: These are methods from classes in the Java Standard Library of which you dont have the
source code) if you used any classes from it such as Random, Scanner, or any GUI
components.

Q: Speaking of GUI components, can I see the construction of my JPanels line-by-line as well?
A: Noremember that the construction of GUI components happens in memory and isnt
visible until the outermost container is packed and set to be visible. You can, however, view the
instance data in the variables window as individual components are being constructed in
memory.

Q: How does user input work when debugging (like with Scanner)?
A: The same! Try it out! The debugger will pause execution at moments where user input is
required to continue. Execution cannot proceed until a value is provided.


9

A concrete example of debugging

The remainder of this guide is dedicated to working through a sample debugging session. If
you feel like youve got the basics down, feel free to stop reading at this point, and try
debugging your own code!

Note: from this point on, screenshots from just Eclipse will be shown as the major debugging
components have already been shown and outlined for both IDE's.

For this example, we will be debugging a bubble sort algorithm. If you arent familiar with how
this algorithm worksget familiar: http://en.wikipedia.org/wiki/Bubble_sort

Here is the source code well be using:

public class BubbleSort
{
public static void bubbleSort(int[] array)
{
if (array == null || array.length <= 1)
{
return; // Invalid input or unnecessary to perform sort
}

int temp = 0; // Used when swapping values
for (int k = array.length; k > 0; k--)
{
for (int i = 0; i < k; i++)
{
if (array[i] > array[i+1])
temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
}
}

} // End bubbleSort method

public static void main(String[] args)
{
int[] a = {0,-2,1,5,-8,2,1,6,7,-7,2};
bubbleSort(a);
for (int i=0; i < a.length; i++)
System.out.print(a[i] + " ");
}

}

Load this code up in your IDE and hit run. What happens?

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 11
at BubbleSort.bubbleSort(BubbleSort.java:16)
at BubbleSort.main(BubbleSort.java:28)

Yikes! Looks like a great opportunity to do some debugging!
10

Obviously, one of the for-loops array bounds is incorrect since an array index out of bounds is
trying to be accessed. Weve pinpointed that muchbut which loop? Which line? Best way to
find out is to set a breakpoint and start stepping through our code!

>> 1. Set a breakpoint at the call to bubbleSort(a) and hit debug.



Note the int[] array we made and how we can see its value in the variables window. We expect
this array to be sorted in ascending order after the call to bubbleSort(a) is complete.


11

>> 2. Lets start stepping through. Since we want to see whats going on in the actual
bubbleSort method (since the exception happens in there), hit Step Into several times.
Watch the debugger go through the code line-by-line and observe the variable values being
updated. For now, just keep stepping through until the variable i gets the value 10.



Right off the bat, you now see whats wrong: the arrays length is 10 (which means the last
elements index is 9, but were trying to access arrayi with i = 10 thus the source of the
ArrayIndexOutOfBoundsException! Sure enough, if you execute one more line, youll get the
exception. You can stop the debugging at this point. Well have to make a correction and try
again.

Since i increments to k and k is set to array.length initially, it follows that the correct bound for
k should be initializing it to array.length 1 in the outer for-loop. Make this change and run the
program (you can remove the breakpoint for now)what happens? Well the program
finishes executing without any exceptions thrown, but what happened to our arrays data?

Output: 2 7 7 2 2 2 5 5 0 0 0

First of all, that is not sorted. Second, those arent even the values we first initialized in the
array. Here is a classic situation where although your program may compile and run, it doesnt
actually get the job done correctlytheres a semantic logic error somewhere!

Since we know nothing really happens in the code until we reach the for-loops, lets set a
breakpoint there and start stepping through againthis time paying close attention to the
arrays elements.

12

>> 3. Set a breakpoint at the outer for-loop, hit debug, and start stepping into until you
reach the if conditional statement.



According to bubble sort, here we check to see if an array element is greater than the next
one, which if true, we swap the values to begin the sorting process. Here, 0 is indeed greater
than -2, so we expect a swap. Lets see what happens!

13

>> 4. Step into a few times. Watch as the elements in the array swap in the variables
window.



Good! The values swapped as expected. Now as we continue on to compare the next 2
elements: 0 and 1, since 0 is less than 1, according to bubble sort, we dont expect a swap.
Step into once:


14

We expect the if statement to evaluate to false and skip the block of code where it swaps the
elements, thus proceeding to the next iteration of the inner for-loop with i = 2. Right? Well,
lets find out. Step into once:



Wrong! Look where the next line of execution is! Only one line was skipped due to the if
statement being false. What we wanted was the entire block to be skippedor associated
with the if statement. A rather silly mistake hereremember that in Java,
whitespace/indentation is irrelevant when declaring blocks of code. A code block to be
associated with an if conditional is always the next valid Java statement, which can be anything
from a semicolon (; which does nothing), one line, or a block to be treated as one statement
indicated with { } curly braces.

In our current code, the next valid statement for the if is only the line following it. So thats
where the problem is! Since we want the entire block proceeding the if statement to be
treated as one block, we must encapsulate the three lines in between curly braces.

>> 5. But before you make that change, if you want to see the damage this error does to
the array, continue stepping through the program!

15



After a while, you can really start to see the damage done to the array (we lost our original
data!) due to half the swapping done every iteration of the loop regardless of whether one is
actually needed. By half the swapping, we mean that old temp values are being
unnecessarily copied in unnecessary swaps).

Alright, now that youve added the necessary curly braces, hit run (or if you wish to watch the
incredibly inefficient bubble sort algorithm in actionkeep the breakpoint and hit debug) and
see the output of the program when finished executing to be:

Output: -8 -7 -2 0 1 1 2 2 5 6 7

Perfect. You have successfully debugged and fixed a broken bubble sort algorithm!

Of course, most debugging scenarios wont end as easily or quickly, as this case was kept
short for demonstrative purposes. However, now that you understand the process, debugging
should become a regular resource in your programming toolbox.

This tutorial is now complete.
Happy debugging!







Written by Joseph Gee Kim (for CS1331 Spring 2011 at Georgia Tech)
Version 1.0 last edited April 20, 2011

Anda mungkin juga menyukai