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