Synced from monorepo
Synced from monorepo Changes: - grok-shell: send an expired external-provider credential to the sign-in flow, not a 401 loop - pager: clickable ▲ jumps to the top of the response being read - grok-shell: keep a large task log from making the completion message too long - Plan viewer scrollbar: widen grab zone to the border column; fix striped thumb in Terminal.app - pager: poll the tmux probe teardown grace instead of sleeping it - security: vendor-compat MCP kill switch is now actually enforced when reported as on - grok-shell: restore session eviction when a leader client disconnects - Bump rust-toolchain to 1.93.0 - workspace: lexical-normalize permission path patterns before glob matching - pager: reject garbage Enter in the /resume picker - pager: show Mermaid affordances in plan mode preview - pager: drop manage-account link from /session-info - workspace: auto-approve read-only git queries; defer write floor to auto classifier - Add free-form pattern editor to the "Always allow" command prompt - grok-shell: fix /btw caching - pager: Tab walks answers in the ask_user_question card - External-provider auth refresh: single 7s attempt instead of 3×5s - pager: don't resurrect finished background tasks as Running when completion arrives first - pager: report tmux truecolor clamping in Doctor - Fix plan viewer scrollbar click+drag hijacked by comment gutter - pager/shell: stop double Recap after the same last turn - sampler: preserve x-should-retry through stream collection - pager: clear plan-mode indicator immediately when the user approves a plan - pager: tmux does not re-read its config on reattach Source-Revision: 64c4de99cc822b25ce9c54ab5a4f372093d0885d
This commit is contained in:
parent
a422116582
commit
780d1388ff
323 changed files with 12258 additions and 7226 deletions
|
|
@ -1,14 +1,13 @@
|
|||
//! Generic OS resource snapshots for soak tests. No shell types: `rss_bytes`
|
||||
//! reads `/proc` (Linux) or shells out to `ps` (macOS); the task/fd counters
|
||||
//! are Linux-only and return `None` elsewhere.
|
||||
//! OS resource snapshots for soak tests, read through `xai_tty_utils` so the
|
||||
//! soaks and production measure the same way.
|
||||
|
||||
/// RSS (bytes), live threads, and open fds sampled together. `None` marks a
|
||||
/// RSS in bytes, live threads, and open files, sampled together. `None` marks a
|
||||
/// metric the platform can't report.
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct ResourceSnapshot {
|
||||
pub rss: Option<usize>,
|
||||
pub threads: Option<usize>,
|
||||
pub fds: Option<usize>,
|
||||
pub open_files: Option<usize>,
|
||||
}
|
||||
|
||||
/// Saturating per-field growth of one [`ResourceSnapshot`] over an earlier
|
||||
|
|
@ -18,23 +17,26 @@ pub struct ResourceSnapshot {
|
|||
pub struct ResourceGrowth {
|
||||
pub rss: Option<usize>,
|
||||
pub threads: Option<usize>,
|
||||
pub fds: Option<usize>,
|
||||
pub open_files: Option<usize>,
|
||||
}
|
||||
|
||||
impl ResourceSnapshot {
|
||||
pub fn capture() -> Self {
|
||||
let usage = xai_tty_utils::sample_process_resources();
|
||||
let widen = |value: Option<u64>| value.map(|n| n as usize);
|
||||
Self {
|
||||
rss: rss_bytes(),
|
||||
threads: thread_count(),
|
||||
fds: fd_count(),
|
||||
rss: widen(usage.rss_bytes),
|
||||
threads: widen(usage.threads),
|
||||
open_files: widen(usage.open_files),
|
||||
}
|
||||
}
|
||||
|
||||
/// RSS only, skipping the thread and fd probes. For hot sampling loops that
|
||||
/// use just `rss`: on Linux this avoids the per-tick `/proc/self/{task,fd}`
|
||||
/// directory scans. The RSS read itself still shells out to `ps` on macOS.
|
||||
/// RSS only, skipping the thread and descriptor scans. For sampling loops
|
||||
/// that read just `rss`.
|
||||
pub fn capture_rss() -> Option<usize> {
|
||||
rss_bytes()
|
||||
xai_tty_utils::sample_process_memory()
|
||||
.rss_bytes
|
||||
.map(|n| n as usize)
|
||||
}
|
||||
|
||||
/// Growth of `self` (after) over `baseline` (before); see [`ResourceGrowth`].
|
||||
|
|
@ -45,68 +47,11 @@ impl ResourceSnapshot {
|
|||
ResourceGrowth {
|
||||
rss: delta(self.rss, baseline.rss),
|
||||
threads: delta(self.threads, baseline.threads),
|
||||
fds: delta(self.fds, baseline.fds),
|
||||
open_files: delta(self.open_files, baseline.open_files),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rss_bytes() -> Option<usize> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let status = std::fs::read_to_string("/proc/self/status").ok()?;
|
||||
for line in status.lines() {
|
||||
if let Some(val) = line.strip_prefix("VmRSS:") {
|
||||
let kb: usize = val.trim().trim_end_matches(" kB").trim().parse().ok()?;
|
||||
return Some(kb * 1024);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
use std::process::Command;
|
||||
let output = Command::new("ps")
|
||||
.args(["-o", "rss=", "-p", &std::process::id().to_string()])
|
||||
.output()
|
||||
.ok()?;
|
||||
let kb: usize = String::from_utf8_lossy(&output.stdout)
|
||||
.trim()
|
||||
.parse()
|
||||
.ok()?;
|
||||
Some(kb * 1024)
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn thread_count() -> Option<usize> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Some(std::fs::read_dir("/proc/self/task").ok()?.count())
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The read's own transient fd closes with the iterator, so before and after
|
||||
/// samples stay symmetric.
|
||||
fn fd_count() -> Option<usize> {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
Some(std::fs::read_dir("/proc/self/fd").ok()?.count())
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -116,18 +61,18 @@ mod tests {
|
|||
let before = ResourceSnapshot {
|
||||
rss: Some(100),
|
||||
threads: Some(5),
|
||||
fds: None,
|
||||
open_files: None,
|
||||
};
|
||||
let after = ResourceSnapshot {
|
||||
rss: Some(30),
|
||||
threads: Some(9),
|
||||
fds: Some(3),
|
||||
open_files: Some(3),
|
||||
};
|
||||
let growth = after.growth_from(&before);
|
||||
assert_eq!(growth.rss, Some(0), "a shrink saturates to zero");
|
||||
assert_eq!(growth.threads, Some(4), "growth is the delta");
|
||||
assert_eq!(
|
||||
growth.fds, None,
|
||||
growth.open_files, None,
|
||||
"a missing baseline sample propagates None"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue