feat(perf): explicit toggle for live thread-group CPU polling (#891)

* feat(perf): explicit toggle for live thread-group CPU polling

Replace implicit thread-group collection (section open / pin) with a persisted
checkbox so Linux /proc scans run only when the user opts in for diagnosis.

Fix IPC: pass includeThreadGroups (camelCase) so Tauri maps the flag to Rust;
reset the CPU baseline when the option changes so thread % deltas are valid.

* docs: CHANGELOG and credits for PR #891

* fix(perf): gate CHILD_RESCAN_EVERY to Linux/macOS only

Avoid dead_code warning on Windows where perf child-PID rescan is unused.
This commit is contained in:
cucadmuh
2026-05-29 19:18:21 +03:00
committed by GitHub
parent 9925771a86
commit 8ea0308dba
9 changed files with 96 additions and 28 deletions
@@ -2,12 +2,15 @@ import {
PERF_LIVE_POLL_MS_MAX,
PERF_LIVE_POLL_MS_MIN,
PERF_LIVE_POLL_MS_STEP,
setPerfLiveIncludeThreadGroups,
setPerfLivePollIntervalMs,
usePerfLiveIncludeThreadGroups,
usePerfLivePollIntervalMs,
} from '../../../utils/perf/perfLivePollSettings';
export default function PerfLivePollControls() {
const pollMs = usePerfLivePollIntervalMs();
const includeThreadGroups = usePerfLiveIncludeThreadGroups();
const pollSec = (pollMs / 1000).toFixed(1);
return (
@@ -28,6 +31,19 @@ export default function PerfLivePollControls() {
onChange={e => setPerfLivePollIntervalMs(Number(e.target.value))}
/>
</label>
<label className="perf-live-poll__row perf-live-poll__row--check">
<input
type="checkbox"
checked={includeThreadGroups}
onChange={e => setPerfLiveIncludeThreadGroups(e.target.checked)}
/>
<span className="perf-live-poll__check-label">
CPU by psysonic threads
</span>
</label>
<p className="perf-live-poll__hint">
Scans in-process thread groups each poll (Linux). Off by default enable only while diagnosing.
</p>
</section>
);
}
@@ -1,8 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useMemo, useRef } from 'react';
import { usePerfLiveSnapshot } from '../../../utils/perf/perfLiveStore';
import {
syncPerfLiveThreadGroupsNeed,
} from '../../../utils/perf/perfLivePollSettings';
import { usePerfLiveIncludeThreadGroups } from '../../../utils/perf/perfLivePollSettings';
import {
togglePerfLiveOverlayPin,
togglePipelineOverlayPin,
@@ -28,13 +26,9 @@ export default function SidebarPerfProbeMonitorTab() {
const coverPinned = usePipelineOverlayPinned('pipeline:cover');
const cpu = live.cpu;
const collecting = live.collecting && cpu == null;
const includeThreadGroups = usePerfLiveIncludeThreadGroups();
const peakMemoryKbRef = useRef(1);
const peakThreadCpuRef = useRef(1);
const [threadSectionOpen, setThreadSectionOpen] = useState(false);
useEffect(() => {
syncPerfLiveThreadGroupsNeed(threadSectionOpen, livePins);
}, [threadSectionOpen, livePins]);
const maxMemoryKb = useMemo(() => {
const current = Math.max(1, ...(cpu?.memory.map(m => m.rss_kb) ?? [1]));
@@ -123,11 +117,10 @@ export default function SidebarPerfProbeMonitorTab() {
/>
</PerfProbeMetricSection>
{(cpu.threadCpu.length > 0 || threadSectionOpen) && (
{includeThreadGroups && (
<PerfProbeMetricSection
title="CPU — psysonic threads"
defaultOpen={false}
onOpenChange={setThreadSectionOpen}
defaultOpen
>
{cpu.threadCpu.length > 0 ? cpu.threadCpu.map(row => {
const pinId = `cpu:thread:${row.label}` as PerfLiveOverlayPinId;
@@ -145,7 +138,9 @@ export default function SidebarPerfProbeMonitorTab() {
/>
);
}) : (
<div className="perf-monitor-empty perf-monitor-empty--inline">Collecting thread samples</div>
<div className="perf-monitor-empty perf-monitor-empty--inline">
No named psysonic threads yet wait for the next poll or load audio/analysis work.
</div>
)}
</PerfProbeMetricSection>
)}