Blog
本記事は、2018年インターンシップに参加された押川さんによる寄稿です。
こんにちは。2018年夏季インターンシップに参加していた東京大学の押川広樹です。大学では定理証明支援系やモデル検査を用いたソフトウェアの検証について研究しています。
インターンでは、PFN で開発中のハードウェアに対して「良い」テストを実行することが目標でした。「良い」テストの基準は色々ありますが、今回は効率的で効果的なテストケースを生成することを目指しました。ここで言う効率的なテストとは、短い時間で実行できるように出来るだけサイズの小さいテストセットのことです。また、効果的なテストとはプログラムのより多くの部分を検査できるカバレッジの高いもののことです。
今回はそのようなテストケースを Concolic Testing と呼ばれる手法に基づいて生成することを試みました。
Concolic Testing
まず、テストというとランダムテストが考えられます。ランダムテストは手軽ですが効果的だとは言えません。例えば以下のようなコードを考えてみます。
int x, y; if (x == y) if (x == 10) fail; else ... else ...
上の例は x
と y
が共に 10 の時だけバグがあるプログラムを表します。ランダムテストによってバグを見つけられる確率は単純計算で (int
が 32 ビットの時) 232 × 232 回に一回です。
このためランダムテストはコーナーケースになるような微妙な部分のテストには向いておらず、高いカバレッジをとるためにはもう少し賢くテストケースを作る必要があります。その一つの例が Concolic Testing と呼ばれる手法です。Concolic は Concrete と Symbolic を組み合わせた言葉で、Concolic Testing とはプログラムの実際の実行とシンボリックな実行を交互に行うテスト手法です。
Concolic Testing では次の手順でテストを進めて行きます。
- ランダムな入力を生成する
- 入力に対して プログラム を実行する
- 実行時にプログラムのどの部分が実行されたかを記録しておく
- 3. の情報を元にその部分が使われるための条件を計算する
- 4. の条件の一部を変更し、プログラムの別の部分が実行されるための制約を得る
- 5. の制約を論理ソルバーで解くことで、実際にその部分を使うような入力を得る
- 2.-6. を繰り返す
各イテレーションごとにプログラムの新しい部分を実行するような入力が得られるので、理想的にはラインカバレッジ100%のテストケースを生成することが出来ます。
ランダムテストと同じ例でどのように動作するかをみてみます。
プログラムは if 文などで分岐する木だと思えるので、上の例は次のように表せます。
まず、x
と y
の値をランダムに生成します。例えば x = 1163551168
、 y = 1363922006
だとします。この下でプログラムを実行すると以下のようなパスを通ります。
このパスを通ったのは、x == y
が成り立たなかったからです。したがって制約としてこの条件の否定、つまり x != y
を考えます。これを満たすような x
と y
をソルバーを使って求めます。
例えば x = 0
、 y = 0
はこの制約を満たします。次はこれを入力としてプログラムを実行します。次のようになります。
確かに1度目の実行とは異なる部分が実行されています。先ほどと同様、このパスを通ったことから、「x == y
かつ x != 10
」が成り立つとわかります。x != 10
を否定した制約、「x == y
かつ x == 10
」をソルバーを用いて解くことで次の入力 x = 10
、y = 10
を得ます。そして、x = 10
、y = 10
で実行することでちゃんと fail を見つけることができます。
Concolic Testing は Godefroid らによる DART (Directed Automated Random Testing) [1] によって導入されました。[1] では Concolic Testing の手続きの導入と、Cで書かれたプログラムに対しての実装の実験がなされています。
上で説明した Concolilc Testing の手続きを素朴に行うと、対象のプログラムが大きくなるにつれてたどる必要のあるパスの数が爆発するためスケールしません。また、生成される制約が大きくなりすぎてソルバーで制約が解消できなくなる可能性もあります。
そのため実用上は、できるだけ早くカバレッジを増やすようにパスをたどったり、生成される制約を小さくする最適化を加えることで、実際のアプリケーションにも適応できるように改良されています。実際、DART にこのような最適化を加えたものに SAGE [2] があります。SAGE は X86 アセンブリを対象にしており、Microsoft で Office のバグを見つけるのに使われたようです。
また、対象をハードウェア設計言語にしたものに HYBRO [3] があります。HYBRO は Verilog を対象とします。
やったこと
今回は SystemVerilog で記述されたプログラムに対して Concolic Testing を行うフレームワークを作成しました。実装時間の都合で、受け入れられるプログラムの種類は限られていています。具体的には、ある程度制限された SystemVerilog の機能を用いて書かれた組み合わせ回路になります。例えば、interface が使われていないことなどを仮定しています。
テストケース生成の手続きの全体像は以下の図のようになります。
大まかには上で説明した手順と同じです。
- まず、SystemVerilog のソースコードをパース・解析して always 文ごとに Control Flow Graph (CFG) を作ります。この際にプログラムが期待する入力の情報(名前や型)を得ます。
- 次に、得た情報を使って最初の入力をランダムに生成し、シミュレータを用いて実行します。シミュレータは実行時の各変数の値の情報を Value Change Dump (VCD) というフォーマットで出力します。
- VCD には各タイミングでの変数の値の情報が入っているのでこれを元に CFG のどのパスが実行されたかを計算します。
- そこからその実行が行われるための条件を求めて、まだ実行されていない部分を次に実行するような入力を生成するのための制約を求めます。
- この制約をSMT ソルバーで解くことで次の入力を得ます。
- このプロセスをラインカバレッジが100%になるまで繰り返します。
SMTソルバーは Z3 [4] を用い、その他は OCaml によって実装しました。Z3 の API は OCaml から利用することができ便利です。
実装は https://github.com/pfnet-research/ATPG4SV で公開しています。
結果
300行程度の比較的小さめの回路に対して実行したところ10イテレーションほどで100%のカバレッジになるテストケースが得られました。
まだ実装が不完全なので対象に制限がありますが、実際の回路に対して動作させることができました。
まとめ
効率的で効果的なテストケースを生成する手法として Concolic Testing を紹介しました。また、インターンで取り組んだ、SystemVerilog で記述されたハードウェアへの適用例を紹介しました。まだ実用的に役に立つレベルのものではないですが、プロトタイプとして面白いことができたと思います。
最後になりましたが、メンターの酒井さんと Kühn さんには、テーマ決め、自分の知識が少なかったハードウェアに関する質問、発表練習、そして休憩中の雑談、とインターン期間を通して大変お世話になりました。ありがとうございました。
参考文献
- [1] P. Godefroid, N. Klarlund, and K. Sen. DART: Directed Automated Random Testing
- [2] P. Godefroid, M. Levin, and D. Molnar. Automated Whitebox Fuzz Testing
- [3] L. Liu and S. Vasudevan. Efficient validation input generation in RTL using hybridized source code analysis
- [4] The Z3 Theorem Prover https://github.com/Z3Prover/z3
おまけ:メンターより
押川さんのメンターを担当した、PFNの酒井とKühnです。
PFN ではディープラーニングを高速化する専用プロセッサー MN-Core™(エムエヌ・コア)の開発も行っていますが、チップ設計の検証は大きな課題の一つでした。 今回の取り組みは、この課題への取り組みとして始まった実験的なプロジェクトであり、押川さんには短い期間で、Coqによる部分的な証明などのアプローチも含めた複数のアプローチを検討のうえ、Concolic Testing に基づいたアプローチを選択し、プロトタイプを実装して実際の回路を対象にテストパターン生成のループを回せるところまで開発していただきました。(注: 既存の回路を対象に実験したものであり、現時点でのMN-Coreの実際の開発に適用しているわけではありません。)
PFNは上から下まで様々なことに取り組んでいていますが、まだまだ手が回っていないことも沢山あり、そこに興味のあるインターンの学生さんが来てくれたことをきっかけに新たな取組みが始まることがしばしばあります。今回のプロジェクトはそのような取り組みの一例でもあります。我こそはという学生の皆さんは、ぜひ来年のPFNインターンシップへの応募をご検討ください。 また、もちろん中途・新卒の人材募集も通年で行っていますので、こちらも興味のある方はぜひご検討ください!PFNの人材募集のページはこちら https://www.preferred-networks.jp/ja/jobs です。(FPGA/ASIC開発の人材も募集しています)