34
任意粒度機能モデルに基づく 動的型付けプログラミング言語向け ソースコード検索手法の提案 神谷年洋, はこだて未来大学 [email protected] SIGSS 20151月研究会 於 鳥取 2015-01-27 神谷年洋, “任意粒度機能モデルに基づく動的型付けプログラミング言語向けソースコー ド検索手法の提案”, 電子情報通信学会技術研究報告書, vol. 114, no. 415, pp.121- 126.

任意粒度機能モデルに基づく動的型付けプログラミング言語向けソースコード検索手法の提案

Embed Size (px)

Citation preview

任意粒度機能モデルに基づく動的型付けプログラミング言語向け

ソースコード検索手法の提案

神谷年洋, はこだて未来大学[email protected]

SIGSS 2015年1月研究会 於 鳥取 2015-01-27

神谷年洋, “任意粒度機能モデルに基づく動的型付けプログラミング言語向けソースコード検索手法の提案”, 電子情報通信学会技術研究報告書, vol. 114, no. 415, pp.121-

126.

2

発表内容

今回はいわゆる「ツールデモペーパー」になっています– 2013年10月の発表の拡張であり、原稿の方はその発表を

前提に– 発表の構成は原稿とは変えてあります

(1) コード検索とは

(2) 小さなサンプルに対する適用(使い方)…3節

(3) アルゴリズム …4節

(4) より大きな例に対する適用例 …5節

(5) 議論 …6節

3

ソースコードを検索する様々な場面 再利用可能な機能を持つコードを探す

「この機能って誰か作ってないかな?」(再利用可能なコンポーネントになっているとは限らず、ベタ書きの可能性も) APIの利用例を探す

「Aの典型的な使い方は?」「AとBを組み合わせて使ってる例はないかな?」 類似したコードを探す

ある修正を同じようなコードの全てに行いたい 特定の実行に対応する場所を探す(デバッグ)

「このエラーメッセージはどこで作られたんだ?」「このボタンを押したときの処理ってどこ?」

4

…およびそれらを支援する手法

● 鷲崎 , 深澤 , “ 有向置換性類似度に基づくコンポーネント検索方式の実現と評価 ,” 情報処理学会論文誌 , Vol.43, No.6,pp.1638-1652, 2002.

● 横森ら , “Java ソフトウェア部品検索システム SPARS-J,” 電子情報通信学会論文誌 D-I, Vol.J87-D-I, No.12, pp.1060-1068, 2004.

● S. Thummalapenta, Tao Xie, “Parseweb: a programmer assistant for reusing open source code on the web,” Proc. the 22nd IEEE/ACM Int’l Conf. Automated Softw. Eng.(ASE’07), pp. 204-213, 2007.

● 泉田ら, “ ソフトウェア保守のための類似コード検索ツール ,” 電子情報通信学会論文誌 , Vol.J86-D-I, No.12, pp.906-908, 2003.

● D. Poshyvanyk, A. Marcus, “Combining Formal Concept Analysis with Information Retrieval for Concept Location in Source Code,” Proc. 15th IEEE Int’l Conf. Program Comprehension(ICPC ’07), pp.37-48, 2007.

5

提案手法

プログラムの実行トレース内から、検索キーワードが含まれる部分列を探す

入力1: 対象プログラム(→ 実行トレース)

入力2: 検索キーワード– 関数やメソッドの名前– 型の名前– 整数や文字列の値(実引数や戻り値、リテラル)

出力: 検索キーワードを含むコールツリーの部分グラフ→ 利用者になるべく小さな部分列を提示するため、ツリーの構造を考慮したコンパクト化を行う

6

想定する利用場面

前述の「様々な場面」のうち、

再利用可能な機能を持つコードを探す

APIの利用例を探す(例) 複数のメソッド名から、それらが同時に使われている例

を探す

類似したコードを探す

特定の実行に対応する場所を探す(デバッグ)(例) 与えられた文字列をファイルに書き込んでいる部分を

探す● 定数(リテラル)に限らず

8

デモ: 準備

(1) 対象プログラムを実行して実行トレースを取得

(2) 検索のインデックスを作成(インデックスのデータ構造については後述)

対象プログラムと引数実行トレースやインデックスを格納するディレクトリ

実行結果は通常の実行と同じ

9

デモ: 1回目の検索

目的: 対象プログラムが「2015-01-21」という文字列を受け取って結果を印字する実行の全体像を調べたい

検索キーワード: v.2015-01-21 と f.write

検索結果: – 関数mainの中で、文字列 “2015-01-21” を引数として メソッド

CalendarUtil.get_dayを呼び出す …①– 同mainの中で、何らかのファイルに “Wed” を印字(write) …②

– paodate/Dateという型が利用されていることが判明 …①

「v.」は値、「f.」は関数名/メソッド名

10

フォーマットの説明

● 手続きの呼び出し

– 呼び出しの入れ子関係はインデントで表現

(returnも例外送出によるものも)

呼び出された手続き 呼び出した場所: 実引数の値 …> 戻り値

● 手続き– パッケージ名/クラス名/メソッド名– パッケージ名//関数名

● 値– 文字列、数値 → 値を表現する文

字列– それ以外のオブジェクト → <型>

手続きや値は検索キーワードにマッチすると赤で印字される

11

デモ: 2回目の検索

目的: 1回目の検索で見つかった型Dateのオブジェクトがどこで生成されたか知りたい

検索キーワード: f.Date/__init__ と v.2015-01-21– 「__init__」はコンストラクタの名前(プログラミング言語の規約)– (f.Date/__init__ だけだと関係ない検索結果も出てきてしまうので、

前回の v.2015-01-21 も追加)

検索結果: メソッド CalendarUtil.get_day の中でこのコンストラクタを呼び出していた

「%Y-%m-%d」というそれらしいフォーマットが渡されている → このオブジェクトが文字列を時間データに変換するなど、

プログラムの重要な役割を担っている?(推測)

12

デモ: 3回目の検索

目的: 型Dateのオブジェクトに対する操作がどこで行われているかを調べる

検索キーワード: t.Date

検索結果: オブジェクトの生成以外にも、引き算(メソッド__sub__の呼び出し)が行われていた …③

「t.」は型名

13

デモ: 4回目の検索

目的: (これまでの検索結果を踏まえ)型Dateのオブジェクトに対する操作を行い、その結果何かを印字するという実行系列の全体像を見たい

検索キーワード: f.Date/__init__, f.Date/__sub__, f.write

検索結果: 型Dateのオブジェクトを直接操作しているのは– メソッド CalendarUtil.get_day と …④– メソッド CalenderUtil.get_day_of_week である …⑤– それら2つのメソッドを、関数mainが呼び出している …⑥

14

デモ: 4回目の検索

目的: (これまでの検索結果を踏まえ)型Dateのオブジェクトに対する操作を行い、その結果何かを印字するという実行系列の全体像を見たい

検索キーワード: f.Date/__init__, f.Date/__sub__, f.write

検索結果: 型Dateのオブジェクトを直接操作しているのは– メソッド CalendarUtil.get_day と …④– メソッド CalenderUtil.get_day_of_week である …⑤– それら2つのメソッドを、関数mainが呼び出している …⑥

15

デモのまとめ

プログラムの仕様を手がかりとして始めた– (途中でソースコードを参照することなく)

対話的に検索を進めていくプロセス– 開始から終了までの実行系列– → 重要な機能を持つクラス– → クラスのオブジェクトに対するすべての操作– → そのオブジェクトの操作を含む開始から

終了までの実行系列

16

検索手法

データ構造● コールツリー

– 節点は手続き(関数、メソッド)の呼び出し● 同じ関数が2回呼び出されれば、2つの子節点となる

– 呼び出しの順序=子節点の順序– 節点は属性として、呼び出された手続きの名前、実引数

や戻り値を持つ● サマリ

– コールツリー内の各節点について、その子孫節点の手続き名、実引数や戻り値を集めた集合

– 子節点のサマリ ⊆ 親節点のサマリ

入出力● 入力: 対象プログラム、検索キーワード● 出力: 検索キーワードを含むコールツリーの部分グラフ

17

検索手法 (続き)

手順

ステップ1検索対象プログラムを実行して実行トレースを取得

ステップ2実行トレースからコールツリーとサマリを生成→ 「インデックス」としてDBに保存

ステップ3前半検索キーワードの全てを属性に含むコールツリー内で深さが極大の節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

ステップ1実行トレース取得

ステップ2インデックス生成

ステップ3検索

検索キーワード

対象プログラム

検索結果

18

ステップ2(インデックス生成)の図解

ステップ2実行トレースからコールツリーとサマリを生成

call i call h call a call a return call b return return call f call c return call d return return call g call e return return returnreturn

簡単のため、実引数や戻り値は省略

→ 属性は手続名のみ

19

ステップ2(インデックス生成)の図解

call i call h call a call a return call b return return call f call c return call d return return call g call e return return returnreturn

i

h

a f g

a b c d e

コールツリーに変換

ステップ2実行トレースからコールツリーとサマリを生成

20

ステップ2(インデックス生成)の図解

A = { a } A = { b } A = { c } A = { d } A = { e }

A = { a } A = { f } A = { g }

A = { h }

A = { i }i

h

a f g

a b c d e

各節点の属性を求める

ステップ2実行トレースからコールツリーとサマリを生成

21

ステップ2(インデックス生成)の図解

属性からサマリを求める

A = { a },S = ϕ

A = { b },S = ϕ

A = { c },S = ϕ

A = { d },S = ϕ

A = { e },S = ϕ

A = { a },S = { a, b }

A = { f },S = { c, d }

A = { g },S = { e }

A = { h },S = { a, b, c, d, e, f, g }

A = { i },S = { a, b, c, d, e, f, g, h }

ステップ2実行トレースからコールツリーとサマリを生成

22

ステップ3(検索)の図解

A = { a },S = ϕ

A = { b },S = ϕ

A = { c },S = ϕ

A = { d },S = ϕ

A = { e },S = ϕ

A = { a },S = { a, b }

A = { f },S = { c, d }

A = { g },S = { e }

A = { h },S = { a, b, c, d, e, f, g }

A = { i },S = { a, b, c, d, e, f, g, h }

Q = { a, e, f }

「手続き a と e と f を呼び出してるところ」

ステップ3前半検索キーワードの全てを属性に含む「極小」(コールツリー内で深さが極大の)節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

23

ステップ3(検索)の図解

A = { a },S = ϕ

A = { b },S = ϕ

A = { c },S = ϕ

A = { d },S = ϕ

A = { e },S = ϕ

A = { a },S = { a, b }

A = { f },S = { c, d }

A = { g },S = { e }

A = { h },S = { a, b, c, d, e, f, g }

A = { i },S = { a, b, c, d, e, f, g, h }

Q = { a, e, f } A(n) ∪ S(n) ⊆ Q を満たし、深さ極大の節点n

ステップ3前半検索キーワードの全てを属性に含む「極小」(コールツリー内で深さが極大の)節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

24

ステップ3(検索)の図解

A = { a },S = ϕ

A = { b },S = ϕ

A = { c },S = ϕ

A = { d },S = ϕ

A = { e },S = ϕ

A = { a },S = { a, b }

A = { f },S = { c, d }

A = { g },S = { e }

A = { h },S = { a, b, c, d, e, f, g }

A = { i },S = { a, b, c, d, e, f, g, h }

Q = { a, e, f }

ステップ3前半検索キーワードの全てを属性に含む「極小」(コールツリー内で深さが極大の)節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

25

ステップ3の図解

A = { a },S = ϕ

A = { b },S = ϕ

A = { c },S = ϕ

A = { d },S = ϕ

A = { e },S = ϕ

A = { a },S = { a, b }

A = { f },S = { c, d }

A = { g },S = { e }

A = { h },S = { a, b, c, d, e, f, g }

A = { i },S = { a, b, c, d, e, f, g, h }

Q = { a, e, f }

Q の要素それぞれについて、サマリにその要素を含む一番浅い節点を探す

ステップ3前半検索キーワードの全てを属性に含む「極小」(コールツリー内で深さが極大の)節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

26

ステップ3(検索)の図解

A = { a },S = ϕ

A = { b },S = ϕ

A = { c },S = ϕ

A = { d },S = ϕ

A = { e },S = ϕ

A = { a },S = { a, b }

A = { f },S = { c, d }

A = { g },S = { e }

A = { h },S = { a, b, c, d, e, f, g }

A = { i },S = { a, b, c, d, e, f, g, h }

Q = { a, e, f } 見つかった節点より深い節点を全て除去する

ステップ3前半検索キーワードの全てを属性に含む「極小」(コールツリー内で深さが極大の)節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

27

ステップ3(検索)の図解

Q = { a, e, f }

h

a f g

e

call h call a return call f return call g call e return return return

検索結果

実行トレースの一部を取り出したもの

ステップ3前半検索キーワードの全てを属性に含む「極小」(コールツリー内で深さが極大の)節点を特定

ステップ3 後半 見つかった節点を根とする部分木から、寄与しない子孫節点を除去

28

オープンソースプロダクトへの適用実験

対象 markdown2– https://github.com/trentm/python-markdown2– Pythonで記述されている– マークダウンと呼ばれる記法で記述されたテキスト

を、HTMLに変換するライブラリ– ユニットテストが付属する

→ 実行トレースを取得するのに利用

対象プロダクトのサイズ

30

検索の内容付属のユニットテスト– 144個のマークダウンテキストをHTMLに変換する– うち16個が失敗する– 失敗するテストケースについては「FAIL」というメッセージが印字される

→ 失敗するテストケースがどのように実行されているか調べよう!

探したい実行系列 – 何らかのデータをファイルから読み込み、– 関数convertで変換して、– 「FAIL」という文字列を印字するもの

→ 検索キーワード: f.read f.convert v.FAIL

検索結果16個の失敗するテストケースのそれぞれについて– ファイルから読み込まれたマークダウンテキスト、

– 関数convertの呼び出し、–

32

議論(制限)

● 実行トレースのカバレッジ– 実行されないコードは検索対象にならない– → カバレッジが高い実行トレースが存在することが前提

● 実行トレースの粒度– 言語のプリミティブ(文字列の連結)などは実行トレースには記録されない

● スケーラビリティ?– 2千行そこそこのコードのインデックスが247MiB– 単純計算では2百万行だと2百GiB超え?(未確認)

● 実装– ソースコードがすぐに参照できるようにする

● ファイル名と行番号にすべき(現状ではバイトコードのアドレス)

– 既存の開発環境や解析ツールとの相互運用を考える

33

まとめ

● ソースコード検索手法– APIの利用例の検索やデバッグで利用することを想定

● デモ– 日付を曜日に変換するプログラムを例題とした利用シナリオ

● アルゴリズム– 実行トレース → コールツリー → 検索語を含む部分グラフ

● オープンソースプロダクトへの一適用例– 単体テストのメッセージから該当する処理を探す

● 議論– 技術的な制約がまだ多い。未実装部分も

34

発表後の修正

● タイトルページに原稿の出典を入れました● タイポ修正 「形」→「型」 p.5● アルゴリズムの説明の修正 p.25

– (原稿の説明は正しかったのですが、スライドの説明が簡略化しすぎて間違っていました)

● 「v.convert」→「f.convert」 p.30