jdb コマンドでアプリをデバッグ
cf. https://docs.oracle.com/javase/jp/6/technotes/tools/windows/jdb.html
事前準備
簡単なアプリを作成してコンテナで動かします。
アプリ実装
デバッグに直接関係のあるクラスのみ記載します。
SampleServiceImpl.java
package nob.example.easyapp.service.impl;
import org.springframework.stereotype.Service;
import nob.example.easyapp.service.SampleService;
import nob.example.easyapp.service.model.SampleInModel;
/**
* SampleServiceの実装クラスです。
*
* @author nob
*/
@Service
public class SampleServiceImpl implements SampleService {
@Override
public String greeting(SampleInModel sampleInModel) {
String name = sampleInModel.getName();
if (name == "") {
name = complementName();
}
String message = "Hello, " + name + "!";
return message;
}
/**
* 名前が未入力の場合に補完します。
*
* @return
*/
private String complementName() {
String retName = "John doe";
return retName;
}
}
docker-compose
- ローカルでビルドした jar ファイルをコンテナから見えるようにしています。
- ports および entrypoint でデバッグポートを空け、デバッグモードに入れるようにしています。
services:
easyapp:
container_name: easyapp
image: eclipse-temurin:21
ports:
- 8080:8080
- 8484:8484
volumes:
- ./easyapp/target/easyapp-0.0.1-SNAPSHOT.jar:/easyapp-0.0.1-SNAPSHOT.jar
entrypoint: ["sh", "-c", "java -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:8484 -jar /easyapp-0.0.1-SNAPSHOT.jar"]
jdb コマンド使い方
開始
デバッグポートを指定して対話モードに入ります:
jdb -attach 8484
ブレークポイント設定
stop in {method.name}でメソッド冒頭にブレークポイントを置きます:
> stop in nob.example.easyapp.service.impl.SampleServiceImpl.greet
Set breakpoint nob.example.easyapp.service.impl.SampleServiceImpl.greet
stop at {class.name}:{line}で特定の行にブレークポイントを置きます:
> stop at nob.example.easyapp.service.impl.SampleServiceImpl:23
Set breakpoint nob.example.easyapp.service.impl.SampleServiceImpl:23
ブレークポイント確認・除去
clearでブレークポイント一覧を確認できます:
> clear
Breakpoints set:
breakpoint nob.example.easyapp.service.impl.SampleServiceImpl.greet
breakpoint nob.example.easyapp.service.impl.SampleServiceImpl:23
clear {breakpoint}でブレークポイントを除去できます:
> clear nob.example.easyapp.service.impl.SampleServiceImpl.greet
Removed: breakpoint nob.example.easyapp.service.impl.SampleServiceImpl.greet
ブレーク時の操作
localsで変数一覧を確認できます:
Breakpoint hit: "thread=http-nio-8080-exec-3", nob.example.easyapp.service.impl.SampleServiceImpl.greeting(), line=23 bci=16
http-nio-8080-exec-3[1] locals
Method arguments:
sampleInModel = instance of nob.example.easyapp.service.model.SampleInModel(id=6426)
Local variables:
name = "nob"
printおよびdumpで変数の中身を確認できます。前者はプリミティブ型向け、後者はオブジェクト向けです:
http-nio-8080-exec-3[1] print sampleInModel
sampleInModel = "SampleInModel(name=nob)"
http-nio-8080-exec-3[1] dump sampleInModel
sampleInModel = {
name: "nob"
}
setで変数を書き換えることができます。ただし、finalフィールドについては変更不可能です:
http-nio-8080-exec-7[1] locals
Method arguments:
sampleInModel = instance of nob.example.easyapp.service.model.SampleInModel(id=6429)
Local variables:
name = "nob"
http-nio-8080-exec-7[1] set name = "snail"
name = "snail" = "snail"
http-nio-8080-exec-7[1] locals
Method arguments:
sampleInModel = instance of nob.example.easyapp.service.model.SampleInModel(id=6429)
Local variables:
name = "snail"
nextおよびstepで処理を進めます。下記 (1) にブレークポイントを置いた場合、前者は (2), 後者は (3) に進みます:
@Override
public String greeting(SampleInModel sampleInModel) {
String name = sampleInModel.getName();
if (name == "") {
name = complementName(); // (1)
}
String message = "Hello, " + name + "!"; // (2)
return message;
}
/**
* 名前が未入力の場合に補完します。
*
* @return
*/
private String complementName() {
String retName = "John doe"; // (3)
return retName;
}
contで次のブレークポイントまで処理を進めます:
http-nio-8080-exec-2[1] cont
>
クラス・メソッド一覧確認
classesで jar に含まれているクラス一覧を確認できます:
# echo "classes" | jdb -attach 8484 | grep nob
nob.example.easyapp.service.model.SampleInModel
nob.example.easyapp.service.impl.SampleServiceImpl
nob.example.easyapp.service.SampleService
nob.example.easyapp.controller.impl.SampleControllerImpl
nob.example.easyapp.controller.SampleController
nob.example.easyapp.EasyappApplication
methods {class.name}}でクラス内のメソッド一覧を確認できます。
> methods nob.example.easyapp.service.impl.SampleServiceImpl
** methods list **
nob.example.easyapp.service.impl.SampleServiceImpl <init>()
nob.example.easyapp.service.impl.SampleServiceImpl greeting(nob.example.easyapp.service.model.SampleInModel)
nob.example.easyapp.service.impl.SampleServiceImpl complementName()
java.lang.Object <init>()
java.lang.Object getClass()
java.lang.Object hashCode()
java.lang.Object equals(java.lang.Object)
java.lang.Object clone()
java.lang.Object toString()
java.lang.Object notify()
java.lang.Object notifyAll()
java.lang.Object wait()
java.lang.Object wait(long)
java.lang.Object wait(long, int)
java.lang.Object finalize()
nob.example.easyapp.service.SampleService greeting(nob.example.easyapp.service.model.SampleInModel)