コードクローン検出技術と その利用法 神谷年洋‡, 植田泰士†, 肥後芳樹†, 楠本真二†, 井上克郎†

Slides:



Advertisements
Similar presentations
Software Engineering Laboratory, Department of Computer Science, Graduate School of Information Science and Technology, Osaka University 保守支援を目的とした コードクローン情報検索ツール.
Advertisements

シーケンス図の生成のための実行履歴圧縮手法
背景 ソフトウェアの大規模化・複雑化 生産性と品質の向上 ↓ オブジェクト指向分析設計の適用 開発ツールの投入.
コードクローン履歴閲覧環境を用いたクローン評価の試み
TF-IDF法とLSHアルゴリズムを用いた 関数単位のコードクローン検出法
AGMアルゴリズムを用いた ギャップを含むコードクローン情報の生成
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
コードクローン問題に 対処する技術の動向 (コードクローン入門)
剽窃 他人の作品や論文を盗んで,自分のものとして発表すること. プログラムが剽窃される事例もある. Aさんのプログラム Xさんのプログラム
研究の背景 コードクローン ソースコード中に存在する一致または類似したコード片
プログラム変更支援を目的とした コードクローン情報付加手法の提案と実装
大規模収集データに基づいた ソフトウェアエンジニアリング
大規模開発データの収集と エンピリカルソフトウェア工学
川口真司 松下誠 井上克郎 大阪大学大学院情報科学研究科
プログラム実行履歴を用いたトランザクションファンクション抽出手法
アイテムセットマイニングを利用した コードクローン分析作業の効率向上
大規模ソースコード集合を対象とした 類似関数集合群の抽出
コードクローン分析ツールGeminiを用いたコードクローン分析手順
コードクローンの分布情報を用いた特徴抽出手法の提案
ギャップを含むコードクローンの フィルタリング手法の提案
コードクローンに含まれるメソッド呼び出しの 変更度合の分析
コードクローンに含まれるメソッド呼び出しの 変更度合の調査
識別子の共起関係に基づく類似コード検索法の提案と 欠陥検出への適用
ソードコードの編集に基づいた コードクローンの分類とその分析システム
コードクローンの集約によるリファクタリング支援システムの提案と実装
ネットワーク共生環境を築く情報技術の創出 テーマ4:高信頼性・高安全性を有するネットワーク共生環境の構築技術の創出
オブジェクト指向プログラミング言語に対応した コードクローン検出技法の提案と実験
動的依存グラフの3-gramを用いた 実行トレースの比較手法
構文定義記述を用いた 多言語対応コードクローン検出ツールの改善
動的スライスを用いたバグ修正前後の実行系列の差分検出手法
オブジェクト指向メトリクスを用いた 開発支援法に関する研究
ソフトウェア制作論 平成30年10月3日.
クローン検出ツールを用いた ソースコード分析ツールの試作
コードクローン検出ツールを用いた ソースコード分析システムの試作と プログラミング演習への適用
リファクタリング支援のための コードクローンに含まれる識別子の対応関係分析
ソースコードの特徴量を用いた機械学習による メソッド抽出リファクタリング推薦手法
コードクローンの動作を比較するためのコードクローン周辺コードの解析
コードクローンに対する一貫性のない変更に起因する欠陥の検出
Token Comparison Approach to Detect Code Clone-related Bugs
柔軟に変更可能な字句解析機構を持つ コードクローン検出ツールの開発
ソースコード縮退による ソースコード理解 神谷年洋 科学技術振興事業団 さきがけ研究21 オブジェクト指向シンポジウム2003.
プログラム実行履歴を用いたコードクローン検出手法
コードクローン検出に基づくデザイン パターン適用支援手法の提案と実現
バイトコードを単位とするJavaスライスシステムの試作
コードクローン編集者数に着目した開発履歴の分析
コード片に共通した特性を自動抽出する ソースコード閲覧ツールの試作
多様なプログラミング言語に対応可能な コードクローン検出ツール CCFinderSW
Geminiを用いた効果的なコードクローン分析方法
○ 後藤 祥1,吉田 則裕2 ,井岡 正和1 ,井上 克郎1 1大阪大学 2奈良先端科学技術大学院大学
ソフトウェア保守のための コードクローン情報検索ツール
コードクローン分類の詳細化に基づく 集約パターンの提案と評価
コーディングパターンの あいまい検索の提案と実装
コードクローン間の依存関係に基づく リファクタリング支援環境の実装
コードクローンの分布情報を用いた特徴抽出手法の提案
第7回OACISシンポジウム 大阪大学におけるソフトウェア工学研究と産学連携活動
大阪大学 大学院情報科学研究科 コンピュータサイエンス専攻 井上研究室
コードクローン間の依存関係に基づく リファクタリング支援手法の提案と実現
クローン検出ツールを用いた ソフトウェアシステムの類似度調査
オープンソースソフトウェアに対する コーディングパターン分析の適用
コードクローン間の依存関係に基づく リファクタリング支援手法の提案と実現
統合開発環境のための プログラミング言語拡張 フレームワーク
欠陥検出を目的とした類似コード検索法 吉田則裕,石尾隆,松下誠,井上克郎 大阪大学 大学院情報科学研究科
容易に使用可能な grep風コードクローン検索ツール
ソフトウェア理解支援を目的とした 辞書の作成法
コードクローン解析に基づく デザインパターン適用候補の検出手法
Javaとは Javaとはオブジェクト指向言語でJava VM(Java仮想マシン)と呼ばれるプログラム上で動作します。
Geminiを用いた効果的なコードクローン分析方法
オブジェクト指向言語における セキュリティ解析アルゴリズムの提案と実現
コードクローンを対象とした リファクタリングの有効性に関する調査
Presentation transcript:

コードクローン検出技術と その利用法 神谷年洋‡, 植田泰士†, 肥後芳樹†, 楠本真二†, 井上克郎† †大阪大学 大学院情報科学研究科 {y-ueda, y-higo, kusumoto, inoue}@ist.osaka-u.ac.jp ‡科学技術振興事業団 さきがけ研究21 kamiya@ist.osaka-u.ac.jp

ツールCCFinder / Gemini / CCShaper コードクローンとは コードクローン検出と開発プロセス コードクローン検出手法 ツールCCFinder / Gemini / CCShaper アルゴリズム 適用事例 2002/11/15

コードクローンとは ソースコード中に類似したコード片があるとき, それらをコードクローンという クローンクラス クローンペア クローンが生じる理由:コピー&ペースト,定型処理,意図的な繰り返し,偶然,プログラミング言語に適切な機能がないため,など 2002/11/15

コードクローンが引き起こす問題 コードクローンはソフトウェア保守を困難にする コードクローンにフォールトが存在する場合,すべてのコードクローンについて修正を行わなければならない 機能追加も同様 大規模なソースコードからコードクローンを効率よく検出する方法が必要 2002/11/15

コードクローン検出と開発プロセス 修正前チェック リファクタリング 定型処理(イディオム)抽出 修正しようとする部分にコードクローンがあるかチェックする → すべて修正 リファクタリング コードクローンを検出する → まとめる 「メソッドの抽出」「メソッドの引き上げ」 定型処理(イディオム)抽出 よく使われるコード断片を抽出 → 部品化 2002/11/15

コードクローン検出のその他の応用 ソースコードの剽窃をチェックする バージョン間の変化を調べる 定量的な評価 よりよいdiffとして 2002/11/15

既存のコードクローン検出手法(1/2) Bakerの手法 Baxterらの手法 行単位でソースコードを比較してコードクローンを検出する C言語のソースファイルを入力,構文解析して,解析木(の部分木)を比較する [Baker1995] B. S. Baker: “On finding Duplication and Near-Duplication in Large Software System,” Proc. Second IEEE Working Conf. Reverse Eng., pp. 86-95, Tronto, Canada (Jul., 1995). [Baxter1998] I. D. Baxter, A. Yahin, L. Moura, M. Sant’Anna, and L. Bier: “Clone Detection Using Abstract Syntax Trees,” Proc. of ICSM ’98, pp. 368-377, Bethesda, Maryland (Nov., 1998). 2002/11/15

既存のコードクローン検出手法(2/2) Merloらの手法 手続き(メソッドや関数)の特徴メトリクスを比較して,計測値が似ていればコードクローンであると判定する [Balazinska1999] M. Balazinska, E. Merlo, M. Dagenais, B. Lague, and K.A. Kontogiannis, "Measuring Clone Based Reengineering Opportunities", Proc. 6th IEEE Int'l Symposium on Software Metrics (METRICS '99), pp. 292-303, Boca Raton, Florida, Nov. 1999. 2002/11/15

CCFinder: コードクローン検出ツール ソースコードを字句解析してトークンの列に直してからコードクローン検出を行う (浅い)文法知識に基づいたソースコード変形 クラススコープや名前空間による複雑な名前の正規化を行う 初期化テーブルを取り除く モジュールの区切りを認識する 言語依存部分を取り替えることで,さまざまなプログラミング言語に対応 2002/11/15

コードクローン検出手順 (1)ソースコードを入力し,トークンの列にする (2)変形ルールにより,トークン列を変形する (3)パラメータ置換を行う (4)マッチングアルゴリズムによりコードクローンを検出する (5)コードクローンの位置(ファイル,行, カラム)を出力する 2002/11/15

例題ソースコード 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) 6. if (pat.match(a[i])) 7. sum += Sample.parseNumber(pat.getParen(0)); 8. System.out.println("sum = " + sum); 9. } 10. static void goo(String [] a) throws RESyntaxException { 11. RE exp = new RE("[0-9,]+"); 12. int sum = 0; 13. for (int i = 0; i < a.length; ++i) 14. if (exp.match(a[i])) 15. sum += parseNumber(exp.getParen(0)); 16. System.out.println("sum = " + sum); 17. } 2002/11/15

ステップ(1):ソースコードを入力し,トークンの列にする 2002/11/15

ステップ(2):変形ルールにより,トークン列を変形する 2002/11/15

ステップ(3):パラメータ置換を行う 2002/11/15

マッチングアルゴリズムにより,コードクローンを検出 ステップ(4): マッチングアルゴリズムにより,コードクローンを検出 2002/11/15

マッチングアルゴリズムにより,コードクローンを検出 ステップ(4): マッチングアルゴリズムにより,コードクローンを検出 2002/11/15

ステップ(5):コードクローンの位置を出力する 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) 6. if (pat.match(a[i])) 7. sum += Sample.parseNumber(pat.getParen(0)); 8. System.out.println("sum = " + sum); 9. } 10. static void goo(String [] a) throws RESyntaxException { 11. RE exp = new RE("[0-9,]+"); 12. int sum = 0; 13. for (int i = 0; i < a.length; ++i) 14. if (exp.match(a[i])) 15. sum += parseNumber(exp.getParen(0)); 16. System.out.println("sum = " + sum); 17. } 2002/11/15

変形ルールを用いない場合 4-6行目と 13-15行目, 8-10行目と 17-19行目 1. static void foo() throws RESyntaxException { 2. String a[] = new String [] { "123,400", "abc", "orange 100" }; 3. org.apache.regexp.RE pat = new org.apache.regexp.RE("[0-9,]+"); 4. int sum = 0; 5. for (int i = 0; i < a.length; ++i) { 6. if (pat.match(a[i])) 7. sum += Sample.parseNumber(pat.getParen(0)); 8. } 9. System.out.println("sum = " + sum); 10. } 11. static void goo(String [] a) throws RESyntaxException { 12. RE exp = new RE("[0-9,]+"); 13. int sum = 0; 14. for (int i = 0; i < a.length; ++i) { 15. if (exp.match(a[i])) 16. sum += parseNumber(exp.getParen(0)); 17. } 18. System.out.println("sum = " + sum); 19. } 変形ルールを用いなかった場合には,行3と行12,行7と行16が異なった行であると判定されるため,検出されるクローンはいずれも3行以下のクローンになります. 2002/11/15

Java向けの変形ルール RJ1:パッケージ名除去 ( PackageName ‘.’ )+ ClassName → ClassName RJ2:Callee挿入 NDotOrNew NClassName ‘(‘ → NDotOrNew CalleeIdentifier ‘.’ NClassName ‘(‘ RJ3:テーブル初期化除去 '=' '{' InitalizationList, '}' → '=' '{' UniqueIdentifier '}‘ ']' '{' InitalizationList, '}' → ']' '{' UniqueIdentifier '}’ RJ4:モジュールの分離 トップレベルの定義や宣言の終わりにUniqueIdentifier を挿入する RJ5:可視性キーワードの除去 protected, public,synchronizedなどのキーワードを取り除く RJ6:コンパウンド・ブロックの処理 if (…), else, while (…) の直後が単文なら{}を補う 2002/11/15

適用例#1 JDKのライブラリ JDK(Java Development Kit) 1.2.2(サンプルとデモプログラムを除く) 入力ファイルは1648個,約50万行 ツールの実行には,Pentium III 650MHzおよび1GBのRAMを持つPCで約3分を要した 2002/11/15

JDKのコードクローン散布図 両軸はソースファイルを辞書順に並べたもの 20行以上のコードクローンを図示 多くのコードクローンが密集している(A) 最長のコードクローン(B) B A 2002/11/15

コードクローンが密集している部分(A) src/javax/swing/plaf/multi/*.java(29個) クラス名を除いてまったく同じクラスの定義 コード生成ツールによって生成された 31| */ 32| public class MultiButtonUI extends ButtonUI { 33|   160| public static ComponentUI createUI(JComponent a) { 161| ComponentUI mui = new MultiButtonUI(); 162| return MultiLookAndFeel.createUIs(mui, 163| ((MultiButtonUI) mui).uis, 164| a); 165| } 2002/11/15

最長のコードクローン(B) 最長のコードクローン(349行) src/java/util/Arrays.javaの18の“sort”メソッド シグネチャ(引数の型と数)が異なる アルゴリズムは同一 2002/11/15

適用例#2 FreeBSD, Linux, NetBSDの比較 3つのOSの比較 FreeBSD 4.0 (C 220万行) Linux 2.4.0 (C 240万行) NetBSD 1.5 (C 260万行) FreeBSDとNetBSDは同じソースコードから,Linuxは異なるソースコード 実行には108分を要した 2002/11/15

FreeBSD, Linux, NetBSDのコードクローン散布図 2002/11/15

FreeBSDとLinuxのコードクローン ドライバ部分に多くのクローン「ファイル」が存在する 共通のソースから分岐したソースファイル 名前が付け替えられたソースファイル あるソースファイルを複数のファイルに分割している → 2002/11/15

大学の演習(1) Pascal風言語で書かれたコードをCASLに変換するコンパイラ C言語で開発 教科書,指導書を参考にして作成 課題1:構文チェッカ, 課題2:意味チェッカ, 課題3:コンパイラ,の順に作成 課題2と課題3では直前の課題で作成したプログラムを拡張 27人分のソースコード(P1~P27)を利用 次に,実験の概要について説明します。 データはある年度の情報科学演習Dにおいて作成されたプログラムを収集 しました。 この演習では,Pascal風言語で書かれたコードをCASLに変換するコンパイラをC言語で開発します。 教科書,指導書を参考にして作成していき,課題1では構文チェッカを,課題2では意味チェッカを課題3ではコンパイラの順に作成していきます。 課題2では課題1の課題3では課題2で作成されたプログラムを拡張していきます。 27人のソースコードをP1」からP27として入力データとします。 そしてDupSに適用し一致するコード片を検出した後,結果を分析します。 2002/11/15

大学の 演習(2) P1 P2 P3 P4 P5 P1 P2 P3 P4 P5 2002/11/15

大学の演習(4) 拡張時における利用割合による分析 関数単位の利用のされ方による分類 拡張時の利用度が高いP17(71%)と低いP5(39%)について調査 P17では課題2で作成したプログラムを関数単位でうまく利用している      → 課題2における設計がしっかりしていたと評価できる P5では何らかの理由で大幅な作り変えが行われた                  → 原因を調査し対処方法を指導に盛り込む 課題2から課題3に拡張する時際の,利用度が高いP17と利用度の低いP5 について調べた結果です。 P17とP5について,その課題2のプログラムの関数について,そのまま利用された関数,修正されて利用された関数,全く再利用されていない関数にわけて集計し円グラフにしました。 P17では64%の関数がそのまま課題3で使用されました。 これより,P17では課題2で作成したプログラムを関数単位でうまく利用していることが言え,課題2における設計がしっかりしていたと評価できます。 またP22では,69%の関数が利用されず,何らかの理由で作り変えが行われたと思われ,原因を調査し対処方法を指導に盛り込む事が考えられます。 2002/11/15

ツールGemini / CCShaper インタラクティブな環境を提供(Gemini) 統計的な分析(Gemini) スキャッタープロット表示,ズーム,ソート 統計的な分析(Gemini) メトリクスグラフ Gappedクローン検出(Gemini) コピー&ペースト&修正されたコード断片 CCFinderの出力から意味的なまとまりを取り出す(CCShaper) 2002/11/15

Gemini: コードクローン分析環境 CCFinderの出力はテキストベース ソースコード分析を補助するツール 分析が大変 直感的,対話的な操作 統計分析機能 2002/11/15

Gappedクローン検出 コピー&ペーストの後,追加や削除が行われる 追加や削除された部分=ギャップ Gappedクローン検出機能により,そのような場合でもコードクローンとして検出することができる 2002/11/15

Gappedクローンの例 Geminiで検出されたGappedクローン 赤い文字の部分がギャップ 543| else { 544| t = NULL_STRING; 545| spaces.push_back(t); 546| errors.push_back(pos); 547| append_recover_to_last_space(); 548| state = MS_INIT; 549| } 550| 551| bool bFirstTrial = true; 552| bool bGotAvailToken = false; 553| while (! bGotAvailToken && pos < text.size()) { 554| if (get_word(&t, SpecialPrevChar)) { 340| else { 341| t = ""; 342| //pool_thru(&pool, &t); 343| spaces.push_back(t); 344| errors.push_back(pos); 345| append_recover_to_last_space(); 346| } 347| 348| bool bFirstTrial = true; 349| bool bGotAvailToken = false; 350| while (! bGotAvailToken && pos < text.size()) { 351| if (get_word(&t)) { Geminiで検出されたGappedクローン 赤い文字の部分がギャップ 2002/11/15

CCShaper: 「絞り込み」ツール CCFinderが検出するコードクローンはトークンの並び 必ずしも意味的な固まり(クラス,メソッド,etc…)にはなっていない リファクタリング(「メソッドの抽出」や「メソッドの引き上げ」)の対象としては不適当なコードクローンが多く含まれる 適切なフィルタリング 2002/11/15

メソッドの抽出 Void methodA(int i){ void methodA(int i){ methodZ(); System.out.println(“name:” + name); System.out.println(“amount:” + i); } Void methodB(int i){ methodY(); void methodA(int i){ methodZ(); methodC(i); } void methodB(int i){ methodY(); Void methodC(int i){ System.out.println(“name:” + name); System.out.println(“amount:” + i); methodC(i); methodC(i); 2002/11/15

メソッドの引き上げ クラスA クラスB クラスC  メソッドA クラスA クラスB クラスC  メソッドA 2002/11/15

CCFinderで検出されるコードクローン(例1) righttokennumber = c.getEndNumber() - c.getStartNumber() + 1; } string getLeftClone() const { char temp[STRLENGTH]; snprintf(temp,STRLENGTH, "%s\t%d,%d,%d\t%d,%d,%d\t",leftID.c_str(), leftstartline,leftstartcolumn,leftstartnumber, leftendline,leftendcolumn,leftendnumber); string clone(temp); return clone; string getRightClone() const return clone; } string getRightClone() const { char temp[STRLENGTH]; snprintf(temp,STRLENGTH, "%s\t%d,%d,%d\t%d,%d,%d\t",rightID.c_str(), rightstartline,rightstartcolumn,rightstartnumber, rightendline,rightendcolumn,rightendnumber); string clone(temp); int getLeftTokenNumber() const return lefttokennumber; の部分を検出してほしい 2002/11/15

CCFinderで検出されるコードクローン(例2) if(func->parameter!=NULL) return error("Error:Parameter Exist"); curVertex->kind=ST_call; lp=makeexptree(NULL,NULL,SIDENTIFIER,idtemp,SPROCEDURE); curVertex->tree=lp; curVertex->refer=search_tree_postorder(lp); curVertex->refer=uniq_RDlist(curVertex->refer); cutSt(); } break; case 66: #line 314 "subPascalParse.y" {struct FUNCTION *func; struct RD *para; lp=makeexptree(&lp,NULL,SRPAREN,NULL,SNULL); rp=makeexptree(NULL,&lp,SIDENTIFIER, idtemp,SPROCEDURE); curVertex->kind=ST_call; curVertex->tree=rp; curVertex->refer=search_tree_postorder(rp); curVertex->refer=uniq_RDlist(curVertex->refer); cutSt(); } break; case 67: #line 355 "subPascalParse.y" {yyval.exptree = yyvsp[0].exptree;} CCFinderは の部分を検出 しかし,このコードクローンはリファクタリングの対象にはならない 2002/11/15

CCShaperの アプローチ コードクローンの存在するソースコード(Java)を構文解析する 構文解析結果とCCFinderの出力から意味的なまとまり(ブロック構造)を抽出する コードクローン検出(CCFinder) 絞り込み(CCShaper) 表示(Gemini)   意味的にまとまりのあるコードクローン コードクローン 2002/11/15

適用実験その1(ANTLR)(1/5) ソースファイルは239個 総行数は約44000行 数値による結果比較 388574 984 1072 CCShaper無し CCShaperあり クローンペア数 388574 984 クローンクラス数 1072 148 解析時間:約2分 2002/11/15

適用実験その1(ANTLR)(2/5) スキャタープロット図による比較 CCShaper無し CCShaper有り a 2002/11/15

適用実験その1(ANTLR)(3/5) 選択範囲のコードクローン public final void mOPEN_ELEMENT_OPTION(boolean _createToken) throws RecognitionException, CharStreamException, TokenStreamException {    int _ttype;    Token _token=null;    int _begin=text.length();    ttype = OPEN_ELEMENT_OPTION;    int _saveIndex;    match('<');    if ( _createToken && _token==null && _ttype!=Token.SKIP ) {       _token = makeToken(_ttype);       _token.setText(new String(text.getBuffer(), _begin, text.length()-_begin));    }    _returnToken = _token; } 2002/11/15

適用実験その1(ANTLR)(4/5) 引数を2つ増やすことで1つのメソッドにまとめることができる 発見されたコードクローンの検証 異なっていたのは の部分のみ このコードクローンが20箇所に現れていた すべてのコードクローンは同じクラスのメソッドであった 引数を2つ増やすことで1つのメソッドにまとめることができる 2002/11/15

適用実験その1(ANTLR)(5/5) クラス図による比較 現状 ANTLRLexer クラス図による比較 現状 ANTLRLexer mOPEN_ELEMENT_OPTION(boolean _createToken) mCLOSE_ELEMENT_OPTION(boolean _createToken) mCOMMA(boolean _createToken) mQUESTION(boolean _createToken) mTREE_BEGIN(boolean _createToken) mLPAREN(boolean _createToken) mRPAREN(boolean _createToken) mCOLON(boolean _createToken) mSTAR(boolean _createToken) mPLUS(boolean _createToken) mASSIGN(boolean _createToken) mIMPLIES(boolean _createToken) mSEMI(boolean _createToken) mCARET(boolean _createToken) mBANG(boolean _createToken) mOR(boolean _createToken) mWILDCARD(boolean _createToken) mRANGE(boolean _createToken) mNOT_OP(boolean _createToken) mRCURLY(boolean _createToken) : Other methods (22個) 2002/11/15

適用実験その1(ANTLR)(5/5) クラス図による比較 リファクタリングした場合 ANTLRLexer クラス図による比較 リファクタリングした場合 ANTLRLexer mTOKEN(boolean _createToken,int i,char c) : Other methods(22個) 2002/11/15

まとめ コードクローン検出 ツールCCFinder / Gemini / CCShaper ソフトウェア保守 ソースコードを調べるための手段 より広範囲な意味での(Gapped)クローンを検出する 絞り込みによって,利用目的に応じたコードクローンを提示する 2002/11/15

END 2002/11/15