5 min read

In this article by Nataraju Neeluru, we will learn how to debug a Java program using a simple command-line debugging tool called JDB. JDB is one of the several debuggers available for debugging Java programs. It comes as part of the Sun’s JDK. JDB is used by a lot of people for debugging purposes, for the main reason that it is very simple to use, lightweight and being a command-line tool, is very fast. Those who are familiar with debugging C programs with gdb, will be more inclined to use JDB for debugging Java programs.

We will cover most of the commonly used and needed JDB commands for debugging Java programs. Nothing much is assumed to read this article, other than some familiarity with Java programming and general concepts of debugging like breakpoint, stepping through the code, examining variables, etc. Beginners may learn quite a few things here, and experts may revise their knowledge.

(For more resources on Java, see here.)

Introduction

JDB is a debugging tool that comes along with the Sun’s JDK. The executable exists in JAVA_HOME/bin as ‘jdb‘ on Linux and ‘jdb.exe‘ on Windows (where JAVA_HOME is the root directory of the JDK installation). A few notes about the tools and notation used in this article:

  • We will use ‘jdb’ on Linux for illustration throughout this article, though the JDB command set is more or less same on all platforms.
  • All the tools (like jdb, java) used in this article are of JDK 5, though most of the material presented here holds true and works in other versions also.
  • ‘$’ is the command prompt on the Linux machine on which the illustration is carried out.
  • We will use ‘JDB’ to denote the tool in general, and ‘jdb’ to denote the particular executable in JDK on Linux.
  • JDB commands are explained in a particular sequence. If that sequence is changed, then the output obtained may be different from what is shown in this article.

Throughout this article, we will use the following simple Java program for debugging:

public class A
{
private int x;
private int y;

public A(int a, int b)
{
x = a;
y = b;
}

public static void main(String[] args)
{
System.out.println(“Hi, I’m main.. and
I’m going to call f1″);
f1();
f2(3, 4);
f3(4, 5);
f4();
f5();
}

public static void f1()
{
System.out.println(“I’m f1…”);
System.out.println(“I’m still f1…”);
System.out.println(“I’m still f1…”);
}

public static int f2(int a, int b)
{
return a + b;
}

public static A f3(int a, int b)
{
A obj = new A(a, b);
obj.reset();
return obj;
}

public static void f4()
{
System.out.println(“I’m f4 “);
}

public static void f5()
{
A a = new A(5, 6);

synchronized(a)
{
System.out.println(“I’m f5, accessing a’s fields ” +
a.x + ” ” + a.y);
}
}

private void reset()
{
x = 0;
y = 0;
}
}

Let us put this code in a file called A.java in the current working directory, compile it using ‘javac -g A.java’ (note the ‘-g’ option that makes the Java compiler generate some extra debugging information in the class file), and even run it once using ‘java A’ to see what the output is. Apparently, there is no bug in this program to debug it, but we will see, using JDB, how the control flows through this program. Recall that, this program being a Java program, runs on a Java Virtual Machine (JVM). Before we actually debug the Java program, we need to see that a connection is established between JDB and the JVM on which the Java program is running. Depending on the way JDB connects to the JVM, there are a few ways in which we can use JDB. No matter how the connection is established, once JDB is connected to the JVM, we can use the same set of commands for debugging. The JVM, on which the Java program to be debugged is running, is called the ‘debuggee’ here.

Establishing the connection between JDB and the JVM

In this section, we will see a few ways of establishing the connection between JDB and the JVM.

  1. JDB launching the JVM:

    In this option, we do not see two separate things as the debugger (JDB) and the debuggee(JVM), but rather we just invoke JDB by giving the initial class (i.e., the class that has the main() method) as an argument, and internally JDB ‘launches’ the JVM.

    $jdb A
    Initializing jdb …

    At this point, the JVM is not yet started. We need to give ‘run’ command at the JDB prompt for the JVM to be started.

  2. JDB connecting to a running JVM:

    In this option, first start the JVM by using a command of the form:

    $java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,address=6000 A
    Listening for transport dt_socket at address: 6000

    It says that the JVM is listening at port 6000 for a connection. Now, start JDB (in another terminal) as:

    $jdb -attach 6000
    Set uncaught java.lang.Throwable
    Set deferred uncaught java.lang.Throwable
    Initializing jdb …
    >
    VM Started: No frames on the current call stack

    main[1]

    At this point, JDB is connected to the JVM. It is possible to do remote debugging with JDB. If the JVM is running on machine M1, and we want to run JDB on M2, then we can start JDB on M2 as:

    $jdb -attach M1:6000

  3. JDB listening for a JVM to connect:

    In this option, JDB is started first, with a command of the form:

    $jdb -listen 6000
    Listening at address: adc2180852:6000

    This makes JDB listen at port 6000 for a connection from the JVM. Now, start the JVM (from another terminal) as:

    $java -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=6000 A

    Once the above command is run, we see the following in the JDB terminal:

    Set uncaught java.lang.Throwable
    Set deferred uncaught java.lang.Throwable
    Initializing jdb …
    >
    VM Started: No frames on the current call stack

    main[1]

    At this point, JDB has accepted the connection from the JVM. Here also, we can make the JVM running on machine M1 connect to a remote JDB running on machine M2, by starting the JVM as:

    $java -Xdebug -Xrunjdwp:transport=dt_socket,server=n,address=M2:6000 A

LEAVE A REPLY

Please enter your comment!
Please enter your name here