5/3の記事「Heimdallrのバグ解析」の続きです。
上記記事に書いてある不具合が再現できたので、Visual C++.NET 2003のプロセスデバッグ機能と、Heimdallrが吐き出す大量のログを使って解析してみました。
その結果、ようやく不具合を引き起こす仕組みらしきものが見えてきました。まだ特定できないのが辛いところですが。
Heimdallrは、インターネットアクセスをWinINet Functionsを使って以下の手順で行っています。
(1) InternetOpen でWinINet初期化。
(2) InternetConnect でsession handleを取得。
(3) HttpOpenRequest でHTTP request handleを取得。
(4) HttpAddRequestHeaders でrequest header設定。
(5) タイムアウトスレッド起動。タイムアウトスレッドは5秒経過したら(3)で取得したhandleに対しInternetCloseHandleを実行してスレッド終了。
(6) HttpSendRequest でrequest送信。
(7) HttpQueryInfoを何度か呼び出してresponse headerの情報を取得。
(8) InternetReadFileを呼び出してデータを受信する。読み出しバイト数として0が返ってくるまで(8)を繰り返す。
(9) タイムアウトスレッド終了。
(10) (3)で取得したhandleに対しInternetCloseHandleを実行。
(11) (2)で取得したhandleに対しInternetCloseHandleを実行。
(12) (2)~(11)を、登録されているサイトの数だけ繰り返す。(2)~(11)は、複数のスレッドで同時に実行する。
(13) (1)で取得したhandleに対しInternetCloseHandleを実行。
今回解析したところ、タイムアウトスレッドは動いていないのにInternetReadFileの呼び出しがいつまでたっても完了していないような形跡が見つかりました。
これだけの情報からどこにバグがあるのか推測してみます。
(a) InternetReadFile呼び出し中に別スレッドからInternetCloseHandleが呼ばれたときにInternetReadFileを終了できない場合がある。つまりWinINetのバグ。
(b) InternetReadFile呼び出し前にタイムアウトスレッドからInternetCloseHandleが呼ばれ、さらに別のスレッドでHttpOpenRequestが呼ばれてhandleが再利用されてしまう。InternetReadFileは準備のできていないhandleから読み出そうとしてハング。つまりWinINetの使い方のバグ。
(c) その他のWinINetの使い方に関するバグ。
(d) 上記の手順に書かれていないところにバグ。
(e) 「タイムアウトスレッドは動いていないのにInternetReadFileの呼び出しがいつまでたっても完了していない」というのがそもそも勘違い。バグは別のところにある。
(f) Heimdallrはちゃんと動いている。私の脳のバグ。
(g) その他。
へぼい仮説ばっかりですが、今まではこの程度の仮説を立てることさえできませんでした。ようやくここまできた・・・。 さて怪しそうなのは(a)~(d)あたりです。もう少し解析してみましょう。次に再現するのは一週間先かもしれませんが。
投稿者 MASATO : 2006年05月12日 01:16 | トラックバック