JavaTM Platform Debugger Architecture |
ホームページ |
JVMTI のモジュール性
Java Virtual Machine Tool Interface (JVMTI) では、仮想マシン (VM) で動作する Java プログラミング言語のアプリケーションをデバッグできるようにするため、その VM から提供される機能について記述されています。JPDA では、JVMTI は VM によって実装され、クライアントは JPDA のバックエンドになります。JPDA のリファレンス実装では、JVMTI は Java HotSpot VM によって実装され、クライアントはバックエンドのリファレンス実装 (JDK に付属の、jdwp.so や jdwp.dll などのネイティブ共用ライブラリとして提供される) になります。
Java HotSpot VM 以外の多くの VM も、JVMTI を実装しています。バックエンドのリファレンス実装は、ほかのいくつかのプラットフォームに移植されました。さらに、バックエンド以外にも JVMTI のクライアントがあります。もっとも有名なのは、ネイティブコードと Java プログラミング言語コードの両方のデバッグを可能にするアプリケーションエージェント (ネイティブレベルの制御と情報を必要とする) です。バックエンドのクリーンルーム実装を意識する必要はありません。そのようなバックエンドを作成することも可能ですが、相当な労力を必要とします。
一部の VM では、JVMTI の実装に問題があります。そのような VM では、JDWP が直接実装されます。クライアント側では、Java プログラミング言語で作成されていないアプリケーションは、JDI を使用するアプリケーションとして不適格なことがあります。アプリケーションによっては、JDWP のクライアントとして実装することもあります。
JDI は、アプリケーションの静的なビューを提供するようにシステムによって実装されることがあります。また、JDWP のフロントエンドとはまったく違う機構で情報を収集したり VM を制御したりするように実装されることもあります。
動作の概要
これまでは、各インタフェースを使用するさまざまな方法について説明してきました。このあとは、標準的な JPDA が全体としてどのように動作するかを見ていきます。説明の中では、個々の呼び出しやコードの詳細について、実例を取り上げます。そのような実例は、理解できなくても問題ありません。実例を具体的にするために紹介してあるだけです。
各インタフェースを橋渡しするのは、要求とイベントという 2 つのアクティビティです。要求は、デバッガ側から出されるもので、情報の照会、リモート側の VM やアプリケーションの状態変更の設定、およびデバッグ状態の設定が含まれています。イベントは、debuggee 側から出されるもので、リモート側の VM やアプリケーションの状態変化を示しています。
1 つの実例を調べてみましょう。IDE のスタック表示でユーザが局所変数をクリックし、その値を要求したとします。IDE は、JDI を使ってその値を取得します。具体的には、getValue
メソッドを呼び出します。次に例を示します。
theStackFrame.getValue(theLocalVariable)ここで、
theStackFrame
は com.sun.jdi.StackFrame
であり、theLocalVariable
は com.sun.jdi.LocalVariable
です。
次に、フロントエンドは、この要求を通信チャネル (たとえば、ソケット) 経由で、debuggee プロセスが動作しているバックエンドに送ります。そのとき、フロントエンドは、その要求を JDWP に準拠したバイトストリームの形式に変換します。具体的に言うと、フロントエンドは GetValues コマンド (バイト値 1) をStackFrame コマンドセット (バイト値 16) で送り、そのあとにスレッド ID、フレーム ID などが続きます。
バックエンドは、そのバイトストリームを解析し、JVMTI を介して VM に照会を送ります。具体的には、要求された値が整数だとすると、次のような JVMTI 関数の呼び出しを実行します。
error = jvmti->GetLocalInt(frame, slot, &intValue);バックエンドは、ソケット経由で応答パケットを返送します。そのパケットには
intValue
の値が入っており、JDWP に準拠したデータ形式になっています。フロントエンドは、応答パケットを解析し、その値を getValue
メソッド呼び出しの値として返します。最後に、IDE は、返された値を表示します。
デバッグ状態を変更する要求も、同様の方法で処理されます。たとえば、ブレークポイントを設定するという要求は、同様のステップで処理されます。もちろん、呼び出される JDI メソッドや、送信される JDWP コマンドや、呼び出される JVMTI 関数は違います。さらに、フロントエンドとバックエンドは、単にデータをやり取りする以上のことを行います。アクティビティを追跡およびスケジューリングし、情報を変換、フィルタリング、およびキャッシュします。したがって、ブレークポイントを設定する要求は、値を取得する照会とはかなり違った仕方で処理されますが、通信の手順は同じです。
デバッグしているアプリケーションがこのブレークポイントに達すると、何が起こるのでしょうか。今度は、イベントの出番になります。仮想マシンは、JVMTI インタフェースを介してイベントを送ります。具体的には、仮想マシンは、イベント処理関数を呼び出して、ブレークポイントを渡します。
バックエンドは、イベント処理関数を次のように設定しています。
static void Breakpoint(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread, jmethodID method, jlocation location) { ...
このバックエンド関数は、関心のあるイベントをフィルタリングし、そのイベントをキューに入れ、ブレークポイントイベント用に定義された JDWP 形式のソケットを介してそのイベントを送信するという、一連のアクティビティを開始します。フロントエンドは、そのイベントをデコードして処理し、最終的には JDI イベントを生成します。具体的には、JDI イベントは、com.sun.tools.jdi.event.BreakpointEvent
として公開されます。その後、IDE は、そのイベントをイベントキューから取り出して取得します。
theEventQueue.remove()ここで、
theEventQueue
は、com.sun.jdi.event.EventQueue
です。IDE は、JDI を介して多くの照会呼び出しを実行することにより、表示を更新すると予想されます。
移殖
仮想マシンの各実装には、それぞれ独自の JVMTI 実装が必要です。JVMTI の実装では、VM のデータ構造に深く踏み込む必要があり、イベントを取得するために VM 実装の中にフックを設定する必要があります。JVMTI サポートのない VM に JVMDT を追加するのは、かなりの作業になります。VM の複雑さと、実装する JVMTI のオプション機能の量に応じて、3 〜 12 か月の作業になると考えられます。JVMTI サポートが組み込まれている VM を新しいプラットフォームに移殖する作業は、VM の JVMTI 以外の部分の移殖が中心になります。JVMTI 部分に必要な付加的な作業量は、比較的少ないものです。
バックエンドのリファレンス実装を新しいプラットフォームに移すには、多くの場合、ソースにわずかの変更 (数行のみ) を加えるか、ソースをまったく変更せずに、再コンパイルするだけで済みます。同じプラットフォーム上で新しい VM を使用する場合は、バックエンドのバイナリコードは多くの場合そのまま動作します。ただし、それは Java プログラミング言語のコードではないため、内容を理解することはできません。このドキュメントではライセンスの問題には触れていないので、注意してください。
フロントエンドの実装は Java プログラミング言語で作成されているため、どのプラットフォームまたは VM でも動作します。ただし、一部のシステムでは、コネクタコードの機能の一部を拡張する必要がある場合もあります。たとえば、フロントエンドのリファレンス実装に含まれている起動ツールでは、仮想マシンが J2SE の規則を使って起動されることを前提としています。JDI のユーザが自分たちの希望に合わせて起動ツールの構文を決めることもできますが、一般に、デバッガアプリケーションでは、その構文が JDI 実装の側で決められていると想定します。また、もし別の種類の通信チャネル (たとえば、シリアル接続) が必要な場合は、その機能も追加する必要があります。結局、その追加を可能にするには、JDI に SPI を追加することになります。ただし、現在のところ、それにはソースを変更する必要があります。
Copyright © 2004 Sun Microsystems, Inc.All Rights Reserved.
コメントの送付先: java-debugger@sun.com |