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
+38 -16
View File
@@ -726,23 +726,28 @@ function targetNodeFromStateDir(){
return index>=0&&parts[index+1]?parts[index+1]:null;
}
function perfFrameKey(frame){
return {functionName:short(frame?.functionName||frame?.name||'(anonymous)',48),url:short(frame?.url||frame?.sourceURL||frame?.scriptUrl||'',72),scriptId:frame?.scriptId??null,lineNumber:frame?.lineNumber??frame?.line??null,columnNumber:frame?.columnNumber??frame?.column??null,valuesRedacted:true};
return {functionName:short(frame?.functionName||frame?.name||'(anonymous)',48),url:short(frame?.url||frame?.sourceURL||frame?.scriptUrl||'',72),sourceFile:short(frame?.sourceFile||'',48),sourceMapStatus:frame?.sourceMapStatus??null,scriptId:frame?.scriptId??null,lineNumber:frame?.lineNumber??frame?.line??null,columnNumber:frame?.columnNumber??frame?.column??null,valuesRedacted:true};
}
function compactProfileHotspot(item){
return {functionName:short(item?.functionName||item?.name||'(anonymous)',64),url:short(item?.url||item?.sourceURL||item?.scriptUrl||'',88),scriptId:item?.scriptId??null,lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,selfTimeMs:item?.selfTimeMs??item?.selfMs??null,totalTimeMs:item?.totalTimeMs??item?.totalMs??null,hitCount:item?.hitCount??item?.sampleCount??null,captureCount:item?.captureCount??null,valuesRedacted:true};
return {functionName:short(item?.functionName||item?.name||'(anonymous)',64),url:short(item?.url||item?.sourceURL||item?.scriptUrl||'',88),sourceFile:short(item?.sourceFile||'',48),sourceMapStatus:item?.sourceMapStatus??null,scriptId:item?.scriptId??null,lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,selfTimeMs:item?.selfTimeMs??item?.selfMs??null,totalTimeMs:item?.totalTimeMs??item?.totalMs??null,hitCount:item?.hitCount??item?.sampleCount??null,captureCount:item?.captureCount??null,valuesRedacted:true};
}
function compactProfileStack(item){
const frames=Array.isArray(item?.frames)?item.frames.slice(0,3).map(perfFrameKey):[];
return {functionName:short(item?.functionName||item?.name||'(anonymous)',64),url:short(item?.url||item?.sourceURL||item?.scriptUrl||'',88),scriptId:item?.scriptId??null,lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,selfTimeMs:item?.selfTimeMs??item?.selfMs??null,totalTimeMs:item?.totalTimeMs??item?.totalMs??null,hitCount:item?.hitCount??item?.sampleCount??null,frameCount:Array.isArray(item?.frames)?item.frames.length:null,frames,framesOmitted:Math.max(0,(Array.isArray(item?.frames)?item.frames.length:0)-frames.length),valuesRedacted:true};
}
function compactScriptHotspot(item){
return {sourceFunctionName:short(item?.sourceFunctionName||item?.functionName||item?.invoker||'(anonymous)',64),sourceURL:short(item?.sourceURL||item?.url||'',88),lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,totalDurationMs:item?.totalDurationMs??item?.durationMs??null,count:item?.count??item?.hitCount??null,invoker:short(item?.invoker||'',64),valuesRedacted:true};
return {sourceFunctionName:short(item?.sourceFunctionName||item?.functionName||item?.invoker||'(anonymous)',64),sourceURL:short(item?.sourceURL||item?.url||'',88),sourceFile:short(item?.sourceFile||'',48),sourceMapStatus:item?.sourceMapStatus??null,sourceCharPosition:item?.sourceCharPosition??null,lineNumber:item?.lineNumber??item?.line??null,columnNumber:item?.columnNumber??item?.column??null,totalDurationMs:item?.totalDurationMs??item?.durationMs??null,count:item?.count??item?.hitCount??null,invoker:short(item?.invoker||'',64),valuesRedacted:true};
}
function compactPerfEvent(item){
return {ts:item?.ts??null,kind:item?.kind??null,durationMs:item?.durationMs??null,pageRole:item?.pageRole??null,pageId:item?.pageId??null,sampleSeq:item?.sampleSeq??item?.seq??null,scriptCount:item?.scriptCount??(Array.isArray(item?.scripts)?item.scripts.length:null),url:short(item?.url||item?.sourceURL||'',88),valuesRedacted:true};
return {ts:item?.ts??null,startAt:item?.startAt??null,endAt:item?.endAt??null,kind:item?.kind??null,durationMs:item?.durationMs??null,pageRole:item?.pageRole??null,pageId:item?.pageId??null,sampleSeq:item?.sampleSeq??item?.seq??null,scriptCount:item?.scriptCount??(Array.isArray(item?.scripts)?item.scripts.length:null),url:short(item?.url||item?.sourceURL||'',88),valuesRedacted:true};
}
function compactPerfCapture(item){
return {ts:item?.ts??null,commandId:item?.commandId??null,status:item?.status??null,durationMs:item?.durationMs??item?.profileDurationMs??null,sampleCount:item?.sampleCount??item?.samples??null,nodeCount:item?.nodeCount??item?.nodes??null,file:short(item?.file||item?.path||item?.relativePath||'',88),valuesRedacted:true};
return {ts:item?.ts??null,startAt:item?.startAt??null,endAt:item?.endAt??null,captureId:item?.captureId??null,commandId:item?.commandId??null,status:item?.status??null,durationMs:item?.durationMs??item?.profileDurationMs??null,sampleCount:item?.sampleCount??item?.profileSampleCount??item?.samples??null,nodeCount:item?.nodeCount??item?.nodes??null,file:short(item?.file||item?.path||item?.relativePath||'',88),valuesRedacted:true};
}
function compactPerformanceWindow(item){
const capture=item?.capture&&typeof item.capture==='object'?item.capture:null;
const script=item?.topLoafScript&&typeof item.topLoafScript==='object'?item.topLoafScript:null;
return {kind:item?.kind??null,ts:item?.ts??null,startAt:item?.startAt??null,endAt:item?.endAt??null,durationMs:item?.durationMs??null,blockingDurationMs:item?.blockingDurationMs??null,pageRole:item?.pageRole??null,pageId:item?.pageId??null,sampleSeq:item?.sampleSeq??null,cpuProfileCoverageStatus:item?.cpuProfileCoverageStatus??null,coverageReason:short(item?.coverageReason||'',90),capture:capture?{captureId:capture.captureId??null,commandId:capture.commandId??null,startAt:capture.startAt??null,endAt:capture.endAt??null,durationMs:capture.durationMs??null,windowDistanceMs:capture.windowDistanceMs??null,profileSampleCount:capture.profileSampleCount??null,valuesRedacted:true}:null,topLoafScript:script?compactScriptHotspot(script):null,relatedNetwork:Array.isArray(item?.relatedNetwork)?item.relatedNetwork.slice(0,1).map((row)=>({ts:row?.ts??null,sampleSeq:row?.sampleSeq??null,phase:row?.phase??null,method:row?.method??null,status:row?.status??null,urlPath:short(row?.urlPath||'',56),bodyByteCount:row?.bodyByteCount??null,traceId:short(row?.traceId||'',32),routeSessionId:short(row?.routeSessionId||'',32),valuesRedacted:true})):[],relatedSamples:Array.isArray(item?.relatedSamples)?item.relatedSamples.slice(0,1).map((row)=>({ts:row?.ts??null,sampleSeq:row?.sampleSeq??null,pageRole:row?.pageRole??null,path:short(row?.path||'',40),routeSessionId:short(row?.routeSessionId||'',32),activeSessionId:short(row?.activeSessionId||'',32),traceIds:Array.isArray(row?.traceIds)?row.traceIds.slice(0,1).map((id)=>short(id,32)):[],valuesRedacted:true})):[],profileEvidence:item?.profileEvidence?{evidenceScope:item.profileEvidence.evidenceScope??null,topFunctions:Array.isArray(item.profileEvidence.topFunctions)?item.profileEvidence.topFunctions.slice(0,1).map(compactProfileHotspot):[],valuesRedacted:true}:null,valuesRedacted:true};
}
function compactPerfFinding(item){
return {severity:item?.severity??item?.level??null,id:short(item?.id||item?.kind||item?.code||'',56),count:item?.count??item?.sampleCount??null,rootCause:item?.rootCause??item?.rootCauseStatus??null,summary:short(item?.summary||item?.message||'',110),valuesRedacted:true};
@@ -812,10 +817,12 @@ function performanceSummaryFromReport(){
longTasks:Array.isArray(perf.longTasks)?perf.longTasks.slice(0,3).map(compactPerfEvent):[],
longAnimationFrames:Array.isArray(perf.longAnimationFrames)?perf.longAnimationFrames.slice(0,3).map(compactPerfEvent):[],
eventLoopGaps:Array.isArray(perf.eventLoopGaps)?perf.eventLoopGaps.slice(0,3).map(compactPerfEvent):[],
scriptHotspots:Array.isArray(perf.scriptHotspots)?perf.scriptHotspots.slice(0,5).map(compactScriptHotspot):[],
profileHotspots:Array.isArray(perf.profileHotspots)?perf.profileHotspots.slice(0,5).map(compactProfileHotspot):[],
profileStacks:Array.isArray(perf.profileStacks)?perf.profileStacks.slice(0,3).map(compactProfileStack):[],
performanceWindows:Array.isArray(perf.performanceWindows)?perf.performanceWindows.slice(0,3).map(compactPerformanceWindow):[],
scriptHotspots:Array.isArray(perf.scriptHotspots)?perf.scriptHotspots.slice(0,4).map(compactScriptHotspot):[],
profileHotspots:Array.isArray(perf.profileHotspots)?perf.profileHotspots.slice(0,4).map(compactProfileHotspot):[],
profileStacks:Array.isArray(perf.profileStacks)?perf.profileStacks.slice(0,2).map(compactProfileStack):[],
captures:captureRows.slice(-3).map(compactPerfCapture),
sourceAttribution:perf.sourceAttribution&&typeof perf.sourceAttribution==='object'?{sourceMapStatus:perf.sourceAttribution.sourceMapStatus??null,note:short(perf.sourceAttribution.note||'',120),sourceFiles:Array.isArray(perf.sourceAttribution.sourceFiles)?perf.sourceAttribution.sourceFiles.slice(0,4):[],valuesRedacted:true}:null,
findings,
toolFindings:performanceToolFindings(),
valuesRedacted:true
@@ -840,26 +847,41 @@ function renderPerformanceSummary(perf){
lines.push('pending performanceCapture: '+(pending||'-'));
}
if(runner.notRunningOrFailed===true) lines.push('runner state: not-running/failed for performance evidence; treat missing CPU profile as tool state, not as proof that no CPU hotspot exists.');
lines.push('window correlation='+String(s.windowCorrelationCount??perf.performanceWindows.length??0)+' covered='+String(s.cpuProfileWindowCoveredCount??0)+' overlapped='+String(s.cpuProfileWindowOverlappedCount??0)+' missed='+String(s.cpuProfileWindowMissedCount??0)+' missing='+String(s.cpuProfileWindowMissingCount??0)+' sourceMap='+String(s.sourceMapStatus||perf.sourceAttribution?.sourceMapStatus||'-'));
if(perf.sourceAttribution?.note) lines.push('source attribution: '+perf.sourceAttribution.note);
lines.push('','CPU profile hotspots');
if(perf.profileHotspots.length===0) lines.push(mode.noCpuProfile===true?'(no CPU profile hotspots; no completed performanceCapture artifact in this run)':'-');
for(const item of perf.profileHotspots.slice(0,10)) lines.push(String(item.selfTimeMs??0)+'ms self '+short(item.functionName||'(anonymous)',44)+' '+short(item.url||item.scriptId||'-',92)+' line='+String(item.lineNumber??'-')+' captures='+String(item.captureCount??'-'));
for(const item of perf.profileHotspots.slice(0,4)) lines.push(String(item.selfTimeMs??0)+'ms self '+short(item.functionName||'(anonymous)',40)+' '+short(item.sourceFile||item.url||item.scriptId||'-',70)+' line='+String(item.lineNumber??'-')+' sourceMap='+String(item.sourceMapStatus||'-')+' captures='+String(item.captureCount??'-'));
lines.push('','CPU profile stacks');
if(perf.profileStacks.length===0) lines.push('-');
for(const item of perf.profileStacks.slice(0,5)){
const frames=Array.isArray(item.frames)?item.frames.slice(0,5).map((frame)=>short(frame.functionName||'(anonymous)',36)+'@'+short(frame.url||frame.scriptId||'-',54)+':'+String(frame.lineNumber??'-')).join(' <- '):'-';
for(const item of perf.profileStacks.slice(0,2)){
const frames=Array.isArray(item.frames)?item.frames.slice(0,3).map((frame)=>short(frame.functionName||'(anonymous)',28)+'@'+short(frame.sourceFile||frame.url||frame.scriptId||'-',36)+':'+String(frame.lineNumber??'-')).join(' <- '):'-';
lines.push(String(item.selfTimeMs??0)+'ms self '+short(item.functionName||'(anonymous)',44)+' line='+String(item.lineNumber??'-')+' frames='+frames+(item.framesOmitted?(' omitted='+String(item.framesOmitted)):''));
}
lines.push('','LoAF script hotspots');
if(perf.scriptHotspots.length===0) lines.push('-');
for(const item of perf.scriptHotspots.slice(0,10)) lines.push(String(item.totalDurationMs??0)+'ms total count='+String(item.count??0)+' '+short(item.sourceFunctionName||item.invoker||'(anonymous)',44)+' '+short(item.sourceURL||'-',92)+' line='+String(item.lineNumber??'-'));
for(const item of perf.scriptHotspots.slice(0,4)) lines.push(String(item.totalDurationMs??0)+'ms total count='+String(item.count??0)+' '+short(item.sourceFunctionName||item.invoker||'(anonymous)',40)+' '+short(item.sourceFile||item.sourceURL||'-',70)+' line='+String(item.lineNumber??'-')+' char='+String(item.sourceCharPosition??'-')+' sourceMap='+String(item.sourceMapStatus||'-'));
lines.push('','Time-window correlation');
if(!Array.isArray(perf.performanceWindows)||perf.performanceWindows.length===0) lines.push('-');
for(const item of (perf.performanceWindows||[]).slice(0,3)){
const capture=item.capture?String(item.capture.captureId||item.capture.commandId||'-')+' dist='+String(item.capture.windowDistanceMs??'-')+'ms':'-';
const script=item.topLoafScript?short(item.topLoafScript.sourceFunctionName||item.topLoafScript.invoker||'(anonymous)',40)+'@'+short(item.topLoafScript.sourceFile||item.topLoafScript.sourceURL||'-',48)+':'+String(item.topLoafScript.lineNumber??'-'):'-';
const samples=(item.relatedSamples||[]).map((row)=>'#'+String(row.sampleSeq??'-')+' session='+String(row.activeSessionId||row.routeSessionId||'-')+' trace='+String((row.traceIds||[])[0]||'-')).join('; ');
const network=(item.relatedNetwork||[]).map((row)=>String(row.status??'-')+' '+String(row.method||'-')+' '+short(row.urlPath||'-',44)+' bytes='+String(row.bodyByteCount??'-')).join('; ');
lines.push(String(item.startAt||item.ts||'-')+' '+String(item.kind||'-')+' duration='+String(item.durationMs??'-')+'ms sample='+String(item.sampleSeq??'-')+' cpuWindow='+String(item.cpuProfileCoverageStatus||'-')+' capture='+capture);
lines.push(' reason='+String(item.coverageReason||'-')+' script='+script);
if(samples) lines.push(' samples='+samples);
if(network) lines.push(' network='+network);
if(item.profileEvidence?.topFunctions?.length>0) lines.push(' same-capture topFunction='+item.profileEvidence.topFunctions.map((fn)=>String(fn.selfTimeMs??0)+'ms '+short(fn.functionName||'(anonymous)',28)+'@'+short(fn.sourceFile||fn.url||'-',32)).join('; '));
}
lines.push('','Longest events');
for(const item of [...perf.longTasks.slice(0,4),...perf.longAnimationFrames.slice(0,4),...perf.eventLoopGaps.slice(0,4)].sort((a,b)=>Number(b.durationMs??0)-Number(a.durationMs??0)).slice(0,10)) lines.push(String(item.ts||'-')+' '+String(item.kind||'-')+' duration='+String(item.durationMs??'-')+'ms role='+String(item.pageRole||'-')+' sample='+String(item.sampleSeq??'-')+' scripts='+String(item.scriptCount??0));
for(const item of [...perf.longTasks.slice(0,3),...perf.longAnimationFrames.slice(0,3),...perf.eventLoopGaps.slice(0,3)].sort((a,b)=>Number(b.durationMs??0)-Number(a.durationMs??0)).slice(0,6)) lines.push(String(item.ts||'-')+' '+String(item.kind||'-')+' duration='+String(item.durationMs??'-')+'ms role='+String(item.pageRole||'-')+' sample='+String(item.sampleSeq??'-')+' scripts='+String(item.scriptCount??0));
lines.push('','Findings');
if(perf.findings.length===0) lines.push('-');
for(const item of perf.findings.slice(0,12)) lines.push(String(item.severity||'-')+': '+String(item.id||item.kind||'-')+' count='+String(item.count??'-')+' '+short(item.summary||item.message||'',150));
for(const item of perf.findings.slice(0,6)) lines.push(String(item.severity||'-')+': '+String(item.id||item.kind||'-')+' count='+String(item.count??'-')+' '+short(item.summary||item.message||'',120));
lines.push('','Tool status');
if(!Array.isArray(perf.toolFindings)||perf.toolFindings.length===0) lines.push('-');
for(const item of (perf.toolFindings||[]).slice(0,10)) lines.push(String(item.severity||'-')+': '+String(item.id||'-')+' count='+String(item.count??'-')+' '+short(item.summary||'',150));
for(const item of (perf.toolFindings||[]).slice(0,6)) lines.push(String(item.severity||'-')+': '+String(item.id||'-')+' count='+String(item.count??'-')+' '+short(item.summary||'',120));
lines.push('','NEXT',' capture: bun scripts/cli.ts web-probe observe command '+(manifest.jobId||'<observer>')+' --type performanceCapture --duration-ms 5000 --wait-ms 8000',' analyze: bun scripts/cli.ts web-probe observe analyze '+(manifest.jobId||'<observer>'),'DISCLOSURE source=existing artifacts valuesRedacted=true; this view does not start browser/probe or mutate runtime.');
return lines.join('\\n');
}
@@ -890,7 +912,7 @@ function renderProjectSummary(project){
const rows=turnSummaryRows();
if(view==='performance-summary'){
const perf=performanceSummaryFromReport();
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:perf.summary,evidenceMode:perf.evidenceMode,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,renderedText:renderPerformanceSummary(perf),sourceFiles:['performance-events.jsonl','artifacts.jsonl','analysis/report.json','commands/pending/*.json','commands/processing/*.json','heartbeat.json','manifest.json'],drillDown:'bun scripts/cli.ts web-probe observe collect '+String(manifest.jobId||'<observer>')+' --view files --file analysis/report.json',valuesRedacted:true}));
console.log(JSON.stringify({ok:true,command:'web-probe-observe collect',view,stateDir:dir,summary:perf.summary,evidenceMode:perf.evidenceMode,artifactFileCount:files.length,skippedFileCount:skippedFiles.length,renderedText:renderPerformanceSummary(perf),sourceFiles:['performance-events.jsonl','network.jsonl','samples.jsonl','artifacts.jsonl','analysis/report.json','commands/pending/*.json','commands/processing/*.json','heartbeat.json','manifest.json'],drillDown:'bun scripts/cli.ts web-probe observe collect '+String(manifest.jobId||'<observer>')+' --view files --file analysis/report.json',valuesRedacted:true}));
process.exit(0);
}
if(view==='project-summary'||view==='project-mdtodo-summary'){