#51852: build: calibrate PGO collection workloads and fix renderer profile loss
What
Fixes two independent problems in the PGO profile collection pipeline that combined to produce the badly-shaped macOS arm64 profiles consumed by v42.3.1 (and generated for 43-x-y):
1. Renderer profile loss (the big one). The macOS collection runs the instrumented app sandboxed, and sandboxed child processes fail their exit-time profraw write (LLVM Profile Error: ... Operation not permitted). Which processes lose is a per-run race — the 42-x-y and 43-x-y collection runs both lost their web-benchmark renderer data, shipping profiles where Blink is essentially absent from the hot set (4 DOM functions in the top-50k vs ~400 expected). This is the root cause of the v42.3.1 Speedometer regression on Apple Silicon. Fixed with %c continuous-mode profiles on Darwin (counters live in an mmap established before sandbox lockdown — no exit-time write to lose) plus --no-sandbox on the macOS collect step, matching what the Linux step already does.
2. Count-budget distortion. The synthetic workloads were capped by wall time, so their tight loops dominated the merged profile: the top 2 blocks held ~10% of all counts, inflating the global hot threshold and demoting real Blink work out of the hot tier. Hotness is a threshold (~10^5 counts), not a share — capping the loops by iterations keeps every electron path 2–3 orders of magnitude above the bar while ending the distortion. MotionMark is soft-capped at 120s (it was 5.4 minutes — 57% of all web-benchmark time — and doubled Skia's weight vs Chrome's corpus), and the reclaimed time funds two extra Speedometer runs. A new async-churn workload (250k deliberately tiny async ops) keeps the per-async-op machinery (AsyncWrap, BaseObject lifecycle, stream plumbing) trained, since its counts ride on async op volume rather than loop iterations.
Also: the profraw directory is now cleaned at collection start — stale %m pool files from a previous run silently merge their counters into a new collection.
Validation
Instrumented 42-x-y build, same scripts CI runs, before → after:
| Metric | before | after |
|---|---|---|
| profraw files collected | 9 (renderers lost, 5 write errors) | 23+ (zero errors) |
| blocks holding 90% of counts | 1,710 | 14,000+ |
| Blink DOM functions in top-50k | 4 | ~404 (Chrome-profile parity) |
| Blink share of hot mass | 14% | ~33–35% |
| top-2-block share | ~10% | 2.6% |
| node serialization paths | 1.1B-count spikes | hot at sane magnitudes (StringBytes::Write 410M, contextBridge 15M) |
A useful side effect: the simdutf dispatcher drops below the hot threshold, so profiles from the fixed collection no longer trigger the LLVM unroller miscompile that #51848 guards against (that flag stays as defense-in-depth until the upstream fix rolls in).
Suggested follow-up (not in this PR): a shape gate in the generation workflow — fail the run if blocks@90% < 8,000 or Blink DOM functions in the top-50k < 100 — so a malformed profile can never silently ship again.
Notes: none
Backports
Semver Impact
Semantic Versioning helps users understand the impact of updates:
- Major (X.y.z): Breaking changes that may require code modifications
- Minor (x.Y.z): New features that maintain backward compatibility
- Patch (x.y.Z): Bug fixes that don't change the API
- None: Changes that don't affect using facing parts of Electron