昨日に続いてまたバグの話。興味の無いあなたならここで ESC キーを押しましょう。今日の話はちょっと長いですからね。
先月から仕事の関係で通信ソフトウェア(一般的なものではありません)を開発していました。5月にプロトタイプがPC98で動き、6月に入ってIBMへの移植を始めました。当初から移植を前提に開発したものですから変更する部分はわずかです。キー入力処理、RS232Cの制御処理のモジュールを置き換えるだけでほぼ移植完了です。プログラムのメインとなる部分には何ら変更はありません。
開発したプログラムは、私の会社がサービスしている商用電子メールシステムへのアクセス、ならびにメールの送受信等の面倒な操作をわずかな操作だけで全て自動で行なうという特化ソフトウェアです。
さて、IBM機への移植を完了、実テストを行なって見ると自動通信中にハングアップします。何度繰り返しても同じです。IBM機のためのRS232C制御部は以前に作成した実績のあるものですが、その時のモジュール開発では、なんどもなんどもハングアップさせたものです。受信割り込みやタイマー割り込みがうまくいかず苦労したものです。さては、その時に解決できていないバグが現われたのかと思うとともに、あの時の虫取りの大変さを思い返すとぞっとするような気持ちでした。
何はともあれ現象をよく掴む事だと思いなおし、ハングアップしていそうな箇所、何箇所かに当りをつけて、デバッグ用の printf 命令を埋め込み再度テスト。問題箇所は次のようなものでした。自分宛のメールボックスにメールが受信されていると一通ずつ自動ダウンロードしてくるのですが、ここの処理は受信しているメールの数だけ同じ処理をループさせて実現しています。(当り前かな)
for(i = 0; i < rec_mail_count; i++){ printf("%d\n", i); デバッグ用の printf ............ ............ メール一通分のダウンロード処理、 ............ いくつか関数をコールしています。 }
ここで rec_mail_count が 1 であるにもかかわらずループが終結せず、画面に表示される i の値は 0 の次に -4563 とゴミ値になっています。どうやら変数 i の値がどこかで破壊されています。これはもうC言語特有のバグに分類できそうです。
それではとPC98でテストして見ると、あんのじょう同じ症状が出ます。ということは、IBM機に関する部分の問題ではなく機種共通のメイン処理の部分のバグと断定できます。ソースリストを追いかけること数時間、全然バグの原因が掴めません。当然の事ながら移植作業に入るまではちゃんと問題なく動いていたプログラムです。リスト上から見る限り処理ロジックにミスは見つかりません。
ここで推理力を発揮させる必要があります。バグ取りは正に最高の推理ゲームです。変数 i の値が破壊されている事実に着目します。C言語特有の... と書きましたが、Cのプログラムで変数を簡単に破壊するようなミスは何かを考えれば自ずと見えてきます。確保した領域以上の書き込みがどこかで起きているはずです。そしてそれは大概、文字列処理にあります。strcpy や scanf("%s.. " や初期化されていないポインタ参照などです。
このような観点でリストの文字列宣言の部分を見直してみました。
char buf[120]; とか char buf[256];とか割と余裕のある領域を確保している宣言ばかりなのですが、中に次のような宣言をしている所がありました。
char date[4]; char day[10]; int timeh; int timem;そしてこれらの変数は次のように使用しています。
sscanf(buf, "%s%s%d:%d", date, day, &timeh, &timem);ここで buf には次のような文字列がメールホストから受信されています。
" Wed 30-May-90 10:30 " (90年 5月30日(水)の意味の文字列です。)
date に "Wed" が、 day に "30-May-90" が、timeh、timem にそれぞれ 10、30が代入されます。確保している文字列長に問題はなさそうです。
ところがホストから送られてくる受信文字をよく調べて見ると、本日のテストでは
" Tue 05-June-90 14:35 "となっています。day の部分が 1 文字増えています。 あんちょこに date[40],day[40] と宣言しなおして解決したわけですが、バグ原因は「月」の英語表現がJan, Feb, Mar, Apr,May, Jun,........ のように3文字であると思いこんでコーディングしたことでした。曜日の表現が Wed, Thu, Fri,のように3文字省略表現されていたために月も3文字省略であるとつい思いこんでしまったのです。開発した時期が5月であったことも運が悪かったようです。ローカル変数なんかはゴミみたいなものですから、スタックの許す限り文字列領域は大きく取っておけば問題はなかったのですが、ついつい貧乏症がでるのか、はっきり分かっている場合には必要最小限の領域しか確保しない癖が災いしてしまいました。
自分がプログラムで処理しようとする対象物をよく理解、把握していなかったとも言えるような失敗でした。
ここまで読まれたあなたはこんな失敗はしないんでしょうか? バグ取りというのもプログラミング過程の一つの楽しみ、勉強と思えば救われます。何事も実践の積み重ねで自信も付き、バグの少ないプログラムが書けるようになるのですから。