本文基于一次真实升核(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 →
.profraw→llvm-profdata merge→profile.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():
- 编译器对
vector→flat_set做隐式转换,构造 临时flat_set - 函数返回的是 临时对象的引用(本身已是危险写法)
- PGO Phase 1 插桩 下,
flat_tree析构路径 无法如 Release 那样内联 - 链接器找不到对应符号 →
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=1,autoninja -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.json 里 revision 需与 DEPS 的 v8_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 要带什么
- 同 commit 完整
src - Phase 1 的
out/(去掉obj/、*.pdb可瘦身) llvm-profdata- 定制主程序若不叫
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
九、总结
- Chrome PGO 与 V8 Builtins PGO 两套文件、两套流程。
- Phase 1 要先过 链接 + benchmark;fork 与 148 返回类型 不一致会在 PGO 插桩下触发 lld-link。
- Phase 2 常见缺
x64-rl.profile;Windows 勿用x64.profile顶替。 - 官方 V8 profile 在 revision 对齐时可直接用。
- CI 检查
gn gen是否传入v8_enable_builtins_optimization等参数。
代码示例中的 FORK_EXTENSIONS_TOOLBAR 为脱敏占位符,请替换为你们仓库实际的 fork 宏名。