290 likes | 400 Views
コードクローンの分布情報を用いた特徴抽出手法の提案. 井上研究室 服部 剛之. コードクローン. ソースコード中に存在する同一,もしくは類似したコード片を同一システム中に持つコード片 ( 以降単にクローンと呼ぶ ) コピーアンドペースト等により生成される ソフトウェア保守を困難にする コードの変更が必要な場合 バグ修正,機能追加 保守作業の手間が増大. クローンセット. CCFinder , Gemini. クローンを対象とした保守支援ツール 検出ツール : CCFinder[1] 分析ツール : Gemini[2] 国内外の個人・組織に配布
E N D
コードクローンの分布情報を用いた特徴抽出手法の提案コードクローンの分布情報を用いた特徴抽出手法の提案 井上研究室 服部 剛之
コードクローン • ソースコード中に存在する同一,もしくは類似したコード片を同一システム中に持つコード片 (以降単にクローンと呼ぶ) • コピーアンドペースト等により生成される • ソフトウェア保守を困難にする • コードの変更が必要な場合 • バグ修正,機能追加 • 保守作業の手間が増大 クローンセット
CCFinder,Gemini • クローンを対象とした保守支援ツール • 検出ツール: CCFinder[1] • 分析ツール: Gemini[2] • 国内外の個人・組織に配布 • 研究機関での利用 • 企業での商用ソフトウェア開発プロセスへの導入 • 配布先からのフィードバックを得ている [1] T. Kamiya, S. Kusumoto, and K. Inoue, “CCFinder: A multi-linguistic token-based code clone detection system for large scale source code”, IEEE Transactions on Software Engineering, 28(7):654-670, 2002. [2] Y. Ueda, T. Kamiya, S. Kusumoto and K. Inoue, “Gemini: Maintenance Support Environment Based on Code Clone Analysis”, Proc. of the 8th IEEE International Symposium on Software Metrics, 67-76, 2002.
フィードバックから得られた情報 • クローン情報の利用法 • リファクタリング支援 • 互いにクローンになっているものを1つにまとめる • 設計情報との一貫性確認 • 設計上説明できるクローンであるか • 問題点 • 検出されたクローンの数が多すぎてどれをみるべきかわからない • 調査の関心がないクローン(定型処理など)が含まれていて,どのクローンから見ていけばいいかわからない クローンの特徴を知る必要性
目的とアプローチ • 目的: クローン分析の効率化 • クローンを特徴に基づいて分類する • 分類を使って,調査の関心がないクローン(定型処理など)をフィルタする • アプローチ: メトリクスを用いてクローンを分類 • 以下のメトリクスを用いる • RAD: クローン間の遠さの尺度 • NIF: クローンが存在するファイルの数 • RNR: クローン内の重複した処理の少なさの度合い
RAD: クローン間の遠さの尺度 • RAD: 与えられたクローンセットの各要素を含むファイルから共通の親ディレクトリまでの距離の最大値 • RAD = 2 • 例:RAD = 1 2 1 ファイルC ファイルA ファイルB は,クローンを表す ファイルA ファイルB
NIF: クローンが存在するファイルの数 • NIF: 与えられたクローンセットの各要素を含むファイルの数 • 例:NIF = 2 • NIF = 3 ファイルC ファイルA ファイルB 2 ファイルA ファイルB 3 は,クローンを表す
RNR: クローン内の重複した処理の少なさの度合い ∑ Tokensrepeated(C) C ∈ S RNR = 1 - ∑ S: クローンセット C: クローンセットの要素 Tokensall(C) C ∈ S Tokensall(C) : クローンC中の総トークン数 Tokensrepeated(C) : クローンC中の繰り返し部分のトークン数 8トークン Tokensall = 8 X A B A B A B Y Tokensrepeated = 4 4トークン 直前の繰り返し
クローンの分布に着目した分類方法 • RAD,NIFは,クローンの分布の特徴を表すメトリクス • 値の高低により,クローンを4つに分類する RAD 高 vertical global local horizontal 低 低 高 NIF
カテゴリ: local • クローンがディレクトリ階層上近い少数のファイルに存在する RAD 高 vertical global local horizontal 低 は,クローンを表す 低 高 NIF
カテゴリ: horizontal • クローンがディレクトリ階層上近い多数のファイルに存在する RAD 高 vertical global local horizontal 低 は,クローンを表す 低 高 NIF
カテゴリ: vertical • クローンがディレクトリ階層上遠くの少数のファイルに存在する RAD 高 vertical global local horizontal 低 低 高 NIF は,クローンを表す
カテゴリ: global • クローンがプログラム広範囲の多数のファイルに存在する RAD 高 vertical global local horizontal 低 低 高 NIF は,クローンを表す
実験 • 目的: RAD,NIF,RNRに着目して分類した各カテゴリに含まれるクローンの特徴を調べる • Java,C,C++で書かれたオープンソースソフトウェアについて分類を行った • CCFinderにより検出された30トークン以上のクローンセットを対象 • 人の手によって確認 • Java: Ant,Webmail,httpunit,Art of Illusion • C: Small Device C Compiler,Sketch • C++ : CppUnit,SWIG • 総ファイル数: 2948個 • 総クローンセット数(30トークン以上): 14874個 • Ant • 総ファイル数: 954個 • 総クローンセット数(30トークン以上): 1643個
クローンの特徴を表す指標 • クローンが含まれる関数の名前を用いて指標を定義する • クローンがどのような関数に存在するかで分類 • same: 同じ名前の関数内に存在するクローン • similar: 名前の似た関数内に存在するクローン • 名前が似ている: 関数名の一部に共通した単語を持つ • 例) addConfiguredInputMapper addConfiguredOutputMapper addConfiguredErrorMapper • different: 異なる関数内に存在するクローン • クローンセットが各指標に該当するか調べることで分類したカテゴリを評価する • 複数の指標に該当することを許す
評価条件 • カテゴリ分けでのRAD,NIFの高低の閾値はそれぞれの最大値の半分とした • RNRについては次のように分割した • RNR低: 0 ≦ RNR ≦ 0.3 • RNR中: 0.3 < RNR ≦ 0.7 • RNR高: 0.7 < RNR ≦ 1 低 中 高
RNRに着目した特徴 • RNRの低いクローンは単純なロジックのクローンであった(繰り返しの多いクローン) public static String getMethodAccess(int access_flags) { StringBuffer sb = new StringBuffer(); if (isPublic(access_flags)) { sb.append("public "); } else if (isPrivate(access_flags)) { sb.append("private "); } else if (isProtected(access_flags)) { sb.append("protected "); } if (isFinal(access_flags)) { sb.append("final "); } if (isStatic(access_flags)) { sb.append("static "); } if (isSynchronized(access_flags)) { sb.append("synchronized "); } 全体が1つのクローン 直前の繰り返し
Antでのクローンセットの分布 RAD 高 要素数が3以上のクローンセットについて調べた(検出されたクローンセットの約35%) vertical 33個 global 7個 local 530個 horizontal 11個 低 低 高 NIF
カテゴリ: local の結果 • same,similarに該当するクローンが多い • あるデータ構造の要素に対して処理を行う各メソッド内にクローンが見られた • 同じデータ構造を用いている
カテゴリ: localに属するクローンの例 public int getClassEntry(String className) { int index = -1; for (int i = 0; i < entries.size() && index == -1; ++i) { Object element = entries.elementAt(i); if (element instanceof ClassCPInfo) { ClassCPInfo classinfo = (ClassCPInfo) element; if (classinfo.getClassName().equals(className)) { index = i; } } } return index; } 同じクローンセットに含まれるメソッドの例として, getConstantEntry getMethodRefEntry などが存在した
カテゴリ: horizontal の結果 • sameに該当するクローンが多い • 例えば他のプログラムを実行するメソッドにクローンが見られた • 引数ごとに異なるメソッドとして実装されている
カテゴリ: horizontalに属するクローンの例 public void execute() throws BuildException { Commandline commandLine = new Commandline(); Project aProj = getProject(); int result = 0; if (getViewPath() == null) { setViewPath(aProj.getBaseDir().getPath()); } commandLine.setExecutable(getClearToolCommand()); commandLine.createArgument().setValue(COMMAND_CHECKIN); checkOptions(commandLine); if (!getFailOnErr()) { getProject().log("Ignoring any errors that occur for: " + getViewPathBasename(),Project.MSG_VERBOSE); } result = run(commandLine); if (Execute.isFailure(result) && getFailOnErr()) { String msg = "Failed executing: " + commandLine.toString(); throw new BuildException(msg, getLocation()); } } 外部プログラムの各機能を呼び出すメソッド中に クローンが存在した
カテゴリ: vertical の結果 • differentに該当するクローンが多い • 類似した構造のパッケージにクローンが見られた
カテゴリ: vertical に属するクローンの例 while (classEnum.hasMoreElements()) { String className = (String) classEnum.nextElement(); log(" Class " + className + " affects:", Project.MSG_DEBUG); Hashtable affectedClasses = (Hashtable) affectedClassMap.get(className); Enumeration affectedClassEnum = affectedClasses.keys(); while (affectedClassEnum.hasMoreElements()) { String affectedClass = (String) affectedClassEnum.nextElement(); ClassFileInfo info = (ClassFileInfo) affectedClasses.get(affectedClass); log(" " + affectedClass + " in " + info.absoluteFile.getPath(), Project.MSG_DEBUG); } main testcases ant ant taskdefs taskdefs
カテゴリ: global の例 • differentに該当するクローンがほとんどである • 定型処理に該当するクローンが見られた
カテゴリ: global に属するクローンの例 } catch (IOException ioex) { throw new BuildException("Error while concatenating: " + ioex.getMessage(), ioex); } finally { if (reader != null) { try { reader.close(); } catch (IOException ignore) { // ignore } } if (os != null) { try { os.close(); } catch (IOException ignore) { // ignore } } } } クローンが存在しているメソッドは ストリームを閉じる処理を2回続けて 行う内容であった
考察 • 異なるソフトウェアでも,同じカテゴリに分類されたクローンに同じ特徴が見られた • ソフトウェアの記述言語を問わない • メトリクス値に基づいてクローンの特徴を抽出可能 • 各カテゴリの特徴を利用することでクローンをフィルタすることができる • 例1) globalに属するクローンは定型処理であるので,リファクタリング対象にならない • 例2) verticalに属するクローンは設計情報との一貫性を確認することが有益 • 他のパッケージからのアドホックなコピーの恐れ • 例3) RNRが低いクローンは単純なロジックの繰り返しであるので利用しにくい • 単純な代入文の連続など • リファクタリングの対象にならない
まとめと今後の課題 • まとめ • コードクローン分析の効率化を支援するコードクローンの特徴抽出手法の提案と評価 • 今後の課題 • 分類の詳細化 • ツールとしての実装 • 実際の開発現場での適用実験と評価