見つけた不具合をFirefoxにフィードバックする時には、それが後退バグである場合、いつの時点から不具合が発生するようになったのかという情報を書き添えておく事が大事です。この記事では、Firefoxの後退バグの発生時期を割り出す事を支援するツールであるmozregressionの使い方を解説します。
後退バグとは?
後退バグ(regression)とは、今まで正常に動いていた機能が、別の箇所の変更の影響を受けて、意図せず壊れてしまった、というケースを言い表す言葉です。
規模の大きなソフトウェアや複雑なソフトウェアでは、気をつけていても後退バグがどうしても発生してしまいがちです。後退バグが発生した直後にすぐに気付ければいいのですが、普段あまり使わない機能だと、いつからかは分からないが気がついたらその機能がずっと壊れたままだった、という事も起こりえます。
このような場面でよく使われるのが、二分探索という手法です。履歴上の「確実に正常に動作していた時点」と「現在」との中間にあたる時点のコードを調べて、その時点でまだ後退バグが発生していなければそこから「現在」との間の中間を調べ直し、その時点でもう機能が壊れていればそこから「確実に正常に動作していた時点」との中間にあたる時点のコードを調べ直す……という要領で範囲を絞り込んでいく事で、当てずっぽうで調べたり虱潰しに調べたりするよりも遙かに効率よく後退バグの発生時点を割り出す事ができます。
Gitでバージョン管理されているプログラムであればgit bisect
というコマンドを使ってそれを行えますし、Mercurialにも同様にhg bisect
というコマンドがあります。ただ、Firefoxのように大規模なソフトウェアでは、二分探索でその都度ビルドするというのは現実的ではありませんし、「特定の時点のNightlyのビルド済みバイナリをダウンロードしてきて展開して起動して……」という事を繰り返すのも大変です。そこで登場するのがmozregressionなのです。
mozregression-guiの使い方
Quick Startのページに英語音声の動画での解説がありますが、ここではアドオンのサイドバーパネル上のツールチップの表示がおかしくなる不具合を例に、後退バグの発生時点を割り出してみる事にします1。
mozregressionの配布ページから辿ってGUI版のWindows用ビルドをダウンロードすると、「mozregression-gui.exe」というファイルを入手できます。これはインストーラで、ダブルクリックして実行すると自動的にC:\Program Files (x86)\mozregression-gui
へインストールされます。起動用のショートカットは自動的には作成されないため、インストール先フォルダを開いて「mozregression-gui.exe」のショートカットをデスクトップなどに作成しておくと良いでしょう。
二分探索の開始
mozregression-guiを起動すると、3ペインのウィンドウが開かれます。
メニューバーの「File」をクリックし、「Run a new bisection(新しく二分探索を始める)」を選択すると、「Bisection wizard」というタイトルのウィザードが開かれて、どのような内容で二分探索を始めるかの設定が始まります。
今回は以下のように設定しました。
-
Application(アプリケーションの種類): firefox(この他に「fennec(Android版Firefox)」「Thunderbird」も選択できます)
-
Bits(バイナリの種別): 64(Windows版のバイナリは現在は64bit版が主流なので。32bit版特有の不具合であれば「32」を選択します)
-
Build Type(ビルドの種別):opt(この他に「debug」(詳細なデバッグログを出力できるビルド)と「pgo」(最適化ビルド)も選択できます)
-
Repository(リポジトリ):mozilla-central(この他に「mozilla-beta」などのブランチや「comm-release」などのThunderbird用リポジトリも選択できます)
「Next」ボタンをクリックすると、テスト時のFirefoxの状態を設定する画面になります。
特定のプロファイルで起動したときだけ後退バグが再現するという場合、再現に必要なプロファイルのパスを「Profile」欄に入力します。「Profile persistence(プロファイルの永続性)」は初期状態では「clone」になっており、テスト実行のたびに元のプロファイルを複製した物を使い捨てする事になります。「reuse」を選択すると指定したプロファイルをそのまま使う事になります。「clone-first」は両者の中間で、最初のテスト実行時に元のプロファイルを複製した後、以後のテストではそれを再使用します。ただ、新規のプロファイルでも現象を再現できる場合は、「Profile」欄は空にしておくことをお勧めします。
「Custom preferences」には、テスト実行時にあらかじめ設定しておく設定値を記述します。Bug 1474784の当初の報告内容は「extensions.webextensions.remote
がfalse
の時に再現する」という物ですので、そのように設定する事にします。「Add preference」をクリックすると行が追加されますので、「name」欄にextensions.webextensions.remote
、「value」欄には"false"
と引用符でくくらずにそのままfalse
と入力しておきます。
「Custom addons」には、テスト実行時にあらかじめインストールしておくアドオンを登録します。Bug 1474784の当初の報告内容はツリー型タブを使用したときという説明になっているため、アドオンの配布ページの「Firefoxへ追加」ボタンを右クリックしてリンク先のファイル(アドオンのインストールパッケージ)をダウンロードし、mozregression-guiのウィザードの「Add addon」ボタンをクリックしてファイルを選択しておきます。
さらに「Next」ボタンをクリックすると、二分探索を行う範囲を指定する画面になります。
初期状態では、起動した日とその1年前の日の範囲で二分探索を行うように入力されています。「Last known good build」欄には最後に正常に動いていたと確認できているビルドの日付を、「First known bad build」欄には最初に異常に気づいたビルドの日付を入力します。この探索範囲は、狭ければそれだけ効率よく絞り込みを行えます。Bug 1474784は7月の1週目までは起こっていない問題だったので、ここでは2018年7月6日から2018年7月11日までを範囲として入力しました。
探索の範囲を入力したら、準備は完了です。「Finish」ボタンをクリックするとウィザードが終了し、二分探索が始まります。
二分探索が始まると、検証用として先ほど指定した範囲の日付の中からいずれかの日のNightlyビルドがダウンロードされ、新規プロファイルで起動されます。この時には、ウィザードで設定した設定やアドオンが反映された状態になっていますので、後退バグが発生しているかどうかを実際に確かめてみることができます。
この例では、このビルドでは後退バグが発生している事を確認できています(スクリーンショット内のNightlyのウィンドウにおいて、サイドバー部分のツールチップが適切にスタイル付けされていない事が見て取れます)。検証用のFirefoxを終了した後、mozregression-guiのメインウィンドウ左上の領域にある「Testing (ブランチ名) build: (日付)」の項目の「good」「bad」の二つのボタンのうち「bad」の方をクリックしましょう。すると再び別のビルドのダウンロードが始まり、ダウンロードが完了するとまたそのビルドが新規プロファイルで起動します。
最初のビルドに続き2番目のビルドも後退バグが発生していましたが、3番目のビルドでは後退バグは発生していませんでした(スクリーンショット内のNightlyのウィンドウにおいて、サイドバー部分のツールチップが適切にスタイル付けされている事が見て取れます)。このような場合は、mozregression-guiのメインウィンドウ左上の項目の「good」「bad」の二つのボタンのうち「good」の方をクリックしましょう。
このようにして「good」と「bad」を振り分けていくと、やがて、次のビルドが起動されない状態になります。
この状態になると、二分探索は終了ということになります。mozregression-guiのメインウィンドウの左上の領域に表示される項目のうち最も下にある緑色の行が「最後の正常ビルド(last good build)」、最も下にある赤色の行が「最初の異常ビルド(first bad build)」を表しており、行をクリックすると右上の領域にそのビルドの詳細が表示されます。この例では、「最後の正常ビルド」は以下の通りでした。
app_name: firefox
build_date: 2018-07-09 14:02:55.353000
build_file: C:\Users\clearcode\.mozilla\mozregression\persist\140937d55bd0--mozilla-inbound--target.zip
build_type: inbound
build_url: https://queue.taskcluster.net/v1/task/eyRSVJsJT4WGMysouGUC_w/runs/0/artifacts/public%2Fbuild%2Ftarget.zip
changeset: 140937d55bd0babaaaebabd11e171d2682a8ae01
pushlog_url: https://hg.mozilla.org/integration/mozilla-inbound/pushloghtml?fromchange=140937d55bd0babaaaebabd11e171d2682a8ae01&tochange=e711420b85f70b765c7c69c80a478250bc886229
repo_name: mozilla-inbound
repo_url: https://hg.mozilla.org/integration/mozilla-inbound
task_id: eyRSVJsJT4WGMysouGUC_w
また、「最初の異常ビルド」は以下の通りでした。
app_name: firefox
build_date: 2018-07-09
build_file: C:\Users\clearcode\.mozilla\mozregression\persist\2018-07-09--mozilla-central--firefox-63.0a1.en-US.win64.zip
build_type: nightly
build_url: https://archive.mozilla.org/pub/firefox/nightly/2018/07/2018-07-09-22-12-47-mozilla-central/firefox-63.0a1.en-US.win64.zip
changeset: 19edc7c22303a37b7b5fea326171288eba17d788
pushlog_url: https://hg.mozilla.org/mozilla-central/pushloghtml?fromchange=ffb7b5015fc331bdc4c5e6ab52b9de669faa8864&tochange=19edc7c22303a37b7b5fea326171288eba17d788
repo_name: mozilla-central
repo_url: https://hg.mozilla.org/mozilla-central
これらの情報をbugの報告に書き添えておくと、実際に修正を行おうとする人の調査の手間が大幅に軽減されます。
なお、最初の異常ビルドの「pushlog_url」欄に現れているURLを開くと、前のビルドからそのビルドまでの間に行われた変更の一覧が現れます。この例では40以上のコミットが一度にマージされた時点から後退バグが発生したという事が読み取れ、後はこの中のどの変更が原因だったかを割り出すという事になります。運がよければ、最初の異常ビルドでの変更が1コミットだけに絞り込める場合もあり、その場合は調査範囲が一気に限定されます。
まとめ
以上、mozregressionを使ってFirefoxの後退バグの発生時点を割り出す手順をご紹介しました。
後退バグの修正にあたっては、いつの時点から機能が壊れていたのか、どの変更の影響で機能が壊れたのか、という事を特定する事が重要です。どの変更でおかしくなったのかが分かれば、原因箇所を特定する大きな手がかりになります。また、新たな別の後退バグを生み出さないためには、後退バグの発生原因となった変更の意図を踏まえつつ対応策を検討する事が有効です。後退バグを報告する場合は、できる限り「どの変更から問題が発生するようになったか」を調べてから報告することが望ましいです。
ただ、このように二分探索で後退バグの発生時点を割り出すためには、各コミット段階で「問題が発生するかどうかを確実に確認できる事」が必須条件となります。ミスが原因で「そもそも起動すらしない」状態のコミットが途中に存在していると、動作を確認できるのは「正常に起動するコミット」の間だけになってしまい、二分探索を有効に行えません。GitHub Flowのようにmasterを常にいつでもリリースできる状態に保ったり、プルリクエストのマージには必ずCIが通る事を条件にしたりといった運用をとる事もセットで行う必要があります。後退バグが発生しても原因をすぐに特定しやすいよう、健全なプロジェクト運営を心がけたいものですね。
-
このBug自体は実際には既に報告済みの他のBugと同じ原因であったことが分かったため、既にあった方のBugで続きをトラッキングするよう誘導されて閉じられています。 ↩