Download presentation
Presentation is loading. Please wait.
1
アスペクト指向言語による 例外処理の記述方法の改善
数理・計算科学専攻 千葉研究室 熊原奈津子 指導教員:千葉滋
2
例外処理の分離の重要性 例外処理とは ロジックとは分離して 書くべき 異常時の処理をまとめて記述したもの 正常時には実行されないコード
Javaにはtry-catchがある 分離はできるが正常時の処理のすぐ下に書かなければならない try { //ファイルへの操作 File file = new File(); file.open(); file.read(); file.write(); : } catch(IOException e){ IOException が発生した 場合の例外処理内容 } 修士論文発表会
3
頻繁に例外処理を差し替えたい - 実験プログラム
頻繁に例外処理を差し替えたい - 実験プログラム 分散環境で動くサーバの性能をテスト 最初は小規模なので例外処理なし 問題(故障)が起きたら必要に応じて追加 故障 命令 コンソール マシンの絵 命令 負荷 コンソールマシン 命令 負荷 制御 プログラム サーバマシン クライアントマシン 修士論文発表会
4
元のプログラムを編集 class Sender{
public void sendCommand(String host, String command) throws Exception{ : Socket s = new Socket(host, port); DataOutputStream out = new DataOutputStream(s.getOutputStream()); out.write(command); out.close(); s.close(); System.out.println(command + “ has been sent to ” + host); } try{ } catch(SocketException e){ // 故障を見つけたらログを出力 } クライアントに ソケットを張る 命令を 送信 複数のホストに 命令を送信 for (int i = 0; i < hostName.length; i++){ new Sender().sendCommand(hostName[i], “./client.sh”); } 修士論文発表会
5
他のマシンを使って再試行(リカバリ) catch 節の中から try ブロックの先頭へ戻る ホスト故障時には、自動的に復旧処理 try{
Socket s = new Socket(host, port); DataOutputStream out = new DataOutputStream( s.getOutputStream()); out.write(command); out.close(); s.close(); } catch(SocketException e) { ホストを変更して再試行 } Java には再試行を直接実現する 構文はない 修士論文発表会
6
GluonJ/Rの提案 - Recover アドバイスをもつ AOP System
例外処理をアスペクトとして記述 例外処理を分離 もとのロジックを壊さずに追加・削除が可能 例外処理に特化した pointcut 指定子を提供 Java バイトコード変換で実現 再試行するための特殊メソッド(retry)を用意 アドバイス内で利用可能 リカバリ処理を容易に記述できる 修士論文発表会
7
GluonJ/Rの特徴:アスペクトとして分離
block ポイントカット指定子 範囲をポイントカットできる ジョインポイントのペアを指定(try ブロック指定に相当) recover アドバイス catch 節に相当 @Glue class SenderRecovery { @Recover( etype = “SocketException”, advice = “{ $1 = getAnotherHost(); GluonJR.retry(); }”) Pointcut p = Pcd .block(Pcd.call(“Socket#new(..)”), Pcd.call(“PrintStream#plintln()”));} Socket s = new Socket( host, port); DataOutputStream out = newDataOutputStream( s.getOutputStream()); out.write(command); out.close(); s.close(); System.out.println(“done”); アスペクトの単純な例と、適用されるプログラムの例 アスペクトで記述することにより、 もとのロジックを壊さず、例外処理を分離 分離して記述 修士論文発表会 元のプログラム アスペクト
8
GluonJ/R のプログラム例 @Glue class SenderRecovery { @Refine
static class Diff extends Sender{ public String getAnotherHost(){ // 他のホスト名を返す } @Recover( etype = “SocketException”, advice = “{ $1 = getAnotherHost(); GluonJR.retry(); }”) Pointcut p = Pcd.block( Pcd.call(“Socket#new(..)”), Pcd.call(“PrintStream#plintln()”)) アドバイス 例外処理を 追加したい 範囲を指定 修士論文発表会 ウィーブ
9
GluonJ/Rの特徴:再試行 特殊メソッド GluonJR.retry() アドバイスの中で利用可能
block で指定した範囲の先頭に戻る @Glue class SenderRecovery { @Recover( etype = “SocketException”, advice = “{ $1 = getAnotherHost(); GluonJR.retry(); }”) Pointcut p = Pcd .block(Pcd.call(“Socket#new(..)”), Pcd.call(“PrintStream#println()”));} Socket s = new Socket(host, port); DataOutputStream out = newDataOutputStream( s.getOutputStream()); out.write(command); out.close(); s.close(); System.out.println(“done”); アスペクトの単純な例と、適用されるプログラムの例を載せる (先のスライドの例をそのまま載せるのが望ましい) リトライしやすい 元のプログラム アスペクト 修士論文発表会
10
行アノテーション ジョインポイントの一種 if(・・・){ : } else { GluonJ/Rの文法拡張
@Line(begin) for(・・; ・・; ・・){ @Line(end) ジョインポイントの一種 GluonJ/Rの文法拡張 block による範囲指定に利用 将来指定されそうな場所にジョインポイントがないとき、あらかじめ書いておく 自由に名前がつけられる 自由に名前がつけれる Pointcut p = Pcd.block(Pcd.line(“begin”), Pcd.line(“end”)); 行あのてーしょんは、ポイントカットできる 修士論文発表会
11
GluonJ/R の実装 GluonJ 1.3 を拡張して実装 行アノテーションのプリプロセッサを用意
プラグイン同様、追加削除が容易 GluonJ の総コード数5000行に対して 拡張部分は400行程度 バイトコード変換には Javassist を利用 行アノテーションのプリプロセッサを用意 修士論文発表会
12
例外ハンドラの追加 メソッドを実装している バイトコード ユーザから与えられる情報 始点・終点(ソースコード) ←始点 処理したい例外の型
例外が生じた場合に 実行したいコード ←始点 クラスファイル block で指定した範囲 ←終点 ・・・ メソッドの情報 ←例外ハンドラの先頭 アドバイス メソッドの属性 Exception Table に含まれる情報 Exception Table ・・・ 例外ハンドラがアクティブとなる バイトコードの範囲(始点・終点) 例外ハンドラがキャッチする 例外のクラス 例外ハンドラの先頭 ・・ 範囲・例外の種類・飛び先 修士論文発表会
13
GluonJR.retry() の実装 GluonJ/R 内でソースコードをコンパイルし、バイトコードへ変換
invokestatic 命令を命令長が同じ3バイトのgoto 命令に置換 メソッドの バイトコード ←始点 block 範囲の 始点に戻る アドバイス : GluonJR.retry(); : invokestatic : goto コンパイル 変換 修士論文発表会
14
範囲を選択するアルゴリズム 始点・終点が同一メソッド内に存在 最も近いペア(始点・終点)を選択
Pointcut p = Pcd.block( Pcd.call(“foo(..)”), Pcd.call(“bar(..)”)); 始点・終点が同一メソッド内に存在 最も近いペア(始点・終点)を選択 始点となるソースコードが実行される直前から終点となるソースコードが実行される直前までを選択 始点候補 終点候補 foo(); : hoge(); bar(); block 修士論文発表会 if (終点に一致 && 始点候補が存在 && 両者が同一メソッド内に存在){ このペアを範囲として選択; } if (始点に一致){ ジョインポイントを上書き保存;
15
実装の要点: blockで指定する範囲の始点
ジョインポイント・シャドーではない ジョインポイントに直接対応するバイトコード命令ではない Invokevirtual, getfield 等 その命令を含むソースコード行の最初のバイトコード命令 retry() の実現のため アドバイス内から goto で戻っても bytecode verifier をパスする new Socket dup aload_1 iload 4 invokespecial Socket() astore 5 先頭の命令 Socket s = new Socket(host, port); コンパイル Try ブロックの始点と終点には、スタックが空の状態で実行される命令の直前が選択される。 これは、recovery アドバイス内で記述できる retry() を実現するためである。 GluonJR での retry() は、bytecode 変換により、try ブロックの始点への goto 命令に置き換えられている。 もし try ブロックの始点のスタックの状態が空でなかった場合、 Retry() の直後に、スタック上にオペランドが積まれていることを想定した命令が実行されてしまう。 Retry() の実行後は、スタックの状態は空であるはずなので、 ここでスタックの整合性が保たれず、 Verify error が起きてしまう。 JP Shadow 修士論文発表会
16
× JVMのスタックの状態遷移図 ○ ・・・・・ 例 Socket s = new Socket(host, port); コンパイル
new Socket dup aload_1 iload 4 invokespecial Socket() astore 5 Socket s = new Socket(host, port); コンパイル new dup ・・・・・ int invokespecial str スタックは 空の状態 ref ref ref ref ref × ○ retry() で戻ってくる 修士論文発表会
17
行アノテーションのプリプロセッサ プリプロセッサで空の スタティックメソッド呼び出しに変換 if(・・・){ : } else {
プログラムの挙動には影響なし 性能に対する影響については後述 if(・・・){ : } else { } @Line(begin) for(・・; ・・; ・・){ @Line(end) LineAnnotation.begin(); class LineAnnotation{ public static void begin(){} public static void end(){} } LineAnnotation.end(); 修士論文発表会
18
現在の実装の限界 異なるブロックをまたぐ範囲の選択 finally 節の扱い メソッドをまたぐ範囲は選択できない
for、while 文等のブロックをまたいでも選択可能 finally 節の扱い 選択範囲外の finally 節が範囲に含まれてしまう可能性がある(コンパイラによる) Recover アドバイス内で例外を投げると、元の finally 節が無視される 修士論文発表会
19
finally 節に関する問題 例1 例2 try { : if (・・){ return; } } catch (){
例3 try { : } catch (){ } finally { } try { : } catch (){ } finally { } 例外発生 アドバイス throw e; アドバイス 追加した方の優先順位が 高いため finally 節が 実行されない 展開された finally 節も 含んでしまう finally 節が実行されない 修士論文発表会
20
実験について 実験環境 目的 CPU : Intel Pentium 4 CPU 2.8GHz メモリ : 1GB
OS : Microsoft Windows XP Professional SP2 JVM バージョン : 1.5.0_06 目的 GluonJ/Rで例外処理をアスペクトとして追加した場合のオーバーヘッドを測定 修士論文発表会
21
実験:try-catch文との実行速度の比較
メソッド m() を10億回 try-catch文を追加 アスペクトをウィーブ public class Test { public void m() throws Exception{ a(); b(); } public void a() throws Exception{} public void b() {} try{ } catch(Exception e){} 実行時間(秒) 例外処理なし 2.6 try-catch文 17.0 GluonJ/R @Glue class InsertTryCatch { @Recover(etype = "java.lang.Exception", advice = "") Pointcut p = Pcd.block(Pcd.call("test.Test#a(..)"), Pcd.call("test.Test#b(..)")); } 修士論文発表会
22
実験:行アノテーションの性能実験 実行時間(秒) 行アノテーションなし 2.6 行アノテーション追加(例外処理なし)
public class Test { public void m() throws Exception{ @Line(begin) a(); @Line(end) b(); } public void a() throws Exception{} public void b() {} 実行時間(秒) 行アノテーションなし 2.6 行アノテーション追加(例外処理なし) 行アノテーション追加(例外処理追加) 17.1 @Glue class InsertTryCatch { @Recover(etype = "java.lang.Exception", advice = "") Pointcut p = Pcd.block(Pcd.line("begin"), Pcd.line("end")); } 修士論文発表会
23
AspectJ との比較 handler ポイントカット after throwing アドバイス
既に try-catch 文がプログラムに含まれている場合、 catch 節の実行時をジョインポイントとして選択 after throwing アドバイス 選択されたジョインポイントが例外を投げて異常終了した場合に実行される 例外を補足する範囲の粒度がメソッドボディ Java の throws も同様の問題 リカバリ処理の実装には使えない 最後に必ず例外を投げなければならない Handler は、try-catch が含まれていない場合に、無力 Javaのthrowsを使って、wrapper をしかけても、粒度がメソッド本体 修士論文発表会
24
関連研究 Eiffel や Ruby の retry 機構 範囲を選択するポイントカット Java には存在しなかった
ループ処理のためのジョインポイント [Harbulot ら ‘06] for 文等のブロック全体をポイントカット ポイントカットできないループも数多く存在 block ポイントカットと行アノテーションは 任意の範囲を選択可能 修士論文発表会
25
まとめ・今後の課題 GluonJ/R 今後の課題 例外処理をロジックと分離して記述可能 容易にリカバリできる オーバーヘッドはほとんどなし
範囲をポイントカットできる pointcut 指定子 容易にリカバリできる アドバイスの中で再試行可能なメソッド オーバーヘッドはほとんどなし 今後の課題 現実のアプリケーションに適用 うまく記述できるか コードサイズをどのくらい減らすことができるか 修士論文発表会
Similar presentations
© 2024 slidesplayer.net Inc.
All rights reserved.