Skip to content

Fix Safari dormant overhead in Live Debugger instrumentation - #438

Draft
watson wants to merge 1 commit into
watson/DEBUG-5787/add-benchfrom
watson/DEBUG-5793/fix-safari
Draft

Fix Safari dormant overhead in Live Debugger instrumentation#438
watson wants to merge 1 commit into
watson/DEBUG-5787/add-benchfrom
watson/DEBUG-5793/fix-safari

Conversation

@watson

Copy link
Copy Markdown
Contributor

What and why?

The Live Debugger runtime benchmark surfaced a large dormant-probe overhead on Safari: a tiny instrumented function cost ~8.9 ns/call (~1,100% over baseline) versus ~0 ns on Chrome and ~1.6 ns on Firefox. The root cause is that the transform hoisted each function's captured parameters into a per-call arrow closure (const $dd_eN = () => ({...})). A nested closure that captures parameters forces JavaScriptCore — Safari and all iOS browsers, since they're all WebKit — to heap-allocate the function's scope on every invocation, even when probes are dormant and the closure is never created or called. V8 and SpiderMonkey sink that allocation; JavaScriptCore does not, so it dominated the dormant overhead. Because every instrumented iOS app pays this cost on every call, it's worth eliminating.

How?

Inline the captured-arguments object literal ({a, b}) directly at each probe-guarded call site instead of hoisting it into a per-call closure. The object now lives inside the if (probe) / probe ? guards, so dormant calls allocate nothing; gating the closure behind the probe check was confirmed insufficient because the captured scope is still allocated. The active path is unchanged — the same arguments object is built the same number of times when a probe fires. Verified with the runtime benchmark: Safari per-call dormant overhead drops ~73% (8.9 → 2.4 ns) and the Hot workload from ~10 to ~7 ns, with no change on Chrome or Firefox. Transform unit-test snapshots and the plugin README/EXAMPLES output catalog are updated to match the new generated shape.

@watsonGraphite App

watson commented Jun 24, 2026

Copy link
Copy Markdown
ContributorAuthor

@datadog-official

datadog-officialBot commented Jun 24, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 937be82 | Docs | Datadog PR Page | Give us feedback!

@github-actions

github-actionsBot commented Jun 24, 2026

Copy link
Copy Markdown

Live Debugger Runtime Benchmark

SDK-loaded dormant-probe runtime overhead, measured against an uninstrumented bundle in the same browser session.

BrowserWorkloadQualityPer-call overhead upper
chromeHotclean<= 2.59 ns
chromeTinyclean<= 0.01 ns
firefoxHotclean<= 10.02 ns
firefoxTinyclean<= 0.01 ns
safariHotclean<= 5.71 ns
safariTinyclean<= 1.60 ns
Full diagnostics
browser workload quality per-call overhead upper overhead upper 95% CI A/A diag block CI acf(1) baseline instrumented samples
------- -------- ------- ----------------------- -------------- -------------- -------------- -------------- ------ --------- ------------ ------------------------------
chrome Hot clean <= 2.59 ns <= 5.21% 2.54..2.58 ns -0.02..0.02 ns 2.54..2.59 ns 0.04 50.858 ms 53.467 ms 102 (trim 20%, outliers 4.9%)
chrome Tiny clean <= 0.01 ns <= 0.12% -0.02..0.01 ns -0.00..0.03 ns -0.02..0.01 ns 0.03 76.842 ms 76.990 ms 102 (trim 20%, outliers 13.7%)
firefox Hot clean <= 10.02 ns <= 39.92% 9.99..10.02 ns -0.00..0.01 ns 9.99..10.02 ns -0.30 38.540 ms 53.940 ms 102 (trim 20%, outliers 2.9%)
firefox Tiny clean <= 0.01 ns <= 0.08% 0.00..0.01 ns -0.00..0.00 ns 0.00..0.01 ns 0.07 97.860 ms 97.910 ms 102 (trim 20%, outliers 11.8%)
safari Hot clean <= 5.71 ns <= 22.85% 5.70..5.71 ns 0.00..0.01 ns 5.70..5.71 ns 0.05 51.200 ms 62.900 ms 102 (trim 20%, outliers 4.9%)
safari Tiny clean <= 1.60 ns <= 42.75% 1.59..1.60 ns -0.00..0.00 ns 1.59..1.60 ns -0.08 37.520 ms 53.570 ms 102 (trim 20%, outliers 7.8%)

Raw samples are in the live-debugger-runtime-bench-results artifact.

@watson
watsonforce-pushed the watson/DEBUG-5793/fix-safari branch from ce8320e to 52ed20dCompareJune 25, 2026 14:01
@watson
watsonforce-pushed the watson/DEBUG-5787/add-bench branch 2 times, most recently from d80cc4f to ba9d257CompareJune 26, 2026 11:49
@watson
watsonforce-pushed the watson/DEBUG-5793/fix-safari branch from 52ed20d to 01561a1CompareJune 26, 2026 11:49
The transform hoisted each instrumented function's captured parameters into a per-call arrow
closure (`const $dd_eN = () => ({...})`). A nested closure that captures parameters forces
JavaScriptCore (Safari and all iOS browsers) to heap-allocate the function's scope on every
invocation -- even when probes are dormant and the closure is never created or called. V8 and
SpiderMonkey sink that allocation; JavaScriptCore does not, so it dominated the dormant runtime
overhead.
The runtime benchmark measured ~8.9 ns/call of dormant overhead on Safari for a tiny function
(vs ~0 on Chrome and ~1.6 on Firefox). Gating the closure behind the probe check does not help,
because the captured scope is still allocated on every call.
Instead, inline the captured-arguments object literal (`{a, b}`) directly at each probe-guarded
call site. The object now lives inside the `if (probe)` / `probe ? ` guards, so dormant calls
allocate nothing. The active path is unchanged: the same arguments object is built the same number
of times when a probe fires.
Safari per-call dormant overhead drops ~73% (8.9 -> 2.4 ns); the Hot workload drops from ~10 to
~7 ns. Chrome and Firefox are unchanged. Transform snapshots and the README/EXAMPLES output
catalog are updated to match the new shape.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@watson