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