Chromium 定制版 PGO 实战:Chrome 与 V8 Builtins 两套体系以及打包踩坑

本文基于一次真实升核(M148 / V8 14.8.x)打包与 PGO 训练整理


前言:为什么 PGO 会「编着编着就缺文件」

切到 Official Release + PGO Phase 2 后,常见报错:

../../v8/tools/builtins-pgo/profiles/x64-rl.profile

needed by snapshot_blob.bin -> gen/v8/embedded.S

missing and no known rule to make it

这往往不是 snapshot 坏了,而是 Chrome 主程序 PGO 与 V8 Builtins PGO 是两套机制:只提交了 Chrome 的 *.profdata,却默认打开了 V8 builtins 优化,构建在 schedule 阶段就失败。


一、两套 PGO:别混成一个「PGO 包」

Chrome PGO V8 Builtins PGO
优化对象 chrome.dll 等 C++ V8 builtins(mksnapshot / snapshot)
训练产物 *.profdata x64-rl.profile
训练工具 tools/pgo/generate_profile.py 官方下载或 v8/tools/builtins-pgo/generate.py
Phase 2 开关 chrome_pgo_phase=2 v8_enable_builtins_optimization=true(Phase 2 默认常开)

is_official_build=true

→ chrome_pgo_phase=2

→ v8_enable_builtins_optimization 默认为 true(若未显式关闭)

→ Windows x64 需要 x64-rl.profile

Chrome profdata 有了 ≠ V8 profile 有了。


二、Chrome PGO:Phase 1 与 Phase 2

Phase 1:插桩采集

  • GN:chrome_pgo_phase=1
  • 编译带 -fprofile-generate
  • 插桩版浏览器跑 benchmark → .profrawllvm-profdata mergeprofile.profdata

python3 tools/pgo/generate_profile.py -C out/PGOPhase1

需要 完整源码树 + Phase 1 输出目录,不能只有二进制。

Phase 2:用 profile 优化

  • -fprofile-use=...profdata + ThinLTO
  • 链接 chrome.dll 通常最慢
  • CI 若每次 clean 全量,每次都像第一次全编

三、Phase 1 链接坑:升核后 fork 与上游 API 类型不一致

Phase 1 除了「能编过、能跑 benchmark」,还可能遇到 链接阶段 才暴露的问题。下面是一次 M148 升核 + chrome_pgo_phase=1 时的案例(扩展工具栏 ViewModel),与 V8 profile 无关,但 不做修复就过不了 Phase 1,也就训不出 Chrome profdata。

3.1 现象

args.gn 中设置:

chrome_pgo_phase = 1

fork 定制宏已开启(如扩展工具栏仍走 vector 顺序语义)

执行:

autoninja -C out/Release chrome

chrome.dll 链接失败,lld-link 报 undefined symbol,符号栈与 base::flat_tree / flat_set 析构相关------表面看像 STL 或 libc++ 链接问题,实际是 接口返回类型与底层实现不一致 在 PGO 插桩 下被放大。

3.2 根因(148 上游 vs fork 历史行为)

148 上游把 ExtensionsToolbarViewModel::GetAllActionIds() 改成了返回 const base::flat_set<ActionId>&,与 ToolbarActionsModel::action_ids() 对齐。

定制 fork 在 产品宏分支 下仍保留 ToolbarActionsModel::action_ids() 返回 std::vector<ActionId>&(扩展工具栏顺序语义依赖 vector,而非 sorted flat_set)。

若 ViewModel 仍声明返回 flat_set&,实现里却 return actions_model_->action_ids()

  1. 编译器对 vectorflat_set 做隐式转换,构造 临时 flat_set
  2. 函数返回的是 临时对象的引用(本身已是危险写法)
  3. PGO Phase 1 插桩 下,flat_tree 析构路径 无法如 Release 那样内联
  4. 链接器找不到对应符号 → chrome.dll 链接失败

非 PGO 或 Phase 0 构建有时能「侥幸过」或表现不同;Phase 1 是更严格的照妖镜。

3.3 修复思路(与上游分叉的最小对齐)

在 fork 宏分支 让声明、实现与底层 同一类型------返回 const std::vector<ActionId>&,直接转发 action_ids(),避免任何 vector→flat_set 临时对象:

#ifdef FORK_EXTENSIONS_TOOLBAR /* 示意:你们实际的 fork 宏名 */

const std::vector<ToolbarActionsModel::ActionId>&

ExtensionsToolbarViewModel::GetAllActionIds() const {

return actions_model_->action_ids();

}

#else

const base::flat_set<ToolbarActionsModel::ActionId>&

ExtensionsToolbarViewModel::GetAllActionIds() const {

return actions_model_->action_ids();

}

#endif

HasAnyExtensions() 在 fork 分支改为 直接读 action_ids().empty(),不要再经可能触发转换的 GetAllActionIds()

bool ExtensionsToolbarViewModel::HasAnyExtensions() const {

#ifdef FORK_EXTENSIONS_TOOLBAR

return !actions_model_->action_ids().empty();

#else

return !GetAllActionIds().empty();

#endif

}

头文件同步改返回类型,并加注释说明:禁止在 fork 路径返回 flat_set&,否则 PGO 链接可能再炸。

3.4 测试建议

做法
链接 chrome_pgo_phase=1autoninja -C out/Release chrome,确认 chrome.dll 链接通过
功能 启动浏览器:扩展工具栏图标显示、拖拽、溢出菜单
回归 Phase 0 / Phase 2 各编一次,确认无新增链接错误

3.5 对 PGO 流程的启示

升核 → 先过 Phase 1 链接 + 能启动

→ 再 generate_profile.py 训 profdata

→ 再 Phase 2 + V8 profile

Phase 1 链接问题 和 Phase 2 缺 x64-rl.profile 是 不同阶段、不同层 的故障:

阶段 典型错误 性质
Phase 1 lld-link undefined symbol(flat_set/flat_tree) fork 与 148 API 类型不一致 + 插桩
Phase 2 missing x64-rl.profile V8 builtins PGO 文件缺失

升核合并时,建议对 flat_set / span / 容器类型变更 做专项 grep:凡 fork 仍用 vector 的模型,上层 ViewModel 不要声明成 upstream 的 flat_set&。


四、V8 Builtins PGO:官方包 vs 自训

4.1 官方 profile

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/{V8四段版本}/meta.json

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/{V8四段版本}/x64-rl.profile

版本来自 v8/include/v8-version.h(如 14.8.178.4),不是 Chrome 产品号。

meta.jsonrevision 需与 DEPSv8_revision 一致 再用。

4.2 x64.profile vs x64-rl.profile

  • Windows 正式构建:x64-rl.profile
  • Linux/macOS + Clang:x64.profile

4.3 内网镜像 404、Google 有

多为 镜像未同步 chromium-v8-builtins-pgo 桶。

4.4 自训难点(摘要)

Windows 要 -rl、GN 须与正式 Release 一致、benchmark 环境、每次 bump V8 重做、profile 默认 gitignore。revision 对齐时 优先官方。


五、推荐策略(决策树)

升核 / 出包

├─ Phase 1

│ ├─ chrome.dll 链接过? → 修 fork/上游 API 不一致(如 GetAllActionIds)

│ └─ generate_profile.py → profdata

├─ Phase 2

│ ├─ Chrome:pgo_data_path / 已提交 profdata

│ └─ V8:x64-rl.profile(官方 revision 对齐)或 v8_enable_builtins_optimization=false

└─ CI:确认 gn gen 真正传入 extra_args

务实组合:Chrome profdata 自训;V8 x64-rl.profile 用官方(revision 一致时)。


六、换机器训练 Chrome PGO 要带什么

  1. 同 commit 完整 src
  2. Phase 1 的 out/(去掉 obj/*.pdb 可瘦身)
  3. llvm-profdata
  4. 定制主程序若不叫 chrome.exe,需与 generate_profile.py 期望名对齐(复制/改名)

七、Phase 2 编译慢

场景 体验
首次全量 很慢
同目录无改动 增量几乎为 0
CI 每次 clean 每次都慢

八、官方 V8 下载(示例 14.8.178.4)

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/14.8.178.4/meta.json

https://storage.googleapis.com/chromium-v8-builtins-pgo/by-version/14.8.178.4/x64-rl.profile

放置:

v8/tools/builtins-pgo/profiles/meta.json

v8/tools/builtins-pgo/profiles/x64-rl.profile


九、总结

  1. Chrome PGO 与 V8 Builtins PGO 两套文件、两套流程。
  2. Phase 1 要先过 链接 + benchmark;fork 与 148 返回类型 不一致会在 PGO 插桩下触发 lld-link。
  3. Phase 2 常见缺 x64-rl.profile;Windows 勿用 x64.profile 顶替。
  4. 官方 V8 profile 在 revision 对齐时可直接用。
  5. CI 检查 gn gen 是否传入 v8_enable_builtins_optimization 等参数。

代码示例中的 FORK_EXTENSIONS_TOOLBAR 为脱敏占位符,请替换为你们仓库实际的 fork 宏名。