Debugging is an art of finding errors (bugs) and eradicating (debug) them from a piece of code written in any programming language. The error can be syntactical or logical. Syntactical errors are easily picked up by the compiler, but logic errors are hard to find. As a result, programmers have to go through eyeball traces to pinpoint the error. It is often a very tedious process because one may have to trace through every statement to pinpoint the bug. The degree of tediousness depends upon the qualitative cardinality of the code, such as the complexity and quantity of code. Modern compilers provide tools, called debuggers, to help in the process. Java Development Kit (JDK) includes a debugger tool, called jdb, thaat can be used to monitor the execution of applications; it also can locate, and remove bugs in the code. This article shows how to use it with a little generic idea about the process of debugging.
Techniques of Debugging
It is very rare to create a perfect piece of code the first time around, even for a seasoned programmer. What one does is write a piece or code, compile and run through it, take care of the problematic points, and rewrite until fully satisfied that the code is perfect and bug free. Typically, the way of debugging a Java code is to scan visually through the program statements and use the System.out.println() method to print the values of the variables at suspected locations of the program code. This simple technique is quite effective and has been used for ages.
With time, the complexity and volume of code increased. This also increased the complexity of debugging. As a result, pinpointing a bug using the same old method is like searching for a needle in a haystack. Therefore, to assist developers in the debugging process, different types of debuggers emerged. A debugger is nothing but a program that helps locate and correct programming errors. Java Debugger is a debugger tool for Java code. It is especially helpful to figure out what the program is doing just before an exception is triggered.
A debugger runs the program normally, line by line, until a specified break point is reached. A break point is a specified position in the line of code marked for the execution to halt until further instruction. It provides an important clue for the programmer to understand exactly what is going on with each statement. The programmer may decide to observe and print the values of the program variables at any point of execution. Unlike the old method of a complete manual debugging, this does not require one to insert a print statement (such as System.out.println() ) to trace down values of the program variables.
A debugging tool infuses certain automation in the process of debugging. It is simply a tool for convenience; the real debugger is the programmer himself. There are different types of debuggers available for Java. Some provide a GUI front end, some are associated with the IDE, and some are as simple as a console debugger. The debugger we shall see down the line is jdb, which is a console debugger. It comes with JDK, so no extra software is required to use this debugging tool.
Working with jdb
As we know, there are two types of error: syntax errors and logic errors. Eradicating syntax errors is not that difficult because the hard task of identifying the error is done by the compiler. The compiler simply indicates the reason and refuses to compile. The programmer then can modify the code with the correct syntax. Logic errors, on the other hand, do not prevent the code from compiling successfully. And, once compiled, they show their true color when he program runs. This type of error is typically suitable for debugging with the jdb debugger tool.
Basic jdb Commands
Some of the commonly used commands are:
- help or ?: Displays the available list of commands with a short description
- run: Command to start execution
- cont: Command to continue execution after breakpoint
- print: Displays the value of program variables objects
- dump: Similar to print command but more verbose
- threads: Lists currently running threads
- thread: Selects a thread as a current thread
- where: Dumps current thread’s stack content. If the command is used as where all, it dumps the stack content of all the threads under the current thread group
- stop at or stop in: Command to set breakpoints
- clear: Removes breakpoints
- step: Executes next line be it a current stack frame or a method call
- next: Executes next line of current stack frame
Apart from these there many other commands. Use help or ? to list them as follows.
Figure 1: List of jdb commands
Let’s investigate the following example code.
public class Employee { private int id; private double salary; public Employee(int id, double salary) { setId(id); setSalary(salary); } void setId(int id) { this.id = id; } int getId() { return id; } void setSalary(double salary) { this.salary = salary; } double getSalary() { return salary; } }
Listing 1: Employee.java
import java.util.Random; public class TestJDBDemo { final static int MAX = 10; public static Employee[] emp = new Employee[MAX]; public static void main(String[] args) { populateData(); System.out.println("Starting Salary of the Employee"); printData(); double commission = 12.05; System.out.println("Adding $ " + commission + " as commision to all employee salary"); addCommissionSalary(commission); System.out.println("Revised salary amount"); printData(); } public static void addCommissionSalary(double rs) { for (int i = 0; i < MAX; i++) { double result = emp[i].getSalary() + emp[i].getSalary() * rs; emp[i].setSalary(result); } } public static void printData() { for (int i = 0; i < MAX; i++) System.out.printf("nID: %d, Salary: $ %.2f", emp[i].getId(), emp[i].getSalary()); System.out.println("n---------------------------------"); } public static void populateData() { Random r = new Random(); for (int i = 0; i < MAX; i++) { emp[i] = new Employee(101 + i, r.nextInt(10000) + 1000); } } }
Listing 2: TestJDBDemo.java
Compile the code and start jdb (jdb is in the bin directory of the JDK installed location). We may also run the code with jdb.
Figure 2: Compiling Java classes with the -g option and executing the jdb debugger
Using Breakpoints
Breakpoints are basically markers set in the line of code to indicate that the execution must pause at that point. Programmers can use this time to examine the values of program variables used up to that point and determine the potential cause of logical errors.
Figure 3: Setting breakpoints
Using the print and dump Commands
We can print the value of the program variables with the print command. Some illustrations can be seen in the following screen shots.
Note that to display local variables (such as printing the value commission in Figures 4-8), the classes must compiled with the -g option, as illustrated in Figure 2.
Figure 4: Print commands
Figure 5: Using dump to display the object’s content
Figure 6: Threads running currently
Figure 7: Dumping the current thread stack content with the where command
Figure 8: Using step and next commands
Conclusion
The commands used with jdb provide the capability to locate and fix logical errors in Java code. Although jdb is not as highly efficient as gdb, it can be used effectively to eradicate bugs in the code. The console commands may seem inconvenient to use but, rest assured, they are sufficient on most occasions. It is an important companion for Java application developers, along with other tools.