JISDLab

View on GitHubtklab-group/JISDLab

lang
ja en

2. Debugging with JPDA

JPDA stands for Java Platform Debugger Architecture and is a framework for debugging Java programs.
JPDA is composed of the following three components.

JISD provides basic debugging operations such as setting breakpoints and step execution by using these interfaces.
In the following sections, we explain the basic debugging operations using JPDA.

2.1 Starting and Stopping the Debugger

In debugging using JPDA, when you start the debugger, if there is a target program to be started internally, it will be started, and then the debugging event processing will be started.

The set observation point operation described in 2.2 and later can be done before or after the debugger is started, and in either case, the setting is delayed until the class to be debugged is loaded.

For example, suppose you generate a debugger as follows.

var dbg = new Debugger("jisd.demo.HelloWorld", "-cp ../sample")

The following code will start the debugger.

dbg.run(1000)

The argument of run() is the approximate time in milliseconds until the target program reaches the observation point. In the above case, the thread will be restarted one second after the debugger is started.

To exit the debugger, execute the following.

dbg.exit()

When debugging is terminated, the debugging event processing is stopped and the target program is terminated.

2.2 Setting Observation Points

Debugging with JISD’s JPDA provides two types of observation points.

When a breakpoint is set, the target program is paused when the observation point is reached, and the variable value can be observed and the step execution can be performed on the spot. At the same time, the information (including the value) of the variable that can be observed from the observation point is generated as a DebugResult object.
On the other hand, when the Watch Point is set, the target program is temporarily stopped when the observation point is reached, but after the information (including the value) of the variable that can be observed from the observation point is acquired and the DebugResult object is generated, the target program can be restarted immediately. After the information (including the value) of variables observable from the observation point is acquired and the DebugResult object is generated, the target program is restarted immediately.

The user can specify for which variables the DebugResult object is generated.

The specific settings are described below.

2.3 Setting by line number

If you want to set an observation point at line 28 of the jisd.demo.HelloWorld class for the debugger instance dbg you just created, you can set it as follows HelloWorld class`, you can do either of the following.

Optional<Point> bp = dbg.stopAt("jisd.demo.HelloWorld", 28)
Optional<Point> wp = dbg.watch("jisd.demo.HelloWorld", 28) 

Note that both breakpoints and watchpoints are treated as instances of the BreakPoint class, which inherits from the Point class. Note that both breakpoints and watchpoints are treated as instances of the BreakPoint class, which inherits from the Point class, and that you cannot set more than one observation point in a row.

On the other hand, there are cases where you want to generate a DebugResult object focusing only on a specific variable.
For example, if you want to set a watchpoint on variable a in line 28 of the jisd.demo.HelloWorld, you can use

String[] vars = {"a"};
dbg.watch("jisd.demo.HelloWorld", 28, vars);

(and breakpoints as well).

2.4 Setting by method name

In some cases, you may want to set an observation point at the beginning of a method instead of at the line number.
For example, if you want to set a breakpoint at the beginning of sayHello() in the jisd.demo.HelloWorld class, you can use

dbg.stopAt("jisd.demo.HelloWorld", "sayHello")

(same for watchpoints).

If you want to set a breakpoint at the beginning of sayHello() in the jisd.demo.HelloWorld class and observe variable a and variable b, you can use

String[] vars = {"a", "b"};
dbg.stopAt("jisd.demo.HelloWorld", "sayHello", vars);

(same for watchpoints).

2.5 Default class name

In any of the above cases, you can omit the class name, in which case the main field of the Debugger will be referred to as the default class name. The main field is initialized with the class name specified in the first argument of the Debugger instance creation. For dbg in the previous examples, "jisd.demo.HelloWorld" corresponds to this field.

If no class name is specified as the first argument (port is specified), the class is initialized with an empty String. setMain() method after the debugger is generated can be used to specify the main field later, which can be used as the default class name.
The following is an example of setting the default class name to “jisd.demo.HelloWorld”.

dbg.setMain("jisd.demo.HelloWorld");

2.6 Operations at break

JISD provides operations specific to break time, such as resuming execution and step execution. If you perform any of the following operations outside of break time, the operation is ignored.

2.6.1 Resume Execution

dbg.cont()

2.6.2 step into

dbg.step()
// dbg.stepIn()
// dbg.stepInto()

2.6.3 step over

dbg.next()
// dbg.stepOver()

2.6.4 step out

dbg.finish()
// dbg.stepOut()
// dbg.stepReturn()

2.6.5 Show execution points in source code

Traverses the set srcDir, The source code corresponding to the current frame is displayed. The srcDir to be traversed must be set in advance. Alternatively, srcDir can be set sequentially as an argument to list().

// dbg.setSrcDir("srcdir1", "srcdir2", ...)
dbg.list()

2.6.6 List observable variables

dbg.locals()

2.6.7 Display stack trace

dbg.where()

2.7 Remote Debugging

Remote debugging using JPDA corresponds to 2. of the debugging methods described in the section 1. Creating a Debugger Instance, and debugging is performed on the program started by the user with the agent attached at hand. Note that when debugging remotely, be sure to compile the program with the debug information added.

The following is an example of executing a program to be debugged.

java -agentlib:jdwp=transport=dt_socket,server=y,address=12345,suspend=n -cp bin jisd.demo.HelloWorld

In response, the user creates a Debugger instance as described in Debug method 2. in chapter `1.

var dbg = new Debugger("host.docker.internal", 12345)

“host.docker.internal” is the address of the host on the docker container that can be used in Docker Desktop, which allows the JISDLab environment built on the docker container to connect to the program running on the host.

If the hostname is omitted, “localhost” will be set as the hostname.

var dbg = new Debugger(12345) // host = "localhost"

2.8 Reset Debugger

It is convenient to use redef() to reset the debugger. redef() creates a new debugger based on the debugger’s own property information. In this case, breakpoints are not inherited.

var dbg2 = dbg.redef()