use std::io::{Read, Write};
use std::net::TcpListener;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::time::{Duration, Instant};
use opentelemetry::Context as OtelContext;
use opentelemetry::trace::{TraceContextExt, Tracer, TracerProvider};
const MEASURE_SECS: u64 = 120;
const WARMUP_SECS: u64 = 60;
const SPAN_THREADS: usize = 2;
const WORK_ITERS: usize = 60_000;
fn env_u64(key: &str, default: u64) -> u64 {
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}
fn rss_bytes() -> Option<u64> {
#[cfg(target_os = "linux")]
{
let status = std::fs::read_to_string("/proc/self/status").ok()?;
let line = status.lines().find(|l| l.starts_with("VmRSS:"))?;
let kib: u64 = line.split_whitespace().nth(1)?.parse().ok()?;
Some(kib * 1024)
}
#[cfg(not(target_os = "linux"))]
{
let out = std::process::Command::new("ps")
.args(["-o", "rss=", "-p", &std::process::id().to_string()])
.output()
.ok()?;
let kib: u64 = String::from_utf8_lossy(&out.stdout).trim().parse().ok()?;
Some(kib * 1024)
}
}
fn spawn_sink(
stop: Arc<AtomicBool>,
pushes: Arc<AtomicU64>,
push_bytes: Arc<AtomicU64>,
tagged_pushes: Arc<AtomicU64>,
) -> std::io::Result<u16> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let port = listener.local_addr()?.port();
listener.set_nonblocking(true)?;
std::thread::spawn(move || {
while !stop.load(Ordering::Relaxed) {
match listener.accept() {
Ok((mut sock, _)) => {
sock.set_nonblocking(false).ok();
sock.set_read_timeout(Some(Duration::from_millis(250))).ok();
let mut buf = [0u8; 32 * 1024];
let mut total = 0u64;
let mut body: Vec<u8> = Vec::new();
while let Ok(n) = sock.read(&mut buf) {
if n == 0 {
break;
}
total += n as u64;
body.extend_from_slice(&buf[..n]);
if n < buf.len() {
break;
}
}
pushes.fetch_add(1, Ordering::Relaxed);
push_bytes.fetch_add(total, Ordering::Relaxed);
if body_has_per_span_labels(&body[..]) {
tagged_pushes.fetch_add(1, Ordering::Relaxed);
}
let _ = sock.write_all(
b"HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: close\r\n\r\n",
);
}
Err(_) => std::thread::sleep(Duration::from_millis(20)),
}
}
});
Ok(port)
}
fn body_has_per_span_labels(body: &[u8]) -> bool {
use std::io::Read as _;
let Some(start) = body.windows(4).position(|w| w == b"\r\n\r\n") else {
return false;
};
let mut out = Vec::new();
if flate2::read::GzDecoder::new(&body[start + 4..])
.read_to_end(&mut out)
.is_err()
&& out.is_empty()
{
return false;
}
out.windows(7).any(|w| w == b"span_id") || out.windows(8).any(|w| w == b"trace_id")
}
#[inline(never)]
fn work(n: u64) -> u64 {
let mut acc = n;
for _ in 0..WORK_ITERS {
acc = std::hint::black_box(acc.wrapping_mul(6364136223846793005).wrapping_add(1));
}
acc
}
fn slope(samples: &[(f64, f64)]) -> f64 {
let n = samples.len() as f64;
if n < 2.0 {
return 0.0;
}
let mean_t = samples.iter().map(|s| s.0).sum::<f64>() / n;
let mean_r = samples.iter().map(|s| s.1).sum::<f64>() / n;
let num: f64 = samples
.iter()
.map(|(t, r)| (t - mean_t) * (r - mean_r))
.sum();
let den: f64 = samples.iter().map(|(t, _)| (t - mean_t).powi(2)).sum();
if den > 0.0 { num / den } else { 0.0 }
}
fn main() {
let measure_secs = env_u64("RSS_PROBE_MEASURE_SECS", MEASURE_SECS);
let warmup_secs = env_u64("RSS_PROBE_WARMUP_SECS", WARMUP_SECS);
if rss_bytes().is_none() {
println!("RSS_PROBE_NO_RSS cannot read RSS on this platform");
std::process::exit(3);
}
let stop = Arc::new(AtomicBool::new(false));
let pushes = Arc::new(AtomicU64::new(0));
let push_bytes = Arc::new(AtomicU64::new(0));
let tagged_pushes = Arc::new(AtomicU64::new(0));
let spans = Arc::new(AtomicU64::new(0));
let port = match spawn_sink(
stop.clone(),
pushes.clone(),
push_bytes.clone(),
tagged_pushes.clone(),
) {
Ok(p) => p,
Err(e) => {
println!("RSS_PROBE_SINK_FAILED {e}");
std::process::exit(4);
}
};
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(2)
.enable_all()
.build()
.expect("build runtime");
let _rt_guard = rt.enter();
let control = std::env::var("RSS_PROBE_NO_PROFILING").is_ok_and(|v| v == "1");
let builder = otel_bootstrap::Telemetry::builder("rss-probe");
let builder = if control {
builder
} else {
builder.with_profiling(&format!("http://127.0.0.1:{port}"))
};
let handles = match builder.init() {
Ok(h) => h,
Err(e) => {
println!("RSS_PROBE_INIT_FAILED {e}");
std::process::exit(5);
}
};
if !control && handles.profiling_handle.is_none() {
println!("RSS_PROBE_BRIDGE_INACTIVE profiling handle absent");
std::process::exit(6);
}
for t in 0..SPAN_THREADS {
let stop = stop.clone();
let spans = spans.clone();
std::thread::Builder::new()
.name(format!("span-{t}"))
.spawn(move || {
let tracer = opentelemetry::global::tracer_provider().tracer("rss-probe");
let mut n = t as u64;
while !stop.load(Ordering::Relaxed) {
let otel_span = tracer.start("rss_probe.unit");
let cx = OtelContext::current_with_span(otel_span);
let attached = cx.attach();
let span = tracing::info_span!("rss_probe.unit", thread = t);
let entered = span.enter();
n = work(n);
drop(entered);
drop(attached);
spans.fetch_add(1, Ordering::Relaxed);
std::hint::black_box(n);
}
})
.expect("spawn span thread");
}
let start = Instant::now();
let mut samples: Vec<(f64, f64)> = Vec::new();
loop {
std::thread::sleep(Duration::from_secs(1));
let elapsed = start.elapsed().as_secs_f64();
if elapsed >= (warmup_secs + measure_secs) as f64 {
break;
}
let Some(rss) = rss_bytes() else { continue };
if elapsed >= warmup_secs as f64 {
samples.push((elapsed, rss as f64));
}
println!("RSS_PROBE_SAMPLE t={elapsed:.1} rss={rss}");
}
stop.store(true, Ordering::Relaxed);
let bytes_per_sec = slope(&samples);
let mib_per_hour = bytes_per_sec * 3600.0 / (1024.0 * 1024.0);
let first = samples.first().map(|s| s.1 as u64).unwrap_or(0);
let last = samples.last().map(|s| s.1 as u64).unwrap_or(0);
let spans_done = spans.load(Ordering::Relaxed);
if spans_done < 1000 {
println!("RSS_PROBE_NO_LOAD spans={spans_done}");
let _ = handles.shutdown();
std::process::exit(7);
}
let pushes_done = pushes.load(Ordering::Relaxed);
let bytes_done = push_bytes.load(Ordering::Relaxed);
let bytes_per_push = bytes_done.checked_div(pushes_done).unwrap_or(0);
let elapsed_secs = start.elapsed().as_secs_f64().max(1.0);
println!(
"RSS_PROBE_OK profiling={} mib_per_hour={mib_per_hour:.2} samples={} \
spans={spans_done} spans_per_sec={:.0} pushes={pushes_done} \
push_bytes={bytes_done} bytes_per_push={bytes_per_push} \
tagged_pushes={} first_rss={first} last_rss={last}",
u8::from(!control),
samples.len(),
spans_done as f64 / elapsed_secs,
tagged_pushes.load(Ordering::Relaxed),
);
let _ = handles.shutdown();
}