google-breakpadを使おう 01
はじめに
google-breakpadは、オープンソースでマルチプラットフォームに対応したクラッシュレポーティングシステムだ。Firefoxのように「長時間連続稼働されるが結構クラッシュすることが多いプログラム」で突然「申し訳ございません」とアプリケーションが弁明を始めることがあるが、それを担うのがこのこれだ。問題が出たらアプリケーションのクラッシュダンプやアプリケーションログをまとめてサーバ側に自動的に送信することで問題の解決を行いやすくすることが目的だ。
なお、アプリケーションがクラッシュをハンドリングしないと、代わりにその親であるOSが弁明をし始める。
うんちく
このライブラリの採用実績としては、以下のようなものがある。
- Google Chrome
- Firefox
- バージョン3.0から使われるようになるらしい
- Breakpad - MozillaZine Knowledge Base
- ブラウザ提供者という意味では競合だが、GoogleはMozillaと仲がいいようだ
- モジラCEO、「Google Chrome」のリリースを歓迎 - builder by ZDNet Japan
詳しく調べたわけではないが、この2つの巨大プロジェクトで採用されているので信頼性は高いように思える。同じようなシステムを自ら開発するよりも、世界の中で選りすぐられた開発者たちがすでに実装したものを使った方が効率はいい。仮に問題があってもBSDライセンスなので勝手に修正して配布してもいい(よね?)。
何をやってくれるのか
主な機能は以下の3つである。
- 例外ハンドラの提供
- クラッシュダンプの生成
- 例えばWindowsではクラッシュダンプが生成されるかどうかはワトソン博士などの設定による
- それに依存せずにクラッシュダンプを生成させることができる
- エラー報告
これはClientDesign - google-breakpad - Breakpad Client Libraries Design Document - Google Codeに記述されているが、私にとっては英語の文章を理解するよりもコードをチェックアウトしてhttp://google-breakpad.googlecode.com/svn/trunk/src/client/windows/breakpad_client.slnというVisual Studioのソリューションファイルを開いて確認する方が直感的だった。ソリューションを開くと3つのプロジェクトがあることが分かる。これが上記の3つの機能にそれぞれ対応している。これら3つの機能をすべて使ってもいいし、必要なものだけ選んで使ってもいい。プロジェクトはすべてスタティックライブラリプロジェクトになっていて、それぞれexception_handler、crash_generation、crash_report_senderという名前がついている。必要に応じて、CRTを静的リンクするか動的リンクするかを選択できる。ただし、UnicodeではなくMBCSを選択することはできない。x64ターゲットのビルド構成も存在しないが、x86(Win32)のものをベースにコピーすればおそらくそのまま動くだろう。
これ以外、例えばエラー報告のためのダイアログなどは提供されていないので、アプリケーション側で実装する必要がある。マルチプラットフォームに対応したエラー報告ダイアログを実装するのは骨が折れるし、アプリケーションの癖が出る部分なので、これはこれで問題ないだろう。
ちなみに、なぜこういった機能が必要かと言うと、バグのない大規模なソフトウェアを作るのは不可能だからだ。そうなると、バグが起きても迅速に修正できるようにするしかない。そのためにはバグを再現することができる正確な情報が必要だが、ユーザからバグの修正に必要な正確な情報を引き出すことは非常に難しい、そもそも正確な情報というのが難しいし、ある程度情報があっても再現することは難しい。あなたがもし何らかのソフトウェアを開発し、バグ報告を受けたことがあるなら、いかにその情報が曖昧なものかは分かるだろう(「動きません」とだけしか情報が無いことが多々あるのだ)。そうなると手っ取り早いのはクラッシュダンプをもらうということに尽きる。クラッシュダンプとそれに対応するコードのリビジョンがあれば、開発者は比較的容易にバグを追跡することが可能になる。Visual StudioでWindowsアプリケーションを開発したことのある人なら、アプリケーションのクラッシュダンプをIDEに読み込ませるとソースコードレベルでどこでクラッシュが発生位置、そのときのスタックの情報などを特定できるということを知っているはずだ。これはバグを推測するよりもはるかに簡単な作業で済む。簡単に作業できれば、バグの修正を迅速に行える可能性が高くなり、最終的にはユーザと開発者の双方にメリットをもたらすことになるだろう。
なお、上記で述べたようなことはクライアントアプリケーション開発に多いが、Webアプリケーションでも今後はこういった必要性が出てくるのかもしれない。それはajaxの出現によってWebアプリケーションが複雑化しているということや、PCやモバイル端末でJavaScriptが動作するリッチブラウザの拡大という背景があるからだ。私はWebアプリケーションの開発をそこまでやったことがないので、そういった事情を詳しくは知らない。
では、実際にどうなっているのかを詳しくみていこう。ただし、Windows版に限定させていただきたい。
詳細
exception_handler
exception_handlerは3つの中で2番目に規模が大きいものだ。下記のような例外を捉えることができる。
- Windowsの構造化例外処理で捉えることのできる例外
- SetUnhandledExceptionFilterによって実現される
- 具体的な内容は以下に記述がある
- http://msdn.microsoft.com/ja-jp/library/cc428942.aspx
- EXCEPTION_RECORD Structure (Windows)
- CRTの_set_invalid_parameter_handlerによって実現される
- _set_invalid_parameter_handler (CRT)
- 純粋仮想関数の呼び出し
- CRTの_set_purecall_handlerによって実現される
- _set_purecall_handler、_set_purecall_handler_m (CRT)
これらによって、開発者がコードで想定できていない例外はおおよそ補足できる。このライブラリを使うと、例外を補足したときにクラッシュダンプ(ミニダンプ)を生成したり、生成が終わった後に何をするかを定義することができる。
crash_generation
crash_generationは3つの中で一番規模が大きいものだ。これについては次回で取り上げる。
crash_report_sender
crash_report_senderは3つの中で一番規模の小さいものだ。
- HTTP/HTTPSでレポートをPOSTすることができる
- HTTP通信はWinInetによって実現される
- User-Agentは「Breakpad/1.0 (Windows)」となっている
- multipart/form-dataで複数のファイルをレポートすることができる
- プロキシ設定は、インターネットオプションのプロキシ設定を踏襲する
- InternetOpen()の呼び出しにINTERNET_OPEN_TYPE_PRECONFIGフラグを指定しているため
おおまかにはこんなところだが、以下のようなことに気をつける必要がある。
- サーバ認証やプロキシ認証が必要な場合はアップロードできない
- WinInetはInternet Explorerなどが保持しているパスワードを自動的に利用してくれたりしない
- このプロジェクトで提供されるクラスにはそのためのインターフェースはない
- サーバは認証不要にしておこう
- インターネットオプションのプロキシ設定が「設定を自動的に検出する」「自動構成スクリプトを使用する」の場合
- INTERNET_OPEN_TYPE_PRECONFIGフラグではプロキシの静的設定しか見てくれないためプロキシを超えることができない
- 企業などでプロキシ配下に端末がある環境がターゲットとなる場合は要注意
- レポートの進捗を知る術がない
- レポート完了に時間がかかる場合は要注意
コードは300行足らずなので、必要に応じて書き換えることも容易だと思う。WinInet APIが返すハンドルをRAIIで管理しているあたりはさすがだ。
まとめ
- 大規模な開発にクラッシュレポーティングシステムは欠かすことができない
- google-breakpadはそれを実現するためにシンプルでパワフルな機能を提供してくれる