D言語 Advent Calendar 2013の二日目の記事です。DustMiteに関する日本語のドキュメントが皆無だったので、DustMiteについて書きます。あと、DustMiteと検索する時は注意してください。
バグ報告をしよう!!
D言語のコアな機能を酷使していると、たまにdmdのバグを踏むことがあります。そんな時は、ぜひバグ報告をしましょう!!
でも、バグを再現する最小コード作るの面倒くさい
わかる。しかし、
$ dmd bug.d
dmd: glue.c:1281: virtual unsigned int Type::totym(): Assertion `0' failed.
のように、特定のメッセージの出力とともにdmdが落ちるというバグの場合、DustMiteを使うと、それなりに簡単にバグ再現最小コードが作れます!!
https://github.com/CyberShadow/DustMite
DustMiteってなんぞ?
DustMiteは、D言語用のコードミニマイザです。こちらが指定したテスターを使い、テストをpassする最小のコードを総当りで探してくれるツールです。たとえば、
module hoge;
import std.stdio;
void main()
{
int a;
"hoge".writeln();
}
この、hogeと出力するだけのコードを、hogeと出力する部分のみを残して最小化したいとします。hoge.dをsrcディレクトリに入れ、DustMiteを使うと、
$ cd src
$ rdmd hoge.d > ../out
$ cd ..
$ dustmite src "rdmd hoge.d 2>&1 | diff - ../out >/dev/null"
src.reducedディレクトリに、
import std.stdio;
void main()
{
"hoge".writeln;
}
のように、最小化されたhoge.dが作成されます。きれいにint a;
が消え去りました。
DustMiteを使って、バグ再現性を保ちつつコードを減らす
上のような感じで、バグ再現性を保ちつつコードを減らすことが出来ます。試しに、Issue 11622の再現コードを減らしてみます。
class A(T)
{
B!T foo()
{
return new B!T;
}
}
class B(T) : T
{
}
static assert(!__traits(compiles, {
class C : A!C { }
}));
bug11622.dをsrcディレクトリに放り込み、まずはエラーメッセージを保存しておきます。
$ cd src
$ rdmd bug11622.d &> ../out
$ cd ..
$ cat out
dmd: glue.c:1281: virtual unsigned int Type::totym(): Assertion `0' failed.
DustMiteには、終了ステータスが、正しい出力の時に0、正しくない出力の時に0以外になるコマンドをテスターとして渡します。今回は、diffを使い、予め保存しておいたエラーメッセージと、実行結果を比較するというコマンドを作り、それを渡します。
$ dustmite src "rdmd bug11622.d 2>&1 | diff - ../out > /dev/null"
第一引数には、最小化したいソースコード群が入ったディレクトリを、第二引数にはテスターとなるコマンドの文字列表現を渡します。DustMiteは、カレントディレクトリにテスト用のディレクトリを作り、そこにソースコード群をコピーした後、そのディレクトリにcdしてテストを実行するので、bug11622.dやoutのパスには気をつけてください。減らす作業が終了すると、src.reducedディレクトリ内に
class A(T)
{
B!T foo()
{
return B!T;
}
}
class B(T) : T
{
}
static assert(!__traits(compiles, {
class C : A!C { }
}));
減らされたbug11622.dが生成されます。どうやら、returnとnewを取り除いても同様のエラーメッセージとなるようです。
まとめ
上のような感じで、DustMiteを使うことで、バグ再現性を維持しつつコードの量をある程度減らすことが出来ます。と言っても完璧ではないので、DustMiteが減らしたコードを人間が減らし、またDustMiteに掛ける、と言った感じで補助的にDustMiteを使うと、とてもバグ再現最小コードの作成が楽になると思います。
運悪くdmdのバグを踏み抜いてしまった時、是非DustMiteを試してみてください。