Skip to content

Attribute performance reports via JS self-profiling and denoise long-task prompts / 性能报告接入采样归因并为长任务提示降噪#6374

Merged
SivanCola merged 2 commits into
esengine:main-v2from
SivanCola:feature/perf-longtask-attribution
Jul 12, 2026
Merged

Attribute performance reports via JS self-profiling and denoise long-task prompts / 性能报告接入采样归因并为长任务提示降噪#6374
SivanCola merged 2 commits into
esengine:main-v2from
SivanCola:feature/perf-longtask-attribution

Conversation

@SivanCola

Copy link
Copy Markdown
Collaborator

Problem

Performance-pressure diagnostics dominate crash triage volume (~18k reports over 30 days across performance.longtask / performance.lag / performance.heap, versus 400 for the highest-volume real crash group), yet none of them carry a stack — longtask PerformanceObserver entries have no attribution beyond a culprit descriptor, so the reports are unactionable. Separately, the cumulative long-task rule (>=3 tasks && >=1.5s total per 60s) fires on routine streaming-render bursts (e.g. 16 tasks of 70–240ms), which is a large part of that volume.

Changes

Attribution (frontend + Go asset server)

  • Run a rolling JS Self-Profiling sampler (10ms interval, 60s buffer) while the performance monitor is installed. When a performance prompt fires, stop the profiler, keep only samples that landed inside recorded long-task windows (plus the lag spike window itself for event-loop reports), aggregate leaf frames by self-time, and append a long task top frames (sampled) section to the report, e.g. 42x post (wails://wails/assets/vendor-markdown.js:1:130216).
  • Serve Document-Policy: js-profiling from the Wails asset server (jsProfilingMiddleware, chained before the workspace-media middleware). Chromium WebViews (WebView2 — the dominant platform for these reports) honor it; WebKit ignores both the header and the API, so macOS/Linux keep today's report shape.
  • Record longtask culprit attribution on each sample, but only surface anomalies (cross-context culprits / named containers); self/window carries no signal and is omitted.
  • The report privacy note now mentions sampled app function names (en / zh / zh-TW).

Denoise

  • Raise the cumulative long-task prompt budget from 1.5s to 3s per 60s window (single-task threshold stays 800ms). The trigger is extracted as shouldPromptForLongTasks with direct test coverage.

Backward compatibility

Surface Old-data behavior Conclusion
CrashPayload (schemaVersion: 2) Unchanged fields; new content is additive lines inside the freeform message string Safe — server/message consumers see the same schema
PerformanceSnapshot In-memory only, never persisted; new optional fields (attribution, longTaskFrames) Safe
localStorage reasonix:perf-reported Format untouched Safe
Asset responses New Document-Policy header; WebKit/older WebViews ignore unknown policies Safe
Profiler API absent (WebKit, older WebView2) startLongTaskProfiler no-ops / catches, prompt path falls back to the synchronous unattributed report Safe

Verification

  • tsx src/__tests__/crash-reporting.test.ts — 60 passed, 0 failed (new coverage: threshold boundaries incl. the 16×~100ms streaming burst staying silent, attribution formatting, trace aggregation windowing/capping, report context formatting)
  • pnpm test (full frontend suite) — pass
  • tsc --noEmit -p tsconfig.test.json — pass
  • cd desktop && go build ./... && go test . — pass (rebased onto latest main-v2, which also touched app.go and the locale files)

Follow-up (separate PR): the sampled data is expected to confirm full-document markdown re-parse per streaming frame as the dominant long-task source; block-level memoization of completed markdown blocks is the planned fix.

…ompts

Production telemetry shows performance-pressure reports dominating crash
triage (~18k in 30 days) while carrying no actionable stacks, and the
cumulative long-task rule fires on routine streaming-render bursts.

- Sample JS stacks with the Self-Profiling API: a rolling 10ms/60s
  profiler is stopped when a performance prompt fires, and samples that
  landed inside recorded long-task windows (plus the lag spike itself
  for event-loop reports) are aggregated into a leaf-frame self-time
  top list, reported as "long task top frames (sampled)".
- Serve Document-Policy: js-profiling from the Wails asset server so
  Chromium WebViews (WebView2) expose the Profiler API; WebKit ignores
  both the header and the API and keeps the unattributed report shape.
- Record longtask culprit attribution, surfacing only cross-context
  anomalies (self/window culprits carry no signal).
- Raise the cumulative long-task prompt budget from 1.5s to 3s per 60s
  window; extract shouldPromptForLongTasks for direct coverage.
- Mention sampled app function names in the report privacy note.
@SivanCola SivanCola requested a review from esengine as a code owner July 12, 2026 17:07
@github-actions github-actions Bot added v2 Go rewrite (1.x) — main-v2 branch, active development desktop Wails desktop app (desktop/**) labels Jul 12, 2026
@SivanCola SivanCola merged commit f4c9bcf into esengine:main-v2 Jul 12, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

desktop Wails desktop app (desktop/**) v2 Go rewrite (1.x) — main-v2 branch, active development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant