概要
Java Debug Wire Protocol (JDWP)は、デバッガと、そのデバッガでデバッグするJava仮想マシン(VM)(以下、ターゲットVMと呼びます)の間の通信に使用されるプロトコルです。これ以降、デバッグするVMのことを、ターゲットVMと呼びます。 JDWPはオプションです。JDKの実装によっては、使用できない場合があります。 JDWPが存在することにより、同一のデバッガを次の環境で動作させることができます。
- 同一のコンピュータ上の異なるプロセス、または
- リモート・コンピュータ上で実行します。
JDWPは、形式とレイアウトだけを詳細に定義し、トランスポートは定義していないという点で、多くのプロトコル仕様とは異なっています。 JDWP実装は、さまざまなトランスポート・メカニズムを単純なAPIを介して受け入れるように設計できます。 特定のトランスポートが、必ずしも上記のデバッガ/ターゲットVMの組み合わせすべてをサポートするわけではありません。
JDWPの設計は、簡単に実装できるよう十分に単純化されていますが、将来の拡張に対応する柔軟性もあります。
現在のところ、JDWPは、トランスポートを受け入れるためのメカニズムや、ディレクトリ・サービスを一切指定していません。 これは将来変更される可能性がありますが、その点は別のドキュメントで扱われることになるでしょう。
JDWPは、Java Platform Debugger Architecture (JPDA)内の1つの層を構成します。 このアーキテクチャには、より高レベルのJava Debug Interface (JDI)が含まれています。 JDWPは、JDIで効率的に使用できるように設計されています。その機能の多くは、その目的に沿って調整されています。 多くのデバッガ・ツールにとって、JDWPよりもJDIの方が適しています。これは特にJavaプログラミング言語で書かれたデバッガ・ツールに言えることです。 Java Platform Debugger Architectureの詳細は、このリリースに関するJava Platform Debugger Architectureのドキュメントを参照してください。
JDWPの起動
トランスポートの接続が確立されたあとは、パケットが送信される前に、接続の2つの側の間でハンドシェークが行われます。
ハンドシェークのプロセスには、次のステップがあります。
- デバッガ側は14バイトをVM側に送信します。これは、文字列"
JDWP-Handshake
"の14文字のASCII文字で構成されます。 - VM側は同じ14バイトで応答: "
JDWP-Handshake
"。
JDWPのパケット
JDWPはパケット・ベースであり、ステートフルではありません。 基本的なパケット・タイプには、コマンド・パケットと応答パケットの2種類があります。
コマンド・パケットは、デバッガまたはターゲットVMのどちらかにより送信されます。 コマンド・パケットは、ターゲットVMから情報を要求するため、またはプログラムの実行を制御するために、デバッガにより使用されます。 また、デバッガにターゲットVM内のイベント(ブレークポイントや例外など)を通知するために、ターゲットVMからコマンド・パケットが送信されます。
応答パケットは、コマンド・パケットへの応答としてのみ送信され、いつも、コマンドの成功または失敗に関する情報を提供します。 応答パケットは、コマンドで要求されたデータ(フィールドまたは変数の値など)を運ぶこともあります。 現在のところ、ターゲットVMから送信されるイベントは、デバッガからの応答パケットを必要としません。
JDWPは非同期です。このため、最初の応答パケットを受信する前に、複数のコマンド・パケットが送信される場合があります。
コマンドパケットおよび応答パケットのヘッダーは、同じサイズになっています。これは、トランスポートの実装および抽象化を容易にするためです。 各パケットのレイアウトは、次のようになります。
- コマンド・パケット
- ヘッダー
- length (4バイト)
- id (4バイト)
- flags (1バイト)
- command set (1バイト)
- command (1バイト)
- データ(可変長)
- ヘッダー
- 応答パケット
- ヘッダー
- length (4バイト)
- id (4バイト)
- flags (1バイト)
- error code (2バイト)
- データ(可変長)
- ヘッダー
JDWP経由で送信されるすべてのフィールドおよびデータは、ビッグ・エンディアン形式である必要があります。 ビッグ・エンディアンの定義については、Java仮想マシンの仕様を参照してください。 最初の3つのフィールドは、両方のパケット・タイプで同一です。
コマンド・パケットと応答パケットのフィールド
共用されるヘッダー・フィールド
length
lengthフィールドは、パケット全体のサイズ(lengthフィールドを含む)をバイト単位で表します。 ヘッダーのサイズは11バイトなので、データを含まないパケットでは、このフィールドが11に設定されます。
id
idフィールドは、パケットのコマンド/応答の各ペアを一意に識別するために使用されます。 応答パケットは、その応答対象であるコマンド・パケットと同じidを保持します。 このため、非同期のコマンドと応答を対応付けることが可能になります。 idフィールドは、1つのソースから送信される未処理のコマンドすべての中で一意でなければなりません。 デバッガから送信される未処理のコマンドでは、ターゲットVMから送信される未処理のコマンドと同じidを使用できます。 これ以外には、idの割当てに関する要求事項はありません。
ほとんどの実装では、ごく単純なカウンタで十分です。 そのようなカウンタでも2^32個の一意な未処理パケットを識別できます。それが、もっとも単純な実装手段です。
flags
flagsは、コマンドをキューに入れて処理する方法を変更するため、およびターゲットVMから送信されたコマンド・パケットをタグ付けするために使用されます。 現在のところ、1フラグ・ビットが定義されています。将来のバージョンのプロトコルでは、フラグがさらに定義される可能性があります。
0x80
- 応答パケット
応答ビットがセットされている場合は、それが応答パケットであることを示します。
コマンド・パケットのヘッダー・フィールド
command set
このフィールドは、有意義な方法でコマンドをグループ化する手段として有用です。 Sunが定義したコマンド・セットは、サポートしているJDIのインタフェースごとにコマンドをグループ化する場合に使用します。 たとえば、JDI VirtualMachineインタフェースをサポートするすべてのコマンドは、VirtualMachineコマンド・セットとしてグループ化されます。
コマンド・セット空間は、次のように大別されます。
0
-63
- ターゲットVMに送信されるコマンドのセット
64
-127
- デバッガに送信されるコマンドのセット
128
-256
- ベンダーにより定義されたコマンドおよび拡張機能。
command
このフィールドは、コマンド・セット内の特定のコマンドを識別します。 このフィールドは、コマンド・セット・フィールドとともに、コマンド・パケットの処理方法を示すのに使用されます。 つまり、何を実行するべきかを受信側に伝えます。 個々のコマンドについては、このドキュメントで後述します。
応答パケットのヘッダー・フィールド
エラー・コード
このフィールドは、応答しているコマンド・パケットが正常に処理されたかどうかを示すために使用されます。 値がゼロの場合は成功を、ゼロ以外の場合はエラーを示します。 返されるエラー・コードは、各コマンド・セットまたはコマンドに固有の場合もありますが、多くの場合はJVM TIエラー・コードに対応しています。
Data
データ・フィールドは、各コマンド・セットまたはコマンドに固有です。 また、コマンド・パケットと応答パケットのペアの間でも異なります。 たとえば、フィールド値を要求するコマンド・パケットのデータ・フィールドには、オブジェクトへの参照と、取得したい値のフィールドIDが含まれます。 それに対する応答パケットのデータ・フィールドには、要求されたフィールドの値が含まれます。
コマンドの詳細情報
一般に、コマンド・パケットまたは応答パケットのデータ・フィールドは、コマンドまたは応答データを定義する複数フィールドのグループを抽象化したものです。 データ・フィールドの各サブフィールドは、ビッグ・エンディアン(Java)形式でエンコードされます。 各コマンドとその応答のデータ・フィールドの構成については、このセクションで詳述します。
さまざまなJDWPコマンドおよび応答の多くに共通の、一般的なデータ型がいくつかあります。 それらについて、このあと説明します。
名前 | Size | 説明 |
---|---|---|
byte |
1 byte | バイト値。 |
boolean |
1 byte | ブール値。falseの場合は0、trueの場合はゼロ以外の値としてエンコードされる。 |
int |
4バイト | 4バイトの整数値。 整数は、符号付き(符号なしと明示されている場合を除く)。 |
long |
8バイト | 8バイトの整数値。 値は、符号付き(符号なしと明示されている場合を除く)。 |
objectID |
ターゲットVMに固有。最大8バイト(表のあとの説明を参照) | ターゲットVM内のオブジェクトを一意に識別する。 特定のオブジェクトは、その存続期間中(または、objectIDが明示的に廃棄されるまで)ずっと、JDWPのコマンドおよび応答の中で、ただ1つのobjectIDによって識別される。 ObjectIDは、参照オブジェクトがガベージ・コレクションされているかどうかにかかわらず、明示的にdisposedでない限り、別のオブジェクトを識別するために再利用されません。 0というobjectIDは、nullオブジェクトを表す。 オブジェクトIDが存在しても、オブジェクトのガベージ・コレクションが妨げられることはない。 ガベージ・コレクションされたオブジェクトにそのオブジェクトIDでアクセスしようとすると、INVALID_OBJECT エラー・コードが返されます。 ガベージ・コレクションは、DisableCollectionコマンドで無効にすることができますが、通常はそうする必要はありません。 |
tagged-objectID |
objectIDのサイズ+1バイト | 最初のバイトは、オブジェクトの型を識別するためのシグネチャ・バイト。 このバイトに設定可能な値については、JDWP.Tagを参照(使用できるのはオブジェクト・タグだけで、プリミティブ・タグは使用できない)。 そのバイトの後に、objectIDそのものが続く。 |
threadID |
objectIDと同じ | スレッドとして認識されているターゲットVM内のオブジェクトを一意に識別する |
threadGroupID |
objectIDと同じ | スレッド・グループとして認識されているターゲットVM内のオブジェクトを一意に識別する |
stringID |
objectIDと同じ | 文字列オブジェクトとして認識されているターゲットVM内のオブジェクトを一意に識別する。 ノート: これは、値としての文字列とはまったく異なる。 |
moduleID |
objectIDと同じ | モジュール・オブジェクトであることがわかっているターゲットVM内のオブジェクトを一意に識別します。 |
classLoaderID |
objectIDと同じ | クラス・ローダー・オブジェクトとして認識されているターゲットVM内のオブジェクトを一意に識別する |
classObjectID |
objectIDと同じ | クラス・オブジェクトとして認識されているターゲットVM内のオブジェクトを一意に識別する。 |
arrayID |
objectIDと同じ | 配列として認識されているターゲットVM内のオブジェクトを一意に識別する。 |
referenceTypeID |
ターゲットVMに固有。最大8バイト(表のあとの説明を参照) | ターゲットVM内の参照型を一意に識別する。 特定の1つのクラスについて、classObjectID とreferenceTypeID が同一であると想定するべきではない。 特定の参照型は、その存続期間中ずっと、JDWPのコマンドおよび応答の中で、ただ1つのIDによって識別される。referenceTypeIDは、参照しているクラスがアンロードされたとしても、別の参照型を識別するために再利用されることはない。 |
classID |
referenceTypeIDと同じ | クラス型として認識されているターゲットVM内の参照型を一意に識別する。 |
interfaceID |
referenceTypeIDと同じ | インタフェース型として認識されているターゲットVM内の参照型を一意に識別する。 |
arrayTypeID |
referenceTypeIDと同じ | 配列型として認識されているターゲットVM内の参照型を一意に識別する。 |
methodID |
ターゲットVMに固有。最大8バイト(表のあとの説明を参照) | ターゲットVM内のあるクラスのメソッドを一意に識別する。 methodIDは、クラス/インタフェース内のメソッド、またはそのサブクラス/サブインタフェース/実装者のいずれかを一意に識別する必要がある。 methodIDは、それ単独で必ずしも一意である必要はない。referenceTypeIDと組み合わせたときに、1つのメソッドを一意に識別していればよい。 referenceTypeIDでは、メソッドの宣言型またはサブタイプを識別できる。 |
fieldID |
ターゲットVMに固有。最大8バイト(表のあとの説明を参照) | ターゲットVM内のあるクラスのフィールドを一意に識別する。 fieldIDは、クラス/インタフェース内のフィールド、またはそのサブクラス/サブインタフェース/実装者のいずれかを一意に識別する必要がある。 fieldIDは、それ単独で必ずしも一意である必要はない。referenceTypeIDと組み合わせたときに、1つのフィールドを一意に識別していればよい。 referenceTypeIDでは、フィールドの宣言型またはサブタイプを識別できる。 |
frameID |
ターゲットVMに固有。最大8バイト(表のあとの説明を参照) | ターゲットVM内のフレームを一意に識別する。 frameIDは、VM全体(特定のスレッド内だけではない)のフレームを一意に識別する必要がある。 frameIDは、スレッドが中断されている間だけ有効であればよい。 |
location |
ターゲットVMに固有 | 実行可能ファイルの位置。 位置は、1バイトのタイプ・タグ+classID +methodID +符号なしの8バイト・インデックス(メソッド内の位置を識別する)により識別される。 ロケーション・インデックスの詳細については、belowを参照してください。 タイプ・タグは、位置のclassIDがクラスとインタフェースのどちらを識別しているかを示すために必要。 ほとんどすべての位置はクラス内に存在するが、インタフェースの静的な初期化子内に実行可能コードが存在することもある。 |
string |
変数 | UTF-8でエンコードされた文字列。ゼロで終了する文字列ではなく、先頭に長さ(4バイトの整数)が置かれる。 |
value |
変数 | ターゲットVMから取得された値。 最初のバイトは、型を識別するためのシグニチャ・バイト。 このバイトに設定可能な値については、JDWP.Tagを参照。 そのバイトのあとに、値そのものが続く。 この値は、objectID (「IDサイズの取得」を参照)か、プリミティブ値(1から8バイト)のどちらか。 それぞれの値型の詳細については、次の表を参照。 |
untagged-value |
変数 | 前述のvalue からシグニチャ・バイトを除いたもの。 この形式は、シグニチャ情報をコンテキストから判別できる場合に使用される。 |
arrayregion |
変数 | 一部の配列操作で使用される値のコンパクトな表現。 最初のバイトは、型を識別するためのシグニチャ・バイト。 このバイトに設定可能な値については、JDWP.Tagを参照。 次にくるのは、シーケンス内の値の数を示す4バイトの整数。 その後に、それらの値そのものが続く。プリミティブ値は、untagged-values のシーケンスとしてエンコードされる。オブジェクト値は、values のシーケンスとしてエンコードされる。 |
- メソッドの開始位置のインデックスは、メソッド内のほかのすべての位置よりも小さい。
- メソッドの終了位置のインデックスは、メソッド内のほかのすべての位置よりも大きい。
- メソッドの行番号表が存在する場合、特定の行に属する位置は、表内のその行の位置インデックスと次の行の位置インデックスの間になる。
メソッド内のインデックス値は、メソッド内の最初の実行可能ポイントから最後まで、ずっと増加していく。 多くの実装では、メソッド内の各バイト・コード命令が独自のインデックスを持つが、それは必須ではない。
オブジェクトID、参照型ID、フィールドID、メソッドID、およびフレームIDは、ターゲットVMの実装によってサイズが異なります。 一般に、そのサイズは、JNIおよびJVMDI呼出しの中で使用されるネイティブ識別子のサイズに対応しています。 各型の最大サイズは、8バイトです。 VirtualMachineコマンド・セットのIDSizesコマンドは、これらの各型のサイズを判別するためにデバッガによって使用されます。
デバッグ者が、実装されていないか、認識されていないコマンド・セットまたはコマンドを含むコマンド・パケットを受信すると、エラー・コード・フィールドがNOT_IMPLEMENTED
(「エラー定数」を参照してください)に設定された応答パケットが返されます。