jdb の使い方
- JDK に標準で付属しているデバッガの jdb についてまとめます
ドキュメント
- https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jdb.html
- https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr011.html
- https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/
- https://docs.oracle.com/javase/8/docs/technotes/guides/jpda/jdb.html
注意事項
- デバッグ対象のプログラムをコンパイルする時に -g オプションを付けてコンパイルしないとローカル変数にアクセスできません
- 以下の内容に間違いがありましたらごめんなさい
デバッグ対象のプログラム
ソースコード
MyProg01.java
import java.time.*;
class MyProg01 {
private String time;
public static void main(String[] args) {
MyProg01 app = new MyProg01();
app.loop();
}
void loop() {
while(true) {
time();
}
}
void time() {
try {
this.time = LocalDateTime.now().toString();
Thread.sleep(1000);
} catch(Exception e) {
e.printStackTrace();
}
}
}
プログラムのコンパイル
# javac -g MyProg01.java
jdb の基本的な使い方
デバッガとともにプログラムを起動する
# jdb MyProg01
Initializing jdb ...
> run
run MyProg01
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started:
>
クラスの一覧を確認する : classes
> classes
** classes list **
MyProg01
boolean[]
byte[]
char[]
double[]
float[]
int[]
java.io.BufferedInputStream
java.io.BufferedOutputStream
...
メソッドの一覧を確認する : methods
> methods MyProg01
** methods list **
MyProg01 <init>()
MyProg01 main(java.lang.String[])
MyProg01 loop()
MyProg01 time()
java.lang.Object <init>()
java.lang.Object registerNatives()
...
スレッドの一覧を確認する : threads
> threads
Group system:
(java.lang.ref.Reference$ReferenceHandler)0x15b Reference Handler cond. waiting
(java.lang.ref.Finalizer$FinalizerThread)0x15a Finalizer cond. waiting
(java.lang.Thread)0x159 Signal Dispatcher running
Group main:
(java.lang.Thread)0x1 main sleeping
デバッグ対象のスレッドを設定する : thread
> thread 1
main[1]
メソッドにブレークポイントを設定する : stop in
- stop in <class id>.<method> でメソッドにブレークポイントを設定できます
main[1] stop in MyProg01.time
Set breakpoint MyProg01.time
main[1]
Breakpoint hit: "thread=main", MyProg01.time(), line=25 bci=0
25 this.time = LocalDateTime.now().toString();
- stop at <class id>:<line> でソースコードの行にブレークポイントを設定することも可能です
現在設定されているブレークポイントを確認する : clear
main[1] clear
Breakpoints set:
breakpoint MyProg01.time
ブレークポイントを削除する : clear
- clear <class id>.<method> でメソッドに指定したブレークポイントを削除できます
main[1] clear MyProg01.time
Removed: breakpoint MyProg01.time
- ソースコード行に指定したブレークポイントは clear <class id>:<line> で削除できます
スタックフレームを表示する : where
main[1] where
[1] MyProg01.time (MyProg01.java:25)
[2] MyProg01.loop (MyProg01.java:17)
[3] MyProg01.main (MyProg01.java:10)
ローカル変数を表示する : locals
main[1] locals
No local variables
- ローカル変数を使っていなかったので、No local variables が出力されます
インスタンス変数を表示する : print
main[1] print this.time
this.time = "2016-01-22T17:33:51.001"
プログラムの中の実行箇所を確認する : list
main[1] list
21 void time() {
22
23 try {
24
25 => this.time = LocalDateTime.now().toString();
26 Thread.sleep(1000);
27
28 } catch(Exception e) {
29 e.printStackTrace();
30 }
ステップ実行する : step
main[1] step
>
Step completed: "thread=main", MyProg01.time(), line=26 bci=10
26 Thread.sleep(1000);
main[1] list
22
23 try {
24
25 this.time = LocalDateTime.now().toString();
26 => Thread.sleep(1000);
27
28 } catch(Exception e) {
29 e.printStackTrace();
30 }
31
次のブレークポイントまで処理を進める : cont
main[1] cont
>
Breakpoint hit: "thread=main", MyProg01.time(), line=25 bci=0
25 this.time = LocalDateTime.now().toString();
ブレークポイントで自動的に実行するコマンドを設定する : monitor
- monitor <command> で実行するコマンドを指定します
main[1] monitor where
main[1] cont
>
Breakpoint hit: "thread=main", MyProg01.time(), line=25 bci=0
[1] MyProg01.time (MyProg01.java:25)
[2] MyProg01.loop (MyProg01.java:17)
[3] MyProg01.main (MyProg01.java:10)
- monitor コマンドを複数回実行すると、複数のコマンドを実行させることができます
自動実行されるコマンドを確認する : monitor
main[1] monitor
3: where
自動実行されるコマンドを削除する : unmonitor
- unmonitor <monitor#> でコマンドを削除します
main[1] unmonitor 3
Unmonitoring 3: where
デバッガを抜ける : exit
main[1] exit
agentlib を使用してリモートからデバッグする
- デバッグされる側のプログラムの起動オプションに agentlib オプションを指定することでリモートからデバッグできるようになります
リモートからデバッグできるようにプログラムを起動する : agentlib
# java -agentlib:jdwp=transport=dt_socket,server=y MyProg01
Listening for transport dt_socket at address: 44411
jdb を接続する : attach
- jdb のオプションに attach オプションを指定してプロセスに接続します
# jdb -attach 44411
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
>
VM Started: No frames on the current call stack
main[1]
agentlib オプション無しで起動したプロセスにリモートから接続する
- agentlib オプションを付けずに起動したプログラムをリモートからデバッグする方法です
プロセス ID を指定して jdb を接続する : SAPIDAttachingConnector
$ jps
26073 Jps
26059 MyProg01
$ jdb -connect sun.jvm.hotspot.jdi.SAPIDAttachingConnector:pid=26059
Initializing jdb ...
- 接続されたプロセスは処理を一時停止します
- "java.lang.InternalError: Metadata does not appear to be polymorphic" エラーで接続できなかったり、接続できてもスレッドリストが取得できない場合があるようでした
jsadebugd 経由で jdb を接続する
- プロセス ID を指定して jdb を接続する代わりに jsadebugd を使用する方法でも Java のプロセスに jdb を接続することが可能です
- jsadebugd の接続
$ jps
26059 MyProg01
26110 Jps
$ jsadebugd 26059
Attaching to process ID 26059 and starting RMI services, please wait...
Debugger attached and RMI services started.
-
jsadebugd を接続すると、接続されたプロセスは処理を一時停止します
-
jdb の接続
$ jdb -connect sun.jvm.hotspot.jdi.SADebugServerAttachingConnector:debugServerName=localhost
Initializing jdb ...
>
- "java.lang.InternalError: Metadata does not appear to be polymorphic" エラーで接続できなかったり、接続できてもスレッドリストが取得できない場合があるようでした
jdb のコネクターの一覧を表示する
# jdb -listconnectors | grep Connector
Connector: com.sun.jdi.CommandLineLaunch Transport: dt_socket
Connector: com.sun.jdi.RawCommandLineLaunch Transport: dt_socket
Connector: com.sun.jdi.SocketAttach Transport: dt_socket
Connector: com.sun.jdi.SocketListen Transport: dt_socket
Connector: com.sun.jdi.ProcessAttach Transport: local
Connector: sun.jvm.hotspot.jdi.SACoreAttachingConnector Transport: filesystem
Connector: sun.jvm.hotspot.jdi.SADebugServerAttachingConnector Transport: RMI
Connector: sun.jvm.hotspot.jdi.SAPIDAttachingConnector Transport: local process