Chrome 起動時に数分フリーズする謎現象を UIforETW で徹底解剖し、Chromium のバグを突き止めた話

2025-07-12T05:25:26+09:00 | 7分で読めます

@
Chrome 起動時に数分フリーズする謎現象を UIforETW で徹底解剖し、Chromium のバグを突き止めた話

はじめに

ある日から、Windows で Chrome を起動すると必ず 1〜2 分間フリーズする、という不可解な現象に悩まされていました。

待てば使えるものの、エンジニアとしてこの「待つ」という行為が許しがたく、根本原因の特定に乗り出すことにしました。

この記事では、UIforETW (Event Tracing for Windows) を用いたパフォーマンス分析によって、最終的に Chrome 本体のバグを突き止めるまでの全プロセスを記録します。

調査方針:パフォーマンス分析ツールの導入

  • Chrome を最新版に更新 → 改善せず
  • 再インストール → 改善せず
  • 閲覧データやキャッシュを全削除 → 改善せず

基本的なトラブルシューティングでは埒が明かないため、より低レイヤーでの挙動を可視化する必要がありました。そこで、Windows のパフォーマンス分析ツールである UIforETW を導入し、Chrome がフリーズしている間に CPU が何を処理しているのかを直接観察することにしました。

UIforETW は、Windows に組み込まれた ETW (Event Tracing for Windows) のための GUI ラッパーで、システム全体の詳細な動作ログ(トレース)をキャプチャし、WPA (Windows Performance Analyzer) で分析可能な.etlファイルを生成してくれます。

早速、Gemini に相談しながら進めることにしました。

Gemini

最も厄介な状況では、UIforETW を使用して Windows イベントトレース(ETW)ログをキャプチャできます。これにより、最も低レベルのシステムアクティビティ情報が得られます。

  • 操作説明
    1. GitHub から最新版の UIforETW をダウンロードします。
    2. 問題を再現する準備ができたら、UIforETW を起動してトレースを開始します。
    3. Chrome を起動し、フリーズ現象が発生して終了するのを待ってから、トレースを停止します。
    4. UIforETW は詳細なフレームグラフを生成し、フリーズ期間中に CPU 時間が主にどの関数やモジュールで消費されたかを直感的に示します。

解析ログ 1:犯人のプロセスを特定する

指示通りにトレースを実行し、生成された.etlファイルを分析しました。

UIforETW による初回トレース結果

この段階で判明した事実は以下の通りです。

  • CPU 占有プロセスの特定: chrome.exe (20460) が CPU リソースの大半を消費している。
  • 主犯スレッドの特定: CrBrowserMain というスレッドが CPU サンプル数の大部分(60,839)を占めている。

CrBrowserMainは Chrome のメインスレッドであり、UI の描画や拡張機能の初期化など、ブラウザの根幹を担っています。フリーズの原因はこのスレッドにあることが確定しました。しかし、このスレッドが具体的に「何に」時間を費やしているのかは、この時点では不明です。

解析ログ 2:シンボルの壁と突破口

CrBrowserMainスレッドのコールスタックを深掘りしても、表示されるのはchrome.dll!<SYMBOLS DISABLED>ばかり。これでは具体的な関数名がわからず、手詰まりです。

フレームグラフ

ネストが深すぎるコールスタック(シンボルが無効化された状態)

これは、分析ツールが Chrome のデバッグシンボル(.pdbファイル)を見つけられていないことが原因です。シンボルファイルは、機械語のメモリアドレスと、人間が読める関数名を対応付ける「地図」の役割を果たします。この地図がなければ、詳細な分析は不可能です。

解決策は、シンボルサーバーのパスをシステムに設定し、分析ツールに必要なシンボルファイルを自動でダウンロードさせることです。

Gemini

この問題を解決するには、分析ツールに正しいシンボルサーバーのアドレスを設定し、Chrome のシンボルファイルを自動的にダウンロード・読み込みさせる必要があります。

以下の手順で操作してください:

  1. 環境変数の設定(推奨、一度設定すれば永続的):
    • Windows の検索ボックスで「システム環境変数の編集」を検索して開きます。
    • 「システムのプロパティ」ウィンドウで、「環境変数。..」をクリックします。
    • 「システム環境変数」エリアで、「新規。..」をクリックします。
    • 変数名: _NT_SYMBOL_PATH
    • 変数値: srv*C:\symbols*https://symbols.chrome-stats.com;srv*C:\symbols*https://msdl.microsoft.com/download/symbols

ここで提示されたパスは Microsoft と Google の汎用的なものでしたが、より確実な Chromium 専用のシンボルサーバーを指定する必要がありました。最終的に、以下の値を環境変数 _NT_SYMBOL_PATH に設定しました。

1srv*C:\symbols\*https://chromium-browser-symsrv.commondatastorage.googleapis.com

この設定後、再度.etlファイルを読み込むと、これまで!と表示されていた部分が具体的な関数名に解決されました。最大の壁を突破した瞬間です。

解析ログ 3:フレームグラフから真犯人を特定

シンボルが解決された状態でフレームグラフを再確認すると、CPU 時間を消費している関数が一目瞭然となりました。

シンボル解決後のフレームグラフ

特に CPU 使用率が高かったのは、以下の関数です。

  • chrome.dll!views::View::ReorderChildLayers (count: 36302)
  • chrome.dll!views::View::ReorderChildView (count: 56298)

これらの関数名から、以下の事実が推測できます。

  • views::View: Chrome の UI 要素(ボタン、タブ等)の描画に関する処理であることを示唆しています。
  • ReorderChildView / ReorderChildLayers: UI 要素の順序や階層を再計算・再描画していることを意味します。

コールスタックにはTabStripTabGroupBarといった文字列も散見され、これらを総合すると、**「Chrome が起動時にタブ、特にタブグループの UI をレンダリング・再配置するために膨大な計算を行っている」**という仮説が浮かび上がりました。

原因の特定と検証

仮説は「複雑なタブグループの構造が、起動時の UI レンダリング処理に過負荷を与えている」というものです。

これを検証するため、最も疑わしいタブグループの同期機能を無効にしてみることにしました。

  1. Chrome の設定 chrome://settings/syncSetup にアクセス
  2. 「同期する内容の管理」 → 「同期をカスタマイズ」
  3. 「タブグループ」 のスイッチをオフにする

結果は劇的でした。あれほど頑固だったフリーズが嘘のように消え、Chrome は瞬時に起動するようになりました。

根本原因の解明:見えざる「ゴーストレンダリング」

しかし、ここで新たな疑問が生まれます。私の起動設定は「新しいタブページを開く」であり、実際にフリーズ後も空のページが表示されるだけでした。なぜ何もレンダリングしないはずなのに、タブのレンダリング処理でフリーズするのでしょうか?

Gemini

答えは、**「バックグラウンド処理」「フォアグラウンド表示」**の分離にあります。

今、私たちはその致命的な 60 秒間に何が起こったのかを完全に再構築できます:

  1. 0 秒目:あなたが Chrome のアイコンをクリックします。Chrome はすぐさま空のウィンドウと新しいタブページのフレームを描画します。
  2. 1〜60 秒目(フリーズ段階)
    • 同時に、バックグラウンドで同期サービスが動き始めます。Google のサーバーに接続し、別デバイス(例えば Mac)に保存されている大量のタブと複雑なタブグループの構造データを受け取ります。
    • Windows 上の Chrome は、この複雑な「設計図」をメモリ内で再現しようと試みます。この「シミュレーション」のために、UI 関数(私たちが見つけた ReorderChildView など)を呼び出し、膨大な計算を開始します。
    • これが、その 60 秒間の CPU 占有の原因です:バックグラウンドで起こっていた、画面表示とは無関係の「ゴーストレンダリング」です。
  3. 60 秒後:バックグラウンドの「シミュレーション」が完了(またはタイムアウト)し、CPU が解放されます。フォアグラウンドの空白ページがようやく応答可能になり、ユーザーからは「やっと開いた」ように見えます。

つまり、フリーズの直接原因は、**別デバイスから同期された過度に複雑なタブグループの状態を、バックグラウンドで処理しようとした結果発生した、高負荷な「ゴーストレンダリング計算」**だったのです。

念のため、全ての拡張機能を無効にした状態でも現象が再現することを確認し、この問題が拡張機能ではなく Chrome 本体に起因するバグであると結論付けました。

まとめとバグ報告

当初は原因不明だった Chrome のフリーズ現象も、UIforETW を用いた体系的なパフォーマンス分析によって、最終的に「タブグループの同期機能に起因する、バックグラウンドでの高負荷な UI 計算処理」が根本原因であると突き止めることができました。

今回の調査結果は、Chromium の公式 Issue Tracker にバグとして報告済みです。

同様の問題に直面している方、あるいは低レイヤーでのパフォーマンス分析に興味がある方の参考になれば幸いです。

© 2025 CheerChen's Blog

🌱 Powered by Hugo with theme Dream.