トップ «前の日記(2011-04-29) 最新 次の日記(2011-05-03)» 編集

日々の破片

Subscribe with livedoor Reader
著作一覧

2011-04-30

_ 森を見て木を見ないバグ

おとといのバグ。

バグ自体はくだらないのだが、原因追究に無駄な時間を使ったのでメモ。

次のようなプログラムを作った。

void Write(string templ, params object[] args)
{
    string msg = string.Format(templ, args);
    ...//IOを伴ういろいろな処理
}
void FooBar(object a, object b, object c, object d)
{
    ...
    Write("{0} {1} {2} {3}", a, b, c, d);
    ...
}

が、テンプレートのパターンと引数に齟齬があり、例外が投げられるバグが見つかった(たとえば、{}内で特定の型を指定しているのに、引数がそれとは合わないなど)。

そこで、以下のようにした。

void WriteAsABCD(object a, object b, object c, object d)
{
   Write("{0} {1} {2} {3}", a, b, c, d);
}
void WriteAsABDC(object a, object b, object c, object d)
{
   Write("{0} {1} {3} {2}", a, b, c, d);
}
void FooBar(object a, object b, object c, object d)
{
    ...
    WriteAsABCD(a, b, c, d);
    ...
}

ところが、実際にはこの修正では引数の型指定がテンプレートと合わなければ修正前と同じことが起きる道理だ。

したがって(実は予想もつかない引数を与えるやつがいるのが原因でやはり)例外が発生する(このメソッド自体が型をアンマーシャルするので、パラメータの型はobjectにせざるを得ないとか、いろいろ理由があって型はobject以外にできない)。

このときReleaseモードでコンパイルしているため、メソッド名と行番号のみがバックトレースに入る。

ArgumentError: ...
Foo::Write ...
Foo::FooBar ...

ここですっかりだまされてしまった。

FooBarがWriteを呼び出すのは、修正前のバージョンだ。修正後のバージョンであれば、Foo::FooBarとFoo::Writeの間にFoo::WriteAsABCDが呼ばれているはずだ。したがって、例外を起こしているプログラムは修正前のバージョンに違いない。

だが、どう調べても実行時に呼ばれているのは修正後のバージョンに見える。PATHにも間違いはなさそうだ(多分)。が、実行時にカレントディレクトリを変えているとか、そこがTemporaryでそこにバックアップされた古いプログラムが存在するとかか?

だが、どう実行しても(実行環境は固有の環境で、デバッガなどは使えない。ネットワークから隔離されているのでリモートデバッガもあり得ない。また、開発環境は実行環境固有の環境を持ちえないので、開発環境でテストすることもあまり意味がない(ユニットテストとモックがあるわけだが、ここではそれをしていないのが大間違いだが、後の祭りである)例外になるし、バックトレースは上記の内容だし、実行しているのは修正後としか思えない。

こういうとき、何を疑うべきだろうか?

もちろんソースコードを疑うべきだ。そして修正前のプログラムにバグがあるのはわかっている。

まあ、瞬時にわからなかったのはしょうがないかなぁとは思うが、それにしても数分で気づけよとは思う。(実際には数十分かかった)

これがトライ&エラーが簡単な環境ならば、printfデバッグですぐにわかるわけだが、それができないときは頭だけが頼りだな(いや、その前にユニットテストをちゃんと作っとけというのはあるけど)。


2003|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|

ジェズイットを見習え