珍しい象を狩ることについて

虫狩りは楽しい。そしてたまに、孫を退屈させるような話(「私の時代は、まだ棒や石で虫を狩っていた」とか)を持って、生きて帰ることができる。
GDC 2014には、そんなトロフィーにふさわしいハンティング・サファリがもうひとつ用意されていた。Unity 5の発表を5日後に控えていたとき、私たちは醜い小さな象のようなバグを "発見 "した(まあ、見逃すのはちょっと難しい)。数分おきにステージに上がって、自分のバグレポーターがいかにすごいかをアピールするのは、何物にも代えがたい。
そこで、リーバイとジョナサンと私は、今取り組んでいる素晴らしい仕事(孫たちを退屈させるような話を増やすこと)をすべて放り出して、ストーキングに出かけた。その時点でわかっていたのは、Monoが実行時に生成するネイティブ・コードのどこかでクラッシュしたということだけだった。
プログラマーなら誰でも知っているように、明らかでないバグに直面したら、まず証拠を集めることから始める。虫の行動パターンを十分に学べば、いずれは一矢報いることができるだろう。そして、時間が刻一刻と迫る中、私たちはあらゆるものを撃つ準備ができていた。
しかし、私たちは困った。ゾウにしては、この虫は驚くほど機敏で卑劣であることが判明した。
この現象はOSX 10.9でのみ発生するようだが、キムは彼のヘビーデューティーなメモリー・デバッガー・ブランチを使って、Windows上でもよく似た現象を確認している。また、以前のバージョンのOSXでGuard Mallocを有効にすると、かなり似たようなものが表示された。しかし、呼び出し階層の任意の深さにあるランダムなスクリプト・コードでクラッシュしていたため、何が同じクラッシュで何が違うのかを確実に言うのは難しかった。また、クラッシュは10本連続で一貫していたが、次の5本ではまったく違っていた。
キムと私が膝の高さまでメモリを、太ももの高さまでアセンブリコードをかき分けながら、レヴィはMonoの秘密とそうでない活動のすべてを広範囲にトレースし、ギガバイトのログと私のおばあちゃんのスピードで動くエディターを作成した。どうやら私たちは、物事が醜くなる直前にクラッシュしたメソッドを常にコンパイルしていたようだ。
しかし、何がクラッシュを引き起こしたのか?直接的な原因は、無効なアドレスからコードを実行しようとしていたことだった。どうやってそこにたどり着いたのか?Monoのシグナル処理にバグがあり、レジュームがうまくいかない?MonoのJITコンパイラのバグで、コンパイルされたコードに正しくジャンプバックしない?別のスレッドがメインスレッドのスタックメモリを破壊している?妖精とグラムキン?
狩猟の2日後、ゾウはまだ元気で外に出ていた。
そこで土曜の夜、私はノートと4色のペン、そして我が家のトレードマークであるユニティの冷蔵庫からたっぷりのビールを取り出した。それから、デバッガーで4つの異なるクラッシュがフリーズするまでUnityのインスタンスを立ち上げ、「レッド・クラッシュ」「ブルー・クラッシュ」「グリーン・クラッシュ」「ブラック・クラッシュ」とラベルを貼り、それぞれ色のついたペンでメモを取ったり、見つけたものすべてのあまりきれいでない図を描いたりした。
これが『ブルークラッシュ』のメモだ:
スタックが16バイトも大きかったのだ!
すべてのクラッシュについて、余分な16バイトを調べると、クラッシュした関数に戻るリターンアドレスが見つかったのだ。トレースしてみると、どのケースでも同じメソッドからの呼び出しがすでに実行されていることがわかった。しかし、よくよく調べてみると、実はまだメソッドがコンパイルされていない呼び出しのリターンアドレスだった!
最後にトレースされたメソッドとこの呼び出しの間に、まだコンパイルされていない呼び出しがいくつかあったからだ。しかし、よく見ると、私たちはいつも彼らの周りをジャンプしていた。
それで、どうやら私たちが戻ることになっているその関数を見てみたんだけど......。
それがこれだ(青くハイライトされている):私たちは間違った方向にジャンプしていた!
Monoがここで行っているのは、JITコンパイラーへの呼び出しと、呼び出し後の命令ストリームにエンコードされたデータ(JITコンパイラーがどのメソッドをコンパイルすべきかを知るために使用する)のみを含む小さな「トランポリン」関数を作成することだ。JITコンパイラーが作業を終えると、これらのトランポリンは削除され、メソッド呼び出しにフックした痕跡はすべて消去される。
しかし、そこにある呼び出し命令は「ニア・コール」と呼ばれるもので、ちなみに符号付き32ビットオフセットを使って次の命令に相対的にジャンプする。
MonoのトランポリンがJITコンパイラーから2GB以上離れると、オフセットが32ビットに収まらなくなり、呼び出し命令を出すときに切り捨てられてしまうのだ。
そのとき、ジョナサンは的確な修正を素早く見つけ出し、彼の日曜日が終わるころには、GDCに間に合うように安定したビルドが完成していた。
そこからの経緯は皆さんご存知の通りだ。私たちはGDC 2014でUnity 5のデモを成功させ、絶賛を浴びました。発売後、Unity 5は瞬く間にこれまでで最も愛されるソフトウェアとなりました。待ってくれ、それはまだ先の話だ......。
その前に、黒と青のクラッシュを修正しなければならない。)