題名:Java Diary-18章

五郎の入り口に戻る

日付:1999/12/5

目次に戻る


SETI@Support-part12(Ver0.51-0.54)

大幅に改訂したVer0.5をリリースしてから数日間、私は大変おびえていた。なんといってもMajor Version Upはいろいろの危険性がともなう。しかししばらくの間「なんやこれは」というメールをもらうこともなかった。

それとともに私はある考えにとりつかれ始めたのである。今回Ver0.5を作成する際、ネット上に存在しているあれやこれやの星図表示プログラムをためしてみた。そしてだいたいのプログラムに存在はしているが、私がはぶいていた機能があった。所謂プラネタリウムのような星空の表示である。

何故かと言えばとにかく2D/3Dの表示機能を作るので手一杯だったからなのだが、「次はどんな機能を作ろうかな」と思っていたときに、ふとあの円形の表示が頭に浮かんだ。そして例によって自分のプログラミング能力を超えてあれこれ想像はふくらみはじめたのである。

たとえばこんなのはどうだろうか。自分が住んでいる場所のデータを入力し、そこで見える星空を表示し、加えてデータの位置も表示するというのは。夜空を見上げると自分が解析したデータが帯状になって空に見える、というのはなかなか優雅ではないか。

しばらくその想像に浸っていたが(実はこれを考えついたのは電車を降りてオフィスまで歩いていく間だったのだが)オフィスに着いた後に現実にひきもどされた。これをやろうと思ったら本職の星座表示プログラムと同じくらいの労力を必要とする、と。だいたい特定の緯度経度から特定の時間に観た空の描画、というのはどうやればいいのだろう?そう思った私はその考えをあっさりゴミ箱に捨てた。

しかしながら、「空に浮かぶ解析したデータの帯」という考えはその後も私の頭の中に残り続けた。解析データがたまっていくにつれて帯がしっかりとしたものになっていく、ってのは結構いいことかもしれない。そしてVer0.5で存在している2D/3D表示ではその帯はなかなか見えないのだ。

となればやはり別の表示を考えねばならんか。。。と思って半球(2D上では円だが)星図を投影する方法に思いをはせた。ひょっとするといくつか方法があるのかもしれないが、平行線で投影すればなんとかなりそうである。そう思うと私はしょこしょこプログラムを作り始めた。そして思ったよりも簡単にそれは実現したのである。やってみると円形のエリアに星図が表示される。端のほうはどうしたってちょっとつぶれてしまうがまあいたしかたない。天の北極を中心として星図をみてみると、解析データが輪のようになって見える。これはいい。私はこの表示がすっかり気に入ったので、「大坪家のSETI@home状況」のページにこの円形の表示も加えることにした。表示を加えたものをVer0.51としてリリースしたのはいいのだが、その数時間後に「もうちょっと表示されるシンボルを小さくしたい」と思うようになった。表紙にも解析状況表示の絵をつけようとしたのだが、表紙ではかなり縮小する必要があるためである。その機能だけを追加したバージョンは0.511となった。0.1バージョン番号を進めるほどのことでもない、と思ったからである。

さて、機能をアップを果たして自画自賛のご機嫌気分もそこそこのうち、私はいくつかのバグレポートを受け取って青ざめることになるのである。

 

まず最初に受け取ったのは「SkyMap-Bitmapの端のほうが表示されません」というものであった。私はこのメールを受け取ったときにまずあたふたした。実のところVer0.5にとりかかってからというもの新しく作った2D/3Dの表示のほうばかり試験していて、Bitmapは「ああ。表示できる。すごいすごい」で済ませてしまっていたからだ。泡食ってBitMapにしてみるとどうやら私のMacintosh上では正常に表示されているようだ。うーむ。これはいかなることか、と思って「またJava VM依存のバグだろうか」と思い始めた。

さて翌日会社にいってWindows上のあれやこれやのJava VMで試験してみたが、どうやらちゃんと表示されてしまっているようだ。うーむ。困った。バグを教えてくれた人との相違点といえば、その人はWin98上で実行しており、私が使っているのがWindows-NTであることくらいだ。しかし私は(Macintosh上のVirtua PCを除けば)Windows-98が走るマシンを持っていないのである。

さて、困った、、と思ったがコードを見直しているうちにあることに気がついた。星図であるところのSkyMapはその名もSkymapというクラスで実現しているのであるが、そのサイズを特に規定していないのである。実際これまではそれでも動いていたのだが、今回加えたあれやこれやの変更によって何かボロがでたのかもしれない。そう思ってあてずっぽうでBitMapの場合最小サイズを規定することにしてみた。対処したつもりになるのはいいのだが、実際に治ったかどうかわからないのが情けないところである。幸いにして後日最初にバグをレポートしてくれた人から「治りました」と教えていただいて私としてはほっと一息である。

しかしそれはまだ始まりにすぎなかった。

ある日私はメールを受け取った。題名は「SETI@Support 0.5 バグレポート」である。私は泡を食ってその内容を読んだ。実に丁寧なメールで、そのバグが発生する状況、及びどうやって応急処置をしたか書いてくれていた。内容は、過去に解析をしたデータを記録しているss_past_data.txtの中に数値以外のデータがはいっているとプログラムがクラッシュする、というものであった。

細かく状況が書いてあるから、バグの原因もすぐ分かった。数値を読みとるつもりで数値以外の文字がはいっていると「なんやこれは」といってプログラムが文句を言う、というやつである。原因は簡単nわかったし、対策も簡単だ。。。と思ってプログラムを直し始めたのだが、ここで私は一つの悟りを得ることになったのである。

いままで「プログラムがクラッシュする」という表現を使っていたが、実際に何がおこるかと言えば「Exception Occured」というメッセージが表示されるのである。これはたとえばC++でプログラムを作ったときに起こる「Segmentation fault, core dumped」に比べれば大変丁寧である、、とくらいしかそれまでは思っていなかった(別に言い訳をするわけではないが、私は日曜プログラマなのである)

さて

「数字以外の文字を数字と思うからいけないわけね。ではちゃんと事前にチェックをすることにしよう。そこで引っかかったらはなから数字に変換しないようにしよう」

と思ったのだがそれはそう簡単にはいかなかった。「さて、数字以外とはなんでしょう?」とは結構ややこしい問題である。-0.3は数字だが、0-.3は(普通)数字ではない。しかしこれをちゃんとチェックしようと思うとこれはなかなか大変である。単に0-9,+,.とかだけであることをチェックするだけではいけないのである。

さて、どうしよう。。。と思ってふとあることに気がついた。文字列から数字に変換する関数は内容が数字にできないと「わからーん」と言って例外なる物を放り出す。ということは、つまるところ変換する関数が「わからーん」と言うからには彼はちゃんとその元となる文字列をちゃんと解析しているわけである。であればそれに加えて私が妙なチェックルーチンを作るなんてのは屋根を二つ重ねるようなものだ。

考えてみれば例外というのは、投げられれば"Catch"すればいいのである。いままではなんとなくほったらかしていたから、投げられた例外は誰にもキャッチされずプログラムがとまってしまうのである。ではちゃんと受け取ってしかるべき処理をすればよいではないか。

しかし考えてみればこの処理は「最初からそのつもりで作られた」機能なのである。私がこれまで真面目に勉強していなかっただけなのだ。そう思うと真面目に例外を受け取っていないところが山ほどあることに気がつく。うーむ。やはり「とりあえず動けばいい」という考えで作れば確かに「とりあえずしか動かない」プログラムができあがってしまうのであるなあ。。。

さて、反省とともにとりあえず0.511aというバージョンをリリースした。この辺からバグフィックスだけをしたものは、末尾のアルファベットで識別しようとしたからである。しかしVer0.5シリーズで発生したバグはまだまだ存在していたのである。

 

会社で使っているマシンには、三つの解析プログラムを立ち上げて、「複数クライアント対応機能」の試験に使っている。朝会社にいくとまず画面を確認して「おお。ちゃんと動いておるわい」と思い、そして解析が終わったデータがあれば「Gaussian Scoreはどうであっただろうか。。。ああ。0だ。また空振りだ」と思うわけである。

さて、それまでにも全く同一ポジションから来たデータを受け取ることが何回かあった。そうしたデータは星図上で観ると全くシンボルが重なって見える。これが本当に重なっているのかあるいは近くにあるだけなのかは今回新しくVer0.5で導入したVector表示機能で拡大してみれば識別できる、というのが私の考えた売りの一つであった。

ところが最近妙に同一ポジションにあるデータを受け取ることが多いな、と思い始めていたのである。私はとてもいい加減な性格だから「まあこんなこともあらあな」と思っていたが、ある日どうもそうではないことに気がついたのである。

朝画面を見るとシンボルが重なっている。ところが別にデータが更新されたわけでもないのに、視点をちょっと動かしてみるともうシンボルは重なっているように見えないのである。さすがのいいかげんな私も「これはおかしい」と思い始めた。そして胸に手をあてて考えること数分。ソースファイルを観ること3分私は「うげげげ」と声を上げた。

簡単に説明しよう。Ver0.5から位置データを3次元データで持つようにした。データは3Dだが表示は2Dなので表示の度に変換が必要となる。しかし描画の度に変換をしたのではたまったものではない。従って視点を動かさない場合は一度2Dに変換したデータをとっておいて、そのまま表示するようにしていたのである。もちろん視点が動いた場合には「やりなしだよーん」フラグをたてて再度変換計算をするようにしたいのだが、データが更新されたときにそのフラグをたてるのを忘れていたのである。私はちょこちょことプログラムを直し、Ver0.511bとしてリリースした。しかし後から考えて言えることだが、このとき何が起こったかをもう少しちゃんと考えていれば、この時点で次のバグをつぶせておけたかもしれなかったのだが。

さて、Ver0.51シリーズもだんだんと長くなってきていたので、私はせっかく得た悟りに従いあちこちに例外処理をちゃんと加えたバージョンをVer0.52としてリリースした。表面的にはなんの機能追加もバグ対処もないが、潜在的に発生する可能性のあった問題をいくつかつぶしたはずではある。と私は思っていた。しかし「顕在化」していたはずのより大きなバグには気がつかなかったのである。

 

そのメールを受け取ったのはVer0.52をリリースした直後だった。題名は「Fromデータの書き換え現象 (バグ?)」であった。私はその題名を観たとき「ああ。Ver0.511bでちょうど対処したばかりのやつだ。 そういえば対処はしたけど、まだメーリングリストで告知してなかったな。Ver0.52にもなったことだし、ちゃんと告知しようか」と思ってかるーい気持ちでそのメールを読み始めた。そしてメーリングリストに「Ver0.52リリースのお知らせ」をするためのメールを書き始めたのである。そちらをあらかた書いてしまうと、念のためにもう一度そのメールを読んだ。何かいやな予感がした。そして2度、3度読み返すうちに背筋に冷たい物が走るのを感じ始めたのである。どうもこれは私が対処したばかりのバグとは違うようだ。

私はそのバグを教えてくれた人にメールをうった。

「Ver0.52は出したものの、ご指摘いただいた問題は気がついておりませんでした。。。さっそく確認してみます。ちょっと問題のあるバグのようなので早急に対処したいと思います。」

ちょうど翌日の朝には新しいワークユニットを受け取れるような状態であった。であれば明日の朝には本当にデータの書き換えが起こるかどうか解る、というものである。私はとりあえず布団をかぶって寝ることにした。

さて、翌朝、Macintoshのスクリーンをみれば見慣れた「SETI@homeの解析おわったよーん」という意味のメッセージが流れている。さてさっそく試験だ。私はSETI@Supportを立ち上げたまま新しいワークユニットを受け取った。するとしばらくの後に正常に新しいワークユニットの位置にシンボルが表示され始めた。データを確認したが、異常もないようだ。となればこれは今までに慣れ親しんだ「Java VMに依存するバグ」なのかもしれない。となれば会社にいって試験をすればわかることだ。少し安心した私はのこのこと会社に向かった。

さて、会社のWindowsマシンでは、四六時中SETI@homeのクライアントが動作しており、SETI@Supportも立ち上がりっぱなしになっている。一定時間たつと処理速度を落とすようにしてあるあからそれほどじゃまになるわけでもない。その画面を確認してみたが、今のところ異常はないようだ。

さて、別の環境ではどうだろう、と思いMicrosoftのJava VMを使ってSETI@Supportを立ち上げてみた。とその瞬間何かおかしなことに気がついた。最新のデータとその前のデータの位置が全く同じと表示されているのである。実際双子のデータ(と勝手に私が呼んでいる同じ位置で、日時、あるいは周波数が異なるデータ)というのは存在するがこんなに簡単にそれにお目にかかれるはずがない。。となればこれはやはり教えてもらったバグなのだろうか。

それから(例によって例のごとくであるが)私はそれから数分の間パニックに陥っていた。つまりバグの再現が大変容易にできたからだ。別の言葉で言えばそのバグは確かに私のように使っていれば発生はしなかっただろうが、別のそして普通の使い方をすれば必ず発生するものであったのだ。

とりあえず私はトイレにいった。今回Ver0.5では星図を大幅に強化した。しかしこのデータを更新し、古いデータを記録に追加する、というところは全くいじらなかったはずだ。では何故このような問題が生じたのか?そんなことばかりを考えながら用を済ますとまた机に戻り、Ver0.3シリーズ以来いじっていなかった関数を(あれこれの記憶をたどりながら)数分眺めていた。

そのうち何か頭にひっかかるものがあった。そしてそれから別のファイルを開くと私は思わず悲鳴をあげそうになった。何がおこったか判明したのである。

問題は所謂Deep Copy, Shallow Copyというやつについて私が全く忘れていたが為に発生したものだった。SETI@Supportではデータを更新した時点で最新のデータをファイルにとっておいてある。そして次にデータを読みに行くとき、解析データのすべてのファイルがそろっていない場合がある、とはあれやこれやの試行錯誤の末に学んだ教訓だった。従って前にセーブしておいたデータを読み込み、その「コピー」を作る。そしてそのコピーの方の情報を更新していく。そして新しいデータが来ているとわかれば、読み込んだデータを「解析済み」のファイルに追記する、とまあそんな動作をしているのである。

問題はこの「コピー」、プログラム中ではclone(クローンである)で生じていた。Javaではしかるべく宣言を行い、関数を追記すれば、その名もclone()という関数を呼ぶことによりインスタンス(この言葉になじみ無ければデータ)のコピーを作成することができる。実はここに落とし穴があった。

ちょっと空想的な例で書くとかくの通りである。私がいる。そして銀行口座に預金に持っている。ここで「五郎のcloneを作れ」とやると見事に私のクローン人間が誕生する。複製だから、このクローン五郎の顔をなぐってみたり、あるいはお化粧をしてみたりしても、元の私の顔にはなんの変化も生じない。

ところが「口座」は両方が共有しているのだ。私は口座を持っている。クローンも口座を持っている。ところが口座の実体は一つだから、クローンが口座から金を引き下ろせば、私があずかりしらぬ内に「私の口座の金も減っている。ここで起こっていたことはまさに「知らない間に銀行口座の残金がかわっている」であったのだ。

この銀行口座まで同じ額のはいった別の口座をつくるか、あるいはいっしょの口座を共有してしまうか、とことに関してはなかなか難しい話が付随している。しかしここではそれにはふれない。今までそれをあまり意識しなくても動いたのはいままで「データ」の中身はcloneと一発語りかけると全部丸ごと複製を作ってくれたからなのだ。ところがVer0.5以降私は「データ」に少し変更を加えていた。その結果としてCloneと語りかけられたとき、丸ごとコピーではなく、一部は共有するようになっていたのだ。

泡を食った私は、さっそくプログラムを修正した。そしてその日のうちに新しいバージョン、Ver0.52aをリリースしたのである。しかしその原因が判明した後に、自分のMacintoshのデータを観てみると3回位置データの上書きが起こっていたことが判明した。なんということだ。自分のデータは自業自得だからいいとして、こうした問題を私はどれだけ世の中にまきちらしてしまったのだろう。

 

かくのごとく私が「しおしおのパー」状態になっていたとき、さらに追い打ちをかけるバグレポートがメーリングリストに投稿されたのである。

私はそのときアメリカに出張中であった。そして出張中であっても一応メールはちゃんとチェックしていたのである。そこに「Macintoshで使っているとOSErr -35 processing classpath itemというエラーが表示される」と教えてくれた人がいた。私はこれまた飛び上がった。私のメインの開発環境はMacintoshである。しかし今までそうしたエラーはお目にかかったことがない。まさか、と思って自分の環境で立ち上げてみたが、結果は同じだ。すなわち正常に立ち上がってしまう。

ひょっとするとJavaのバージョンとMac OSのバージョンが合わないせいかもしれない、、と淡い希望を抱いて「バージョンを教えてください」と投稿したが、直後に二人の方から「私のところでも起こってます」と教えてもらえた。もはやバージョンがどうのこうの、という問題ではなさそうである。すぐにでも改訂版を出したい衝動にかられたが、私はまだCaliforniaにいた。となればアップロードが可能になるのは帰国してからになる。原因はわからないが解決策は決まっていた。つまりVer0.4以前で使っていた環境、Visual Cafeでプログラムをコンパイルすることである。少なくとも以前はそうした問題はおきなかったはずだ。となれば問題は開発環境をCode Warrierに変更したことにあるにちがいない。

日曜日、アパートに(そして日本に)戻った私はさっそく元の開発環境を使ってコンパイルしたMacintoshだけのバージョン、Ver0.52bをリリースした。しかしその直後にまたもや自分が間違いをしたことに気がついたのである。

Ver0.5で発生していた「位置データが上書きされる」問題は私の精神衛生にきわめてよくない影響を与えていた。それもこれも私がcloneなどということをしたからだ。だいたい人間であってもクローン技術というのは何かと物議をかもしだす。となればとにかくCloneに手をださずに問題をすませる方法はないだろうか?私は以前自分が何故cloneを使っていたかをさっぱり忘れていたのである。

さて、プログラムを観ている限り、どうやらcloneは使わずにすませられそうである(と誤解した)。そしてVer0.52bを最初にアップしたとき、私はその部分を少し変更していたのである。

ところが「お詫び文」とともに0.52bをアップした直後に私は何故自分がclone技術に手を出したかを思い出したのである。それにはそれ相応の理由があった。となれば変更したのはやはり間違いだった。数時間後私はまた変更を加えたバージョンをアップした。

これだけバグが続くと深刻な鬱に陥っても不思議ではない。なんということだ。と私はうじうじ考えていた。Ver0.52b(バグ有り)をアップしてから改訂版をアップするまで、アクセス記録を観ると10人ばかりの人が私のダウンロードページにアクセスしている。そのうち何人がMacintoshユーザーかしらないが、とにかくそれから数時間私は悪夢にうなされ続けた。バグを治したつもりでまたバグのあるプログラムをアップしてしまった。もしその間にダウンロードした人がいたら何がおこるのであろう。

半日ノイローゼになったあげく「27日土曜日の正午から午後7時までの間にMacintosh版をダウンロードされた方はご面倒ですが再度ダウンロードをお願いいたします。」という「再度のお詫び文」をアップした。そして私はよれよれになったのである。

 

それから数日の間、私はSETI@Supportの事を考えるのもおっくうな状態になっていた。Ver0.5で「ほーらすごい星図がかけるぞ」と得意になっていたのもなんのその。またもや私はバグのあるプログラムを世の中に放ってしまっていたのである。そんなことをうだう考えながら品川駅の通路を歩いていると、ふとある光景が頭に浮かんだ。

それは宇宙戦艦ヤマトの中で、森雪が見ているなんかのレーダーの画面である。真ん中から同心円状の光がひろがっていくと、そこで目標がぴこんと光る。何故そんなことを思いついたかといえば、最近解析済みデータの点滅をもう少しなんとかできないものかと思い始めてからだと思う。

さて一旦こうしたイメージが頭に浮かぶとしばらくはそのことばかり考えている。しかしこの「同心円が広がっていく」というのは結構ややこしかった。(少なくとも私には)さっそく暇なときに久しぶりにコードをいじり始める。最初はちょっとだけいじるつもりだったが、従来からあった解析済みデータ点滅のOn/OFF機能も合わせることにしたのであれこれ変更が必要となった。しかしあれやこれやの試行錯誤の末、なんとか(実は予想したよりも大幅にかっこよくなかったのだが)なんとか機能は形になってきた。

さて、こうなると新しいリリースが視野にはいってくる。それだけでは物足りないので、従来ずっと考えていてほったらかしにしてあった機能を付け加えることにした。Ver0.43から解析済みのデータを別ファイルにしておいて追加する事が可能となっている。たぶんこの機能だけ目にした方は「なんだこれは?」と思われたかもしれない。これだけではあまり用途がないからだ。しかし本当は当時から「別の解析支援プログラムでとったデータをこの機能を使って表示させる」というのが頭にあったのである。ところがいざやろうとするとこれがなかなか面倒で、ついのばしのばしにしてきてしまった。

これまた広く使われているI4WUの作者の方は、I4WUの出力データをSETI@Supportのデータ形式に変換するユーティリティを発表している。ありがたやありがたやと思う。ところが当然のことながら他の支援ツールにそうしたものはない。私が知っている限り支援ツールは両手で数えるほど存在しているが、私の勝手な推測によれば一番メジャーなのはSETIWatch+SETILogのようである。ではなんとかSETILogのデータを読み込めるようにしたいではないか。そう思っていくつかデータをとってみると、これが結構膨大である。おまけに一つだけどうやっても変換が不可能なデータが存在していた。SETI@Supportでは「いつ解析が始まったか」を記録しているが、SETILogでは「いつ解析が終わったか」を記録しているのである。しばらく悩んだが素直に「ここはちょっと定義がちがってます。」と看板を立てることにして前にごりごりと進むことにした。

さて、この機能にもだいたいの目処がたった。そして同じ頃、Macintoshで私を悩ませた OSErrにも解決の糸口が見えてきた。実は何故それが起こったかわからないのだが、治す(と思われる)方法は見つかったのである。コンパイルを行う際に、共通のサブルーチンを(この場合はクラスと言うべきか)含んだファイルを指定する。それがスタンダード(と思われる)ものではなく、ちょっとかわったものになっているのである。なんだこれは?と思ってそれを戻したら一応父のiMac上ではOSErrはでなくなった。とはいっても一度懲りている私はまだ「なんとかもうちょっと検証ができぬものか」と考えているのではあるが。

さて、これを書いている時点では、あと数日でVer0.54がリリースされるはずである。その後はVer0.5シリーズはバグフィックスだけを行うことになる。

 次の章


注釈