From 9d35321323f0f311eed937720bd38084449db543 Mon Sep 17 00:00:00 2001 From: Zack Cerza Date: Wed, 1 Oct 2025 18:26:44 -0600 Subject: [PATCH] Write stats to cache_stats_file after compile Signed-off-by: Zack Cerza --- docs/Configuration.md | 3 ++ src/config.rs | 9 +++++ src/server.rs | 77 +++++++++++++++++++++++++++++++++++++------ src/test/tests.rs | 2 +- tests/harness/mod.rs | 1 + tests/oauth.rs | 1 + 6 files changed, 82 insertions(+), 11 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 8fb584027..3908ab276 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -6,6 +6,9 @@ # If specified, wait this long for the server to start up. server_startup_timeout_ms = 10000 +# If specified, write JSON-formatted stats to this file after each compile operation. +cache_stats_file = "/home/user/.cache/sccache-stats.json" + [dist] # where to find the scheduler scheduler_url = "http://1.2.3.4:10600" diff --git a/src/config.rs b/src/config.rs index 9b1ad6677..6ac96c37d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -585,6 +585,7 @@ pub struct FileConfig { pub cache: CacheConfigs, pub dist: DistConfig, pub server_startup_timeout_ms: Option, + pub cache_stats_file: Option, } // If the file doesn't exist or we can't read it, log the issue and proceed. If the @@ -977,6 +978,7 @@ pub struct Config { pub fallback_cache: DiskCacheConfig, pub dist: DistConfig, pub server_startup_timeout: Option, + pub cache_stats_file: Option, } impl Config { @@ -998,6 +1000,7 @@ impl Config { cache, dist, server_startup_timeout_ms, + cache_stats_file, } = file_conf; conf_caches.merge(cache); @@ -1013,6 +1016,7 @@ impl Config { fallback_cache, dist, server_startup_timeout, + cache_stats_file, } } } @@ -1312,6 +1316,7 @@ fn config_overrides() { }, dist: Default::default(), server_startup_timeout_ms: None, + cache_stats_file: None, }; assert_eq!( @@ -1334,6 +1339,7 @@ fn config_overrides() { }, dist: Default::default(), server_startup_timeout: None, + cache_stats_file: None, } ); } @@ -1463,6 +1469,7 @@ fn test_gcs_service_account() { fn full_toml_parse() { const CONFIG_STR: &str = r#" server_startup_timeout_ms = 10000 +cache_stats_file = "/home/user/.cache/sccache-stats.json" [dist] # where to find the scheduler @@ -1623,6 +1630,7 @@ no_credentials = true rewrite_includes_only: false, }, server_startup_timeout_ms: Some(10000), + cache_stats_file: Some(PathBuf::from("/home/user/.cache/sccache-stats.json")) } ) } @@ -1715,6 +1723,7 @@ size = "7g" ..Default::default() }, server_startup_timeout_ms: None, + cache_stats_file: None, } ); } diff --git a/src/server.rs b/src/server.rs index fb9cd69ef..64406a2bf 100644 --- a/src/server.rs +++ b/src/server.rs @@ -31,6 +31,7 @@ use anyhow::Context as _; use bytes::{buf::BufMut, Bytes, BytesMut}; use filetime::FileTime; use fs::metadata; +use fs::File; use fs_err as fs; use futures::channel::mpsc; use futures::future::FutureExt; @@ -42,7 +43,7 @@ use std::collections::{HashMap, HashSet}; use std::env; use std::ffi::OsString; use std::future::Future; -use std::io::{self, Write}; +use std::io::{self, BufWriter, Write}; use std::marker::Unpin; #[cfg(feature = "dist-client")] use std::mem; @@ -50,7 +51,7 @@ use std::mem; use std::os::android::net::SocketAddrExt; #[cfg(target_os = "linux")] use std::os::linux::net::SocketAddrExt; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::pin::Pin; use std::process::{ExitStatus, Output}; use std::sync::Arc; @@ -488,8 +489,14 @@ pub fn start_server(config: &Config, addr: &crate::net::SocketAddr) -> Result<() crate::net::SocketAddr::Net(addr) => { trace!("binding TCP {addr}"); let l = runtime.block_on(tokio::net::TcpListener::bind(addr))?; - let srv = - SccacheServer::<_>::with_listener(l, runtime, client, dist_client, storage); + let srv = SccacheServer::<_>::with_listener( + l, + runtime, + client, + dist_client, + storage, + config.cache_stats_file.clone(), + ); Ok(( srv.local_addr().unwrap(), Box::new(move |f| srv.run(f)) as Box _>, @@ -504,8 +511,14 @@ pub fn start_server(config: &Config, addr: &crate::net::SocketAddr) -> Result<() let _guard = runtime.enter(); tokio::net::UnixListener::bind(path)? }; - let srv = - SccacheServer::<_>::with_listener(l, runtime, client, dist_client, storage); + let srv = SccacheServer::<_>::with_listener( + l, + runtime, + client, + dist_client, + storage, + config.cache_stats_file.clone(), + ); Ok(( srv.local_addr().unwrap(), Box::new(move |f| srv.run(f)) as Box _>, @@ -521,8 +534,14 @@ pub fn start_server(config: &Config, addr: &crate::net::SocketAddr) -> Result<() let _guard = runtime.enter(); tokio::net::UnixListener::from_std(l)? }; - let srv = - SccacheServer::<_>::with_listener(l, runtime, client, dist_client, storage); + let srv = SccacheServer::<_>::with_listener( + l, + runtime, + client, + dist_client, + storage, + config.cache_stats_file.clone(), + ); Ok(( srv.local_addr() .unwrap_or_else(|| crate::net::SocketAddr::UnixAbstract(p.to_vec())), @@ -579,6 +598,7 @@ impl SccacheServer { client: Client, dist_client: DistClientContainer, storage: Arc, + cache_stats_file: Option, ) -> Result { let addr = crate::net::SocketAddr::with_port(port); let listener = runtime.block_on(tokio::net::TcpListener::bind(addr.as_net().unwrap()))?; @@ -589,6 +609,7 @@ impl SccacheServer { client, dist_client, storage, + cache_stats_file, )) } } @@ -600,13 +621,22 @@ impl SccacheServer { client: Client, dist_client: DistClientContainer, storage: Arc, + cache_stats_file: Option, ) -> Self { // Prepare the service which we'll use to service all incoming TCP // connections. let (tx, rx) = mpsc::channel(1); let (wait, info) = WaitUntilZero::new(); let pool = runtime.handle().clone(); - let service = SccacheService::new(dist_client, storage, &client, pool, tx, info); + let service = SccacheService::new( + dist_client, + storage, + &client, + pool, + tx, + info, + cache_stats_file.clone(), + ); SccacheServer { runtime, @@ -818,6 +848,10 @@ where /// This field causes [WaitUntilZero] to wait until this struct drops. #[allow(dead_code)] info: ActiveInfo, + + /// A file that will contain JSON-formatted stats output, written after + /// each compile operation. + cache_stats_file: Option, } type SccacheRequest = Message>; @@ -857,7 +891,11 @@ where Request::Compile(compile) => { debug!("handle_client: compile"); me.stats.lock().await.compile_requests += 1; - me.handle_compile(compile).await + let resp = me.handle_compile(compile).await; + if let Some(val) = &me.cache_stats_file { + me.stats.lock().await.clone().write(val)? + } + resp } Request::GetStats => { debug!("handle_client: get_stats"); @@ -916,6 +954,7 @@ where rt: tokio::runtime::Handle, tx: mpsc::Sender, info: ActiveInfo, + cache_stats_file: Option, ) -> SccacheService { SccacheService { stats: Arc::default(), @@ -927,6 +966,7 @@ where creator: C::new(client), tx, info, + cache_stats_file, } } @@ -938,6 +978,7 @@ where let (_, info) = WaitUntilZero::new(); let client = Client::new_num(1); let dist_client = DistClientContainer::new_disabled(); + let cache_stats_file = None; SccacheService { stats: Arc::default(), dist_client: Arc::new(dist_client), @@ -948,6 +989,7 @@ where creator: C::new(&client), tx, info, + cache_stats_file, } } @@ -960,6 +1002,7 @@ where let (tx, _) = mpsc::channel(1); let (_, info) = WaitUntilZero::new(); let client = Client::new_num(1); + let cache_stats_file = None; SccacheService { stats: Arc::default(), dist_client: Arc::new(DistClientContainer::new_with_state(DistClientState::Some( @@ -981,6 +1024,7 @@ where creator: C::new(&client), tx, info, + cache_stats_file, } } @@ -1909,6 +1953,19 @@ impl ServerStats { ); } } + + /// Write stats in JSON format to a file. + fn write(&self, path: &Path) -> Result<()> { + let file = match File::create(path) { + Ok(f) => f, + Err(e) => { + debug!("Couldn't open stats file for writing: {}", e); + return Ok(()); + } + }; + let mut writer = BufWriter::new(file); + Ok(serde_json::to_writer(&mut writer, self)?) + } } fn set_percentage_stat( diff --git a/src/test/tests.rs b/src/test/tests.rs index fad140229..5e18e4936 100644 --- a/src/test/tests.rs +++ b/src/test/tests.rs @@ -89,7 +89,7 @@ where )); let client = Client::new(); - let srv = SccacheServer::new(0, runtime, client, dist_client, storage).unwrap(); + let srv = SccacheServer::new(0, runtime, client, dist_client, storage, None).unwrap(); let mut srv: SccacheServer<_, Arc>> = srv; let addr = srv.local_addr().unwrap(); assert!(matches!(addr, crate::net::SocketAddr::Net(a) if a.port() > 0)); diff --git a/tests/harness/mod.rs b/tests/harness/mod.rs index f413e395f..27357c65e 100644 --- a/tests/harness/mod.rs +++ b/tests/harness/mod.rs @@ -189,6 +189,7 @@ pub fn sccache_client_cfg( rewrite_includes_only: false, // TODO }, server_startup_timeout_ms: None, + cache_stats_file: None, } } diff --git a/tests/oauth.rs b/tests/oauth.rs index af8709c35..0016de396 100644 --- a/tests/oauth.rs +++ b/tests/oauth.rs @@ -60,6 +60,7 @@ fn config_with_dist_auth( rewrite_includes_only: true, }, server_startup_timeout_ms: None, + cache_stats_file: None, } }