fix(perf): keep probe monitor metrics visible on Windows (#933)

Stop replacing the whole Monitor tab when CPU/RSS sampling is unsupported;
show pipeline, UI rate, and analysis sections with an inline platform note.
Also compute UI diagRates when the Rust snapshot returns supported: false.
This commit is contained in:
cucadmuh
2026-05-31 02:55:34 +03:00
committed by GitHub
parent fc7964fb07
commit 77ecc8ddfe
2 changed files with 39 additions and 36 deletions
@@ -25,6 +25,7 @@ export default function SidebarPerfProbeMonitorTab() {
const analysisPinned = usePipelineOverlayPinned('pipeline:analysis'); const analysisPinned = usePipelineOverlayPinned('pipeline:analysis');
const coverPinned = usePipelineOverlayPinned('pipeline:cover'); const coverPinned = usePipelineOverlayPinned('pipeline:cover');
const cpu = live.cpu; const cpu = live.cpu;
const cpuSupported = cpu?.supported === true;
const collecting = live.collecting && cpu == null; const collecting = live.collecting && cpu == null;
const includeThreadGroups = usePerfLiveIncludeThreadGroups(); const includeThreadGroups = usePerfLiveIncludeThreadGroups();
const peakMemoryKbRef = useRef(1); const peakMemoryKbRef = useRef(1);
@@ -51,14 +52,6 @@ export default function SidebarPerfProbeMonitorTab() {
); );
} }
if (cpu && !cpu.supported) {
return (
<div className="perf-monitor-empty">
Live CPU/memory monitoring is unavailable on this platform or build.
</div>
);
}
const toggleLive = (id: PerfLiveOverlayPinId) => () => togglePerfLiveOverlayPin(id); const toggleLive = (id: PerfLiveOverlayPinId) => () => togglePerfLiveOverlayPin(id);
const livePinned = (id: PerfLiveOverlayPinId) => livePins.has(id); const livePinned = (id: PerfLiveOverlayPinId) => livePins.has(id);
@@ -94,7 +87,13 @@ export default function SidebarPerfProbeMonitorTab() {
/> />
</PerfProbeMetricSection> </PerfProbeMetricSection>
{cpu && ( {cpu && !cpuSupported && (
<div className="perf-monitor-empty perf-monitor-empty--inline">
Live CPU and RSS sampling is unavailable on this platform. Pipeline, UI rate, and analysis metrics below still work.
</div>
)}
{cpuSupported && cpu && (
<> <>
<PerfProbeMetricSection title="CPU — processes"> <PerfProbeMetricSection title="CPU — processes">
<PerfProbeMetricCard <PerfProbeMetricCard
+31 -27
View File
@@ -96,6 +96,29 @@ function readUiCounters(): { progress: number; waveform: number; home: number }
}; };
} }
function buildAnalysisDiag(): PerfAnalysisDiag {
return {
tracksPerMinute: getAnalysisTracksPerMinute(),
lastTotalMs: snapshot.analysis?.lastTotalMs ?? null,
lastFetchMs: snapshot.analysis?.lastFetchMs ?? null,
lastSeedMs: snapshot.analysis?.lastSeedMs ?? null,
lastBpmMs: snapshot.analysis?.lastBpmMs ?? null,
};
}
function nextDiagRates(
nextCounters: { progress: number; waveform: number; home: number },
now: number,
): PerfDiagRates | null {
if (!prevCounters || prevCountersAt <= 0) return snapshot.diagRates;
const dt = Math.max(0.25, (now - prevCountersAt) / 1000);
return {
progress: (nextCounters.progress - prevCounters.progress) / dt,
waveform: (nextCounters.waveform - prevCounters.waveform) / dt,
home: (nextCounters.home - prevCounters.home) / dt,
};
}
async function pollOnce(): Promise<void> { async function pollOnce(): Promise<void> {
const generation = pollGeneration; const generation = pollGeneration;
const now = Date.now(); const now = Date.now();
@@ -103,17 +126,16 @@ async function pollOnce(): Promise<void> {
const snap = await invoke<ProcSnapshot>('performance_cpu_snapshot', buildPerfCpuSnapshotRequest()); const snap = await invoke<ProcSnapshot>('performance_cpu_snapshot', buildPerfCpuSnapshotRequest());
if (generation !== pollGeneration) return; if (generation !== pollGeneration) return;
const nextCounters = readUiCounters();
const diagRates = nextDiagRates(nextCounters, now);
prevCounters = nextCounters;
prevCountersAt = now;
if (!snap.supported) { if (!snap.supported) {
setSnapshot({ setSnapshot({
cpu: { app: 0, webkit: 0, supported: false, memory: [], threadCpu: [] }, cpu: { app: 0, webkit: 0, supported: false, memory: [], threadCpu: [] },
diagRates: snapshot.diagRates, diagRates,
analysis: { analysis: buildAnalysisDiag(),
tracksPerMinute: getAnalysisTracksPerMinute(),
lastTotalMs: snapshot.analysis?.lastTotalMs ?? null,
lastFetchMs: snapshot.analysis?.lastFetchMs ?? null,
lastSeedMs: snapshot.analysis?.lastSeedMs ?? null,
lastBpmMs: snapshot.analysis?.lastBpmMs ?? null,
},
collecting: false, collecting: false,
updatedAt: now, updatedAt: now,
}); });
@@ -160,30 +182,12 @@ async function pollOnce(): Promise<void> {
} }
} }
const nextCounters = readUiCounters();
let diagRates = snapshot.diagRates;
if (prevCounters && prevCountersAt > 0) {
const dt = Math.max(0.25, (now - prevCountersAt) / 1000);
diagRates = {
progress: (nextCounters.progress - prevCounters.progress) / dt,
waveform: (nextCounters.waveform - prevCounters.waveform) / dt,
home: (nextCounters.home - prevCounters.home) / dt,
};
}
prevCounters = nextCounters;
prevCountersAt = now;
prevProc = snap; prevProc = snap;
setSnapshot({ setSnapshot({
cpu, cpu,
diagRates, diagRates,
analysis: { analysis: buildAnalysisDiag(),
tracksPerMinute: getAnalysisTracksPerMinute(),
lastTotalMs: snapshot.analysis?.lastTotalMs ?? null,
lastFetchMs: snapshot.analysis?.lastFetchMs ?? null,
lastSeedMs: snapshot.analysis?.lastSeedMs ?? null,
lastBpmMs: snapshot.analysis?.lastBpmMs ?? null,
},
collecting: false, collecting: false,
updatedAt: now, updatedAt: now,
}); });