Courgette スタンドアローン化について

これはなに

筆者はサイボウズラボユース開発支援制度を受けつつ、実行バイナリの差分生成についていろいろと調べています。 今日は、その活動紹介の一環として、最近やったことを記しておきたいとおもいます。 なお、以下の内容は 3月にやった成果発表スライドhttps://blog.cybozu.io/entry/2021/04/02/170042 を先に眺めてもらってからのほうが理解しやすいかと思います。

Courgette という Google Chrome に附属した高性能な実行バイナリ差分ソフトウエアがあります。これはGoogle Chrome のソフトウエアアップデートの高速化に使われています。

効果としては公式サイトを読んでもらえたらと思いますが、Chromium 190.1 -> 190.6 のアップデート容量が以下のように大幅に減少しています。

Full update 10,385,920

bsdiff update 704,512

Courgette update 78,848

しかし、これはChromiumソースコードと密結合していて普通に使えるものではないので、ソースコードが4万個以上ある Chromiumソースコードから、実行バイナリ差分生成プログラム Courgette のソースコードから肝心なものを取り出し、100ファイルほどと簡単に利用できるようにしました。

本稿では、どのようにやってきたかの概要を記したいと思います。リポジトリはここにあります: https://github.com/hiromi-mi/standalone-courgette

まず、先に使い方だけ紹介しておきます。

使い方

ビルド方法

$ git clone --recurse-submodules https://github.com/hiromi-mi/standalone-courgette
$ cd standalone-courgette
$ bash build.sh

こうすると courgetteout/Default 以下に生成されます。

$ ./out/Default/courgette --help

パッチ生成

パッチを それぞれ old_filenew_file とします。

./courgette -gen $(realpath old_file) $(realpath new_file) $(realpath diff.courgette)

すると diff.courgette という差分ファイルが生成されます。

パッチ適用

生成された差分ファイルの適用は以下でできます:

./courgette -apply $(realpath old_file) $(realpath diff.courgette) $(realpath out_file)

Chromium の大規模さについて

  • 翻訳単位の個数が4万個以上
  • ビルド時に /tmp 20GBを使いきる、中間生成物が多すぎてinode不足になる
  • 独自ビルドシステムを使っていて、使い回しがきかない
  • Core i9-10900X で 90分かかる (デバッグシンボルなし)
  • 基盤ライブラリ base だけでソースコードが4000ファイル
    • この基盤ライブラリでは std::string などSTLのほぼ全部の内容が再定義されているので、STLまわりの知識が使えない
  • モノシリックなソースコード
    • base なのにbase 以外に依存している

方法

基本的には、4万以上のソースコードファイルからいかに必要な関数を取り出すかが大事になってきます。

まず、メタビルドシステムを ninja を生成するために、gn というGoogle の独自ビルドシステムのビルドファイルを書きます。 トップレベル BUILD.gn からはじまり、その依存ファイルに沢山 .gn とか BUILD.gn とか *.gni というファイル群があるので、それらを Chromium のビルドファイルをもとに書き換えます。

次に、ビルドエラーを直します。前述の通り Chromium 本体の base/ は4000ファイルぐらいあり、他ライブラリに依存していて、かつインターフェースが頻繁にかわるなどで使いにくいです。 そのため、mini_chromium の利用を試みました。 これは base library の独立して使える縮小版.... のはずが Courgette をビルドするには未対応なものが多くあります。そこでパッチ をあてました。 その方法ですが、以下のとおりとても泥臭い手法です。

  1. . まず Courgette のビルドエラーになる箇所とその原因関数を特定します
  2. . その関数やクラスが含まれたファイルを Chromium 本体のbase/からコピーします
  3. . mini_chromium の base/ と Chromium 本体の base/ とがコンフリクトする箇所を書き換えます
    • 例: C++ で書かれているのですが、String 型が mini_chromium の string と base の string と STLの std::string が混ざっていてつらいです
    • 例: File クラスのAPIが微妙に違うものを使っています
  4. . 書き換えますが、コピーしたファイルが依存するヘッダファイルが不足するので、やはりビルドエラーになります
  5. . 1. から 4. をくりかえし、ビルドエラーがなくなるまで修正します。 また、(base のはずなのに) 他ライブラリに依存していることなどがあり、まともに修正できないこともあります。そのときは、Courgetteで使用している関数やクラスや、それに依存しているAPIを全部調べ上げて、使われていない関数定義などを全部コメントアウトしたり、使わないようにソースコードを書き換えます。

以上、1から6をただひたすらに繰りかえしていきました。50ファイルほどは書き換えたと思います。

そして、Courgette 自身にも同様にパッチを書きます。https://github.com/hiromi-mi/courgette/commit/58628c4a839860ca0ad061af96eb3b2cb9260d81

最後に、依存するサードパーティライブラリにパッチを当てて、ビルドシステムを作っていきました。

苦労した点と今後について

苦労した点はいろいろとあるのですが、 - ソースコードの分量が多すぎて、読み解くだけで数十時間 (総和) かかりました。 - ドキュメントが少ない。あるとしてもブラウザーChromiumソースコード解説だけで、それを取り出すことについては述べられていません。 - モノシリックなソースコードはその中で完結している分には分かりやすいが、一部だけ取り出そうとすると大変です。

今後については: - このアルゴリズムをそのままに、現在再実装に取り組んでいます。いずれできたら公開して別のソフトウエアアップデートシステムに組み込めたらと思っています。 - また、アルゴリズムの改良をしたいと思っています。

宣伝

このソフトウエアは サイボウズラボユース開発支援制度の支援を受け作成されたものです。