fix: correlate webprobe performance windows

This commit is contained in:
Codex
2026-07-02 13:54:14 +00:00
parent 9fd8fc6f9f
commit c2d3814415
5 changed files with 580 additions and 28 deletions
@@ -51,6 +51,9 @@ test("performance-summary labels LoAF-only evidence when CPU profile capture is
scriptHotspots: [{
sourceFunctionName: "Response.json.then",
sourceURL: "https://hwlab.example.test/app.js",
sourceFile: "app.js",
sourceCharPosition: 123456,
sourceMapStatus: "missing",
totalDurationMs: 2108,
count: 2,
valuesRedacted: true,
@@ -99,4 +102,159 @@ test("performance-summary labels LoAF-only evidence when CPU profile capture is
assert.match(text, /pending performanceCapture: cmd-perf\(pending\)/u);
assert.match(text, /runner state: not-running\/failed/u);
assert.match(text, /no CPU profile hotspots; no completed performanceCapture artifact/u);
assert.match(text, /sourceMap=missing/u);
assert.match(text, /char=123456/u);
}, 20_000);
test("performance-summary renders CPU profile window misses and source attribution context", async () => {
const stateDir = await mkdtemp(join(tmpdir(), "unidesk-web-observe-performance-window-"));
await mkdir(join(stateDir, "analysis"), { recursive: true });
await writeFile(join(stateDir, "manifest.json"), JSON.stringify({ jobId: "webobs-perf-window-test", status: "completed" }) + "\n");
await writeFile(join(stateDir, "heartbeat.json"), JSON.stringify({ status: "completed", updatedAt: "2026-07-02T12:08:06Z" }) + "\n");
await writeFile(join(stateDir, "performance-events.jsonl"), "");
await writeFile(join(stateDir, "analysis", "report.json"), JSON.stringify({
manifest: { status: "completed" },
heartbeat: { status: "completed", updatedAt: "2026-07-02T12:08:06Z" },
frontendPerformance: {
summary: {
eventCount: 2,
longTaskCount: 0,
longAnimationFrameCount: 1,
eventLoopGapCount: 1,
captureCount: 1,
profileHotspotCount: 1,
scriptHotspotCount: 1,
windowCorrelationCount: 1,
cpuProfileWindowCoveredCount: 0,
cpuProfileWindowOverlappedCount: 0,
cpuProfileWindowMissedCount: 1,
cpuProfileWindowMissingCount: 0,
maxLongAnimationFrameMs: 1811,
maxEventLoopGapMs: 4490,
longAnimationFrameRedMs: 200,
eventLoopGapRedMs: 1000,
cpuProfileStatus: "captured-hotspots-outside-performance-window",
attributionMode: "cpu-profile-missed-performance-window",
sourceMapStatus: "missing",
valuesRedacted: true,
},
performanceWindows: [{
kind: "long-animation-frame",
ts: "2026-07-02T12:00:00Z",
startAt: "2026-07-02T12:00:00.000Z",
endAt: "2026-07-02T12:00:01.811Z",
durationMs: 1811,
sampleSeq: 7,
pageRole: "control",
pageId: "page-1",
cpuProfileCoverageStatus: "missed",
coverageReason: "nearest performanceCapture started after this frontend performance window ended",
capture: {
captureId: "perf-late",
commandId: "cmd-late",
startAt: "2026-07-02T12:00:10.000Z",
endAt: "2026-07-02T12:00:18.158Z",
durationMs: 8158,
windowDistanceMs: 8189,
profileSampleCount: 812,
valuesRedacted: true,
},
topLoafScript: {
sourceFunctionName: "Response.json.then",
sourceURL: "https://hwlab.example.test/assets/CodeWorkbenchView.js",
sourceFile: "CodeWorkbenchView.js",
sourceCharPosition: 456789,
lineNumber: 91,
sourceMapStatus: "missing",
totalDurationMs: 1811,
count: 5,
valuesRedacted: true,
},
relatedSamples: [{
ts: "2026-07-02T12:00:01Z",
sampleSeq: 7,
pageRole: "control",
activeSessionId: "ses_test",
traceIds: ["trc_test"],
valuesRedacted: true,
}],
relatedNetwork: [{
ts: "2026-07-02T12:00:00.500Z",
sampleSeq: 7,
phase: "response",
method: "GET",
status: 200,
urlPath: "/v1/workbench/sessions/ses_test/turns",
bodyByteCount: 1048576,
traceId: "trc_test",
valuesRedacted: true,
}],
valuesRedacted: true,
}],
scriptHotspots: [{
sourceFunctionName: "Response.json.then",
sourceURL: "https://hwlab.example.test/assets/CodeWorkbenchView.js",
sourceFile: "CodeWorkbenchView.js",
sourceCharPosition: 456789,
lineNumber: 91,
sourceMapStatus: "missing",
totalDurationMs: 1811,
count: 5,
valuesRedacted: true,
}],
profileHotspots: [{
functionName: "(program)",
sourceFile: "CodeWorkbenchView.js",
sourceMapStatus: "missing",
selfTimeMs: 120,
totalTimeMs: 200,
captureCount: 1,
valuesRedacted: true,
}],
profileStacks: [],
captures: [{
captureId: "perf-late",
commandId: "cmd-late",
startAt: "2026-07-02T12:00:10.000Z",
endAt: "2026-07-02T12:00:18.158Z",
durationMs: 8158,
profileSampleCount: 812,
valuesRedacted: true,
}],
longAnimationFrames: [{ ts: "2026-07-02T12:00:00Z", startAt: "2026-07-02T12:00:00.000Z", endAt: "2026-07-02T12:00:01.811Z", kind: "long-animation-frame", durationMs: 1811, sampleSeq: 7, pageRole: "control", scriptCount: 1 }],
sourceAttribution: {
sourceMapStatus: "missing",
note: "No source-map artifact is loaded by this analyzer; function/file/line fields are raw browser URLs or CPU profile callFrame locations.",
sourceFiles: [{ sourceFile: "CodeWorkbenchView.js", count: 2, sourceMapStatus: "missing", valuesRedacted: true }],
valuesRedacted: true,
},
},
findings: [],
}) + "\n");
const script = nodeWebObserveCollectViewNodeScript({
maxFiles: 100,
view: "performance-summary",
traceId: null,
sampleSeq: null,
timestamp: null,
turn: null,
commandId: null,
windowMs: null,
});
const result = spawnSync("bash", ["-lc", `state_dir=${shellQuote(stateDir)}\n${script}`], {
cwd: join(import.meta.dir, "../../.."),
encoding: "utf8",
});
assert.equal(result.status, 0, result.stderr || result.stdout);
const output = JSON.parse(result.stdout);
const text = String(output.renderedText ?? "");
assert.equal(output.summary.cpuProfileWindowMissedCount, 1);
assert.match(text, /window correlation=1 covered=0 overlapped=0 missed=1 missing=0 sourceMap=missing/u);
assert.match(text, /cpuWindow=missed/u);
assert.match(text, /nearest performanceCapture started after/u);
assert.match(text, /CodeWorkbenchView\.js/u);
assert.match(text, /bytes=1048576/u);
assert.match(text, /session=ses_test trace=trc_test/u);
}, 20_000);