第 299 回 PTT のお知らせ
日時: 2004年4月22日(木) 18:30 から
場所: 東京工業大学大岡山キャンパス 西8号館10階1008号室
地図を参考に、大岡山駅(東急大井町線、東急目黒線)からおいで
下さい。駅前交差点を渡ったところがキャンパスの正門です(図の右下)。
開催場所は図の25番で示される建物です。
西8号館はふたつの建屋から構成されています。正面玄関を入ったところ
がE棟で玄関奥にのびる廊下を渡ってW棟に行けます。開催場所はW棟の
10階の最奥にあります。
話者:
薄井 義行
話題:
アスペクト指向を利用してデバッグコードを挿入できるソフトウェア開発環境
概要:
デバッグの際に記述するデバッグコードをアスペクト指向(AOP)を利用して挿入するシステムを提案する。システムの特徴として、デバッグコードとソー
スコードを分離して記述することができる。また、AOPを利用しているため特定のパターン(メソッド呼び出しなど)を入力することで、バッグコード挿入
位置を一度に複数指定することができる。デバッグコードの挿入位置の指定はGUIで行うこともできる。発表では一般的なAOPの話と本システムの特徴に
ついて話します。
第 299 回 PTT メモ
日時:2004年4月22日 (木) 18:30 から
場所:東京工業大学大岡山キャンパス 西8号館10階1008号室
出席者:
千葉 滋,竹内 秀行,松沼 正浩,柳澤 佳里,光来 健一,佐藤 芳樹,
鈴木 康博,日比野 秀章,熊原 奈津子,石川 零,西澤 無我,脇田 建,
鷹岡 良治,島村 謙太,靄見 敏行,岩崎 敏,中野 宏温,中西 悠,
広松 悠介(東工大),河邊 昌彦(早大),寺田 実,丸山 一貴,
花田 智洋,金山 政利,鈴木 信吾(電通大),伊知地 宏(ラムダ数教研),
山口 文彦(東京理科大),石畑 清(明大),網代 育大(NEC),
田中 哲朗,紙名 哲生,副田 俊介,渡邉 卓也,河内 一了,矢代 武嗣,
荒木 伸夫,森山 絵美,西村 大介,筧 一彦(東大)
話者:薄井 義行(東京工業大学)
題名:アスペクト指向を利用してデバッグコードを挿入できるソフトウェア開発環境
概要:
本研究ではデバッグのためにアスペクト指向(AOP)を利用したツールであるEclipse
プラグインBugdelを提案する。AOPとはクラス間にまたがる処理をモジュール化
するものでありpointcutによりイベントを指定しpointcut位置で実行するコード
をAdviceとして記述する。pointcutではフィールドアクセス、メソッド呼び出し、
例外ハンドラの実行などを指定することができる。本システムではデバッグコード
挿入位置をpointcutで指定することで挿入位置をまとめて指定することができる。
AOPの言語として有名なものにはAspectJがあるが、本システムの特徴としてデバッグ
に有効であると考えられる行単位のpointcutを提供している。また、Adviceコード内
からpointcut位置に存在するローカル変数へアクセスすることができる。これらの
機能はAspectJでは提供されていない。なぜならメソッドのカプセル化を阻害し、
アスペクトのモジュール性を低下させてしまうからである。しかし、本システム
ではデバッグには有用であると考え提供している。
参考 [Bugdel Home Page: http://www.csg.is.titech.ac.jp/~usui/bugdel/]
質疑応答:
- - Javassistでbytecodeに対するAOP処理はできるのか
- できます。
- 直接的なAPIは提供していませんが、バイトコードを調べるためのAPIが提供され
ているので、それらを使うことでAOP的な処理を行えます。また、Javassitは
bytecodeの詳しい知識を持っていない人がbytecodeを編集できるようにも設計さ
れています。そのためbytecodeそのものを調べるような低レベルな抽象度の操作
に加えてソースコードレベルでの高い抽象度で操作を行うことができます。例えば、
メソッドに処理を追加しようとした場合、以下のようなプログラムで行うことが
できます。
CtClass clazz = ClassPool.getDefault().get("Point");//Pointクラスのメタオブジェクトを取得
CtMethod method = clazz.getDeclaredMethod("move");//moveメソッドのメタオブジェクトを取得
method.insertBefore("System.out.println(\"before move\");");//moveメソッドの直前にプリント文を挿入
このプログラムを実行する際、Pointクラスのソースコードは必要なく
bytecode(クラスファイル)のみで処理が行えます。
- 参考[ Javassist Home Page: http://www.csg.is.titech.ac.jp/~chiba/javassist/]
- - 行番号の管理は
- Eclipseのリソースマーカ機能を使って管理しています。
- リソースマーカはファイルの文字列や行番号に対してマークづけを行いファイルの
内容が変更された場合その位置情報を適切に更新してくれます。リソースマーカは
Eclipseで使われているデバッガのブレイクポイントやコンパイルエラーの位置を
表示するために利用されています。
- - AOPだけでなくデバッガとの比較は
- Bugdelはデバッグ出力ができるという点で優れていると思います。
- 通常のデバッガの場合ブレイクポイントを指定してプログラムの実行を止め変数を
確認しますが、変数が多い場合一つ一つ確認するよりもデバッグ出力により確認す
る方が効率的だと考えています。また、本システムはデバッグ出力だけでなくプロ
グラムの断片を挿入することもできます。
- - どういった形での変数指定が可能か
- デバッグコード内で利用できるローカル変数やフィールド変数へのアクセスは通常
のプログラムと同じ方法でアクセスできます。デバッグコードは挿入する位置の
プログラム断片を記述します。また、pointcutとしてメソッドの実行位置を指定した
場合にメソッドの引数へアクセスはコンパイル時の引数の名前でアクセスできますが
$1,$2,...をつかってアクセスすることもできます。
- - デバッグ用変数の利用は
- 例えば、メソッドの実行時間を測定する際に利用できます。メソッドの実行時間を
測定する際、デバッグ用変数を一つ用意しメソッド実行の直前に現在時刻を取得し
デバッグ変数に代入します。メソッド実行の直後にも現在時刻を取得し、それと
デバッグ変数からメソッドの実行時間を測定することができます。このような処理
を任意のメソッドで行えばBugdelはプロファイラとしても使えると思います。
しかし、現在はデバッグ用変数をサポートしていません。すぐにサポートしたいと
考えています。
- - クラスごとにあったりなかったりする変数にアクセスしようとした場合はどうなるか
- weave(コンパイル)エラーになります。
- 変数を持っていないクラスにデバッグコードを挿入しようとした場合weaveエラー
になりエラーを通知するダイログが表示されます。
- - 動的なデバッグコードの挿入、削除
- 現在はサポートしていません。
- 将来的にサポートする際、クラス定義の変更機能(Hot Swap機能)やデバッガを利用
して行いたいと考えています。Javaの場合クラスの定義はロードされる前であれば
自由に変えることができデバッグコードの挿入、削除ができますが、ロード済みの
クラスに対し挿入、削除を行うことが困難になります。
- このように動的にAOPのためのコード挿入、削除を行うことはDynamic AOP として
広く研究されています。私が所属する研究室でも研究されています。そこでは、
JDK1.4から導入されたJPDA(Java Platform Debugger Architecture)のクラス再
定義機能
を使うことで動的にコードの挿入、削除を行っています。また、アクティブフレーム
上のメソッドに対してはブレイクポイントをpointcut位置に設定してブレイクポ
イント
に達した際にAdviceコードをデバッガが実行するという方法をとっています。
- 参考 [効率的な Java Dynamic AOP システムを実現する Just-in-Time Weaver:
佐藤 芳樹, 千葉 滋,情報処理学会論文誌:プログラミング, vol. 44, no. SIG 13
(PRO18), 15-24 項, 2003年10月]
- - 行番号などcodeに依存する場合, デバッグコードの再利用は
- 現在はcodeに依存するデバッグコードの再利用はできません。
- フィールドアクセスやメソッド呼び出に対するデバッグコードの挿入は再利用でき
ますが、行単位のpointcutに対する再利用はできません。例えば、ソースコードに
行単位のpointcutを設定した後、そのソースファイルを別のものに置き換えた場合、
メソッドの外側などに行単位のpointcutが設定されてしまう可能性があります。
将来的には行単位のpointcutを指定した際その周辺のプログラムコードを覚えてお
きファイルが置き換えられても周辺プログラムの内容を見て適切に更新されるよう
にしたいと考えています。
- - 継承がある場合, どのように扱われるか
- 例えば以下のようなプログラムを考えた場合pointcutとしてmethodExecution
(B.foo())
を指定するとBugdelの場合、どの位置もデバッグコード挿入位置の対象となりま
せん。
- AspectJでexecution(void B.foo())とした場合も同様に対象となりません。しかし、
AspectJの場合withinやtargetなどのpointcutを使うことでB.foo()の実行位置を
pointcutの対象にできます。
class A{
void foo(){}
}
class B extends A{}
また、Bugdel、AspectJともにクラス名を指定する際「+」を使うことができ、これ
により任意のサブクラスという指定が出来ます。例えば、pointcutとして
execution(* java.awt.Window+.dispose())を指定するとjava.awt.Windowクラスと
そのサブクラスのdispose()メソッドが対象となります。
- - $0, $1, $2
- これはBugdelの処理系で使われているライブラリJavassistが提供する特殊変数
です。
メソッド呼び出しの際のターゲットとなるオブジェクトやメソッドの引数などを表
します。意味は各pointcutの種類によって変わります。
-
例えば、pointcutとしてフィールドアクセスを指定した場合、$1はフィールドに代入
する値を表します。任意のフィールド代入位置で代入する値を表示されたいと考えた
場合、pointcutとしてfieldSet(*)を指定します。そして、挿入コードでは
「System.out.println(?);」?の位置に$1を記述します。
-
pointcutとして例外ハンドラを指定した場合、$1は例外オブジェクトを表します。
java.lang.RuntimeExceptionの例外ハンドラの実行位置で例外のトレースを表示さ
せたいと考えた場合、pointcutとしてhandler(java.lang.RuntimeException)を
指定し、挿入するコードでは「$1.printStackTrace()」を記述します。
-
参考[Bugdel Home Page:
http://www.csg.is.titech.ac.jp/~usui/bugdel/3advice/index.html]
-
- 使い勝手の感想は
-
すみません。自分ではあまり利用していないのでコメントできません。
しかし、任意のメソッドの実行位置でデバッグ出力を行いメソッドの呼び出し
順番を把握するために利用しています。このような処理は
AspectJ/AJDT(AspectJ Development Tools)を使えば出来ますがAOPを少しだけ
利用したいと思った場合にはAspectJ/AJDTを使うよりもBugdel(本システム)を
利用した方が簡単に利用できます。
-
- conditional breakpoints
- pointcutで指定した位置にブレイクポイントを設定できるようになれば
条件付ブレイクポイントを指定できるようになると思います。そのために
Eclipseから提供されているデバッガインターフェイスを利用しようと
考えています。
-
- AspectJとの共存は? 行番号などの情報はAspectJによって消されてしまわないか
- 問題ありません。
- AspectJ用のEclipseの開発環境 AJDT(AspectJ Development Tools) とBugdelを
共に使った場合、コンパイルの順番は
1.javac[ソースコードからバイトコードを生成]
2.ajc(aspectj compilerのweave)[バイトコードからバイトコードを生成]
3.Bugdelのデバッグコードの挿入[バイトコードからバイトコードを生成]
の順番に行われます。行番号の情報やローカル変数の名前は1.javacで
コンパイルした際にバイトコード中に記述されます。その情報は2.ajcの
コンパイラを通ったあとも所持されています。そのため、Bugdelの処理系
でも行番号の情報などを利用することができます。
-
- 名前の由来
- 本システムの名前「 Bugdel 」は bug delete バグを削除するという意味です。
開発した当初はBugdel自体にバグが多かったので、
「Bugdelからバグが出てくる(バグでる)」というニュアンスも含んでいました。