jdb の使い方メモ

  • 15
    Like
  • 0
    Comment
More than 1 year has passed since last update.

jdb の使い方

  • JDK に標準で付属しているデバッガの jdb についてまとめます

ドキュメント

注意事項

  • デバッグ対象のプログラムをコンパイルする時に -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