From 168c42cc913cb58d771da68d5b2f962136bdc2fb Mon Sep 17 00:00:00 2001 From: flupkede Date: Sun, 18 Oct 2026 21:46:46 +0200 Subject: [PATCH] fix: status CPU reports the whole serve process tree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The snapshot measured the serve process alone (ProcessesToUpdate::Some(&[pid])) — during a rebuild wave the scip-csharp helper and its dotnet children do the heavy lifting, so the reported CPU read near-zero exactly when the machine was busy. The snapshot now refreshes all processes once (so parent links are populated), walks the serve process's descendant tree and sums the per-process usage, normalized against the core count as before. --- AGENTS.develop.md | 1 + CHANGELOG.md | 2 ++ src/serve/mod.rs | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/AGENTS.develop.md b/AGENTS.develop.md index 4319e4d4..0780f325 100644 --- a/AGENTS.develop.md +++ b/AGENTS.develop.md @@ -241,6 +241,7 @@ Never `unwrap_or_default()` a store error on a search path — "no results" and ## Changelog highlights (recent) +- **v1.5.33 (pending)** — status CPU = whole serve process tree (incl. scip/dotnet children) instead of the serve process alone - **v1.5.33 (pending)** — Razor refs mapped to source: helper emits `GetMappedLineSpan()` paths/lines (#line → .razor/.cshtml), SCIP stamp split (`SCIP_INDEX_BUILDER_VERSION`) so symbol rebuilds skip the text re-embed wave - **v1.5.33 (pending)** — FTS commit-failure hardening: per-file transient redrive, crash-safe vector rollback, mixed-generation sweep in refresh, stale note on get_chunk - **v1.5.33 (pending)** — `.codesearchinclude` whitelist: when present (repo root or `~/.codesearch/`), only matching files are indexed; ignore rules still win, absent list = historical behavior diff --git a/CHANGELOG.md b/CHANGELOG.md index b589fa44..d9bdd89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ finalized in place with a date — no renaming/migration step needed. ### Fixed +- **The status CPU now reports the whole process tree, not just the server.** The status snapshot measured the serve process alone — during a rebuild wave the scip-csharp helper and its dotnet children do the heavy lifting, so the reported CPU read near-zero exactly when the machine was busy. The snapshot now refreshes all processes, walks the serve process's descendant tree and sums their usage. + - **Razor-generated code references now resolve to the .razor/.cshtml source lines, like VS's ctrl-click.** The C# helper recorded symbol positions with the raw `GetLineSpan()`, so references living inside Razor source-generated trees pointed at generated virtual paths — and since the Razor SDK moved to in-memory source generators (no more `obj/razor/*.g.cs` on disk), those references vanished from rebuilt indexes entirely (find_impact on a ViewModel lost its view-code references). The helper now uses `GetMappedLineSpan()` for both the document path and the ranges: `#line` directives resolve every generated-tree position back to the .razor/.cshtml source file and line, hidden regions keep the last mapping (Roslyn semantics), and trees with no directives at all are dropped. The SCIP symbol indexes get their own builder stamp (`SCIP_INDEX_BUILDER_VERSION`, now `2026-10.2`) so a deployed binary rebuilds them without a text re-embed wave — the text index keeps comparing the unchanged `INDEX_BUILDER_VERSION`. - **A failed FTS commit no longer leaves the index in mixed generations.** On Windows an indexer/AV filter holding a freshly created tantivy segment file makes the FTS commit fail (os error 5) and the writer is discarded by design — but the per-file incremental paths inserted the new chunks into the vector store first with no rollback, so the failed attempt's ids were orphaned (never in file_meta) while the tracked generation was already deleted: search (FTS) and get_chunk (vector) then disagreed and stale lines kept coming back (the HUSQ.Aprimo worktree symptom). Three layers now: the per-file paths redrive on the transient race like force reindex already did; a failed publish rolls the fresh chunk ids back out of both stores (file_meta stays old, so the next pass heals clean); and the refresh sweep deletes untracked vector chunks and re-indexes live files whose tracked ids went missing, so already-corrupted indexes heal on the next refresh without a force reindex. get_chunk also warns when the source file is newer than the last indexing write. The fts/ subdir is now marked hidden on Windows too. diff --git a/src/serve/mod.rs b/src/serve/mod.rs index 8087be02..fab9d10a 100644 --- a/src/serve/mod.rs +++ b/src/serve/mod.rs @@ -3940,15 +3940,44 @@ impl ServeState { let repos = self.repo_statuses_lightweight(); let active_sessions = self.active_session_count(); - // CPU usage — reuse shared System instance so cpu_usage() can compute delta + // CPU usage — serve process PLUS all its descendants. The SCIP + // helpers and their dotnet children do the heavy lifting during a + // rebuild wave; measuring the serve process alone reads near-zero + // exactly when the machine is busy. Refresh ALL processes once so + // parent links are populated, then walk the tree from our pid and + // sum the per-process usage (cpu_usage() computes its delta against + // the previous pass on the shared System instance). let cpu = (|| -> Option { use sysinfo::ProcessesToUpdate; let pid = sysinfo::get_current_pid().ok()?; let mut sys = self.sysinfo_system.lock().ok()?; - sys.refresh_processes(ProcessesToUpdate::Some(&[pid]), true); - let proc = sys.process(pid)?; + sys.refresh_processes(ProcessesToUpdate::All, true); + + let mut tree: Vec = vec![pid]; + let mut seen = std::collections::HashSet::new(); + seen.insert(pid); + let mut grew = true; + while grew { + grew = false; + for (p, proc_) in sys.processes() { + if seen.contains(p) { + continue; + } + if proc_.parent().is_some_and(|parent| seen.contains(&parent)) { + seen.insert(*p); + tree.push(*p); + grew = true; + } + } + } + + let total: f32 = tree + .iter() + .filter_map(|p| sys.process(*p)) + .map(|p| p.cpu_usage()) + .sum(); let num_cpus = sys.cpus().len().max(1) as f32; - Some(format!("{:.0}%", proc.cpu_usage() / num_cpus)) + Some(format!("{:.0}%", total / num_cpus)) })() .unwrap_or_else(|| "—".to_string());