とれないバグをとれるようにする3つの方法 | 本郷ではたらく社長のブログ

とれないバグをとれるようにする3つの方法

最近は、今進んでいるプロジェクトがもうすぐひと段落するので、僕はデバッグをがりがりすることが多いです。バグがあるときになって帰れないので、終電を逃すことが多く、最近は日記を更新できていませんでした(>ω<)

前々から、僕はけっこうプロジェクト終盤でもICPCでもデバッガーとなることが多いのですが、そのときにためた、バグをとるポイントなどまとめてみることにします。

といっても、GDBを使うととれるようなバグは、GDBを使えばとれますのであんまり難易度が高いものではありません。悩んでもなかなかみつからない、なかなかとれない類のバグをとるのが僕は好きですが、そのときに何を考えてとっているのかを紹介します。といっても、僕はここらへんのことは自己流なので、当然もっといい方法があると思っています。なので、コメントなど熱烈歓迎です!!

難しい類のバグをとる際に一番重要なのは、バグにかかわるあらゆる状況をおしなべて把握・検証することです。たいてい、ここが原因だろう、と思っているところじゃないところにバグがあります。そういうところにあるバグだからこそ、とるのが難しいのですけど。といっても、やみくもにコードを眺めていてもなかなか見つかりません。僕は、バグをとるとき、次の3つの方法で、なるべく多くの状況を抜けなくチェックするようにしています。

(1)盲点を突く

前提を捨てることが必要です。このバグはおそらくここが原因だろう、というような前提をもっていると、たいていそこはすでによく自分でもチェックしているはずです。原因だと思っているコードよりも、そうじゃないところにバグは潜んでいます。なので、普段コードを書く上であまり気にしていない部分などを重点的にチェックすることが必要です。

よく抽象化されているコードでも、結合が0というわけではありません。逆に、疎結合であるために、モジュール間の関連がほとんどないと思いこんで、わずかな関連から漏れだすバグを見落としてしまいます。ここはよく抽象化されているから安全だろう、という前提は、デバッグのときには置くことができません。

僕がよくやるのは、コードを、1行1行しらみつぶしにチェックしていきます。どんなに抽象化されていたとしても、ちょっとでも関連があれば、関連しているコードをしらみつぶしにチェックしていきます。ありとあらゆる部分を疑います。疑心暗鬼になるぐらい、徹底的に疑います。複数人でコードを書いている時は、ここは大丈夫だろう、と他の人が指摘している部分をより重点的にチェックします。大丈夫だ、という指摘を、逆に盲点がそこに潜んでいるかもしれない、というヒントとしてとらえればよいと思っています。

たとえば、配列の添え字などは、とるのが難しい典型的なバグの一つです。また、プラスとマイナスを間違えてしまうようなバグも、非常にとるのが難しいです。行単位だけでなく、トークン単位でしらみつぶしにチェックしていきます。最初はしんどいですけど、(2)で紹介するような、バグパターンみたいなものを持っておけば、だんだん慣れていくと思っています。

(2)パターンを蓄積する

毎回、起こりうる状況をすべて考えるのは、必要なことですが、なかなかできることではありません。バグが起こりうるパターンを蓄積していくことも必要です。パターンにとらわれてしまうと、パターンから外れたバグをとれなくなってしまう、という危険性はあるのですが。しかしながら、パターンを蓄積しておき、毎回そのパターンを網羅するようにしたほうが、見落としが少なくなります。見落としが少なくなるメリットの方が大きいので、僕はバグが起こるパターンをできる限り蓄積し、バグをとるときにはそのパターンを網羅的にチェックすることにしています。

このとき、できるかぎりたくさんのバグが起こるパターンを蓄積していくことが必要です。自分が今まで起こしたバグだけではなく、できれば、ペアプログラミングなどによって、他の人がバグを起こすパターンも蓄積し、自分のものにしていく必要があります。

そのためには、できるかぎり、異なるバックグラウンドをもった人となるべく多くペアプロの経験を積むことが一番よいです。僕の場合は、ICPCのときは、他のメンバーがアルゴリズムが強かったので、難しいアルゴリズムを書いていく際に起こりうるバグみたいなものを蓄積していきました。Sedueを作ったときは、システムプログラミングに関してどういうバグがありうるのかを蓄積することができました。

できるかぎり多くの人とペアプロすると、パターンの蓄積ができるのはもちろんのこと、当然のことながら、今まで持っていなかったバグのパターンに出会うことも多く、自分にとって意外性の高いバグが起こるということを経験できます。ようは、パターンを網羅するだけでなく、あえて外してみるタイミング、みたいな直感を養うことができます。なかなか本などで勉強しても、このような経験はできないと思います。難しいバグは、自分が考えていなかった部分にあるものなので、その意味でも、自分が考えていなかったことを気づかせてくれるペアプロは、デバッグ能力を身に付ける上でも有効だと考えています。

(3)わからなかったら一人で悩まない

なんだか当たり前のようなことですが、なかなか職業でプログラムを書いていると、むずかしいのがこの点です。どうしても時間単位でものごとを考えてしまいがちなのですが、1人で5時間なやむより、2人で1時間なやんでバグをとったほうが結果的にはお得です。

一人でなやまないで、相談することのメリットは、相手にバグを探してもらえるというだけでなく、もう一つ重要な点があります。それは、相手に対して、自分が考えていることを伝えるプロセスが発生する、ということです。自分一人で考えていると、考え方が局所的になりがちです。おそらくここがバグっているだろうとそこだけを見てしまいます。ただ、ひとに説明するとなると、バグにいたるまでの過程から、コードの仕組みを詳しく説明することになります。

説明することによって、改めて流れを自分の中でも整理できる、というのは、デバッグにおいても非常に重要になってきます。ようは、説明するときに、説明になやんだり、ぱっと出てこなかった部分にバグが潜んでいる可能性が高いです。(1)の盲点とも関係してきますが、盲点となっている部分を列挙するために、ひとに説明するということは非常に有効です。

大体僕はこんなことを考えて、いつもデバッグをしています。思考としては、適度に発散させるのがコツなんではないかと。できるかぎりいろいろなことを発想して、可能性を探ると、とれないバグはとれると思います。あとは、ディスプレイに対する動体視力は、意外と重要かもしれません。結局は、早くデバッグするためには、できるかぎり多くの情報を一度にみて、パターンにマッチさせていくことが重要なので、パターンをあたりまえのように把握できるようにして、あとは単位時間あたりに見れる情報量を地道に増やしていくのがよいのだと思います。

バグは、入らないのが一番だし、バグとりの間は、あんまり生産をしているわけでもありません。ただ、ひとつのバグがシステムを止めてしまうこともあるので、決して無視できないポイントです。あんまり生産的な活動でないからこそ、すばやく対処するために、自分の中で方法論を確立していくことが必要だと思います。逆にいうと、デバッグ作業は、GDBなど使ってチェックするのは方法として当たり前になっていますが、それ以上のレイヤーになると行き当たりばったりになりがちです。行き当たりばったりだと、効率的なデバッグは難しいのではないかと思います。

今回は、僕はこうしているよ、という一例をしめしただけなのですが、自分なりの方法論さえ確立できれば、それによって、複雑なバグとりに対する意識みたいなものは確固たるものになるのではないかと思います。そうなれば、常に神経を払ってバグをとれるようになるのではないかと。TODOリスト的なものは、スケジュール管理においても、生産性をあげるためにも重要ですけど、それと同じような仕組みを、自分の中に確立できればよいのではないかと。