Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

examples/zombie_probe.rs

//! TEMPORARY measurement probe — count zombie children left by gix worktree
//! operations. Delete after measuring.
//!
//! `cargo run --example zombie_probe -- <mode> <arg> <n>`
//! modes: `selftest`, `status <repo>`, `clone <src> <dest>`

fn zombies() -> Vec<(i32, String)> {
    let me = std::process::id() as i32;
    let mut out = Vec::new();
    let Ok(rd) = std::fs::read_dir("/proc") else {
        return out;
    };
    for e in rd.flatten() {
        let name = e.file_name();
        let Some(name) = name.to_str() else { continue };
        let Ok(pid) = name.parse::<i32>() else { continue };
        let Ok(stat) = std::fs::read_to_string(format!("/proc/{pid}/stat")) else {
            continue;
        };
        let Some(rp) = stat.rfind(')') else { continue };
        let comm = stat[stat.find('(').map(|i| i + 1).unwrap_or(0)..rp].to_string();
        let rest: Vec<&str> = stat[rp + 1..].split_whitespace().collect();
        if rest.len() < 2 {
            continue;
        }
        if rest[1].parse::<i32>().unwrap_or(-1) == me && rest[0] == "Z" {
            out.push((pid, comm));
        }
    }
    out
}

fn main() -> anyhow::Result<()> {
    let mut args = std::env::args().skip(1);
    let mode = args.next().unwrap_or_else(|| "selftest".into());
    println!("probe pid {} mode {mode}", std::process::id());
    match mode.as_str() {
        // Prove the COUNTER is not blind: leak one child on purpose.
        "selftest" => {
            let child = std::process::Command::new("/bin/sh")
                .arg("-c")
                .arg("exit 0")
                .spawn()?;
            let pid = child.id();
            std::mem::forget(child); // never waited => zombie
            std::thread::sleep(std::time::Duration::from_millis(300));
            let z = zombies();
            println!("selftest: leaked pid {pid}; counter sees {} {:?}", z.len(), z);
        }
        "status" => {
            let repo = args.next().expect("status <repo> <n>");
            let n: usize = args.next().unwrap_or_else(|| "5".into()).parse()?;
            for i in 0..n {
                match nornir::gitio::worktree_freshness(std::path::Path::new(&repo)) {
                    Ok(f) => println!("  [{i}] dirty={}", f.dirty),
                    Err(e) => println!("  [{i}] ERR {e:#}"),
                }
                println!("  [{i}] zombies: {}", zombies().len());
            }
        }
        "clone" => {
            let src = args.next().expect("clone <src> <dest>");
            let dest = args.next().expect("clone <src> <dest>");
            let n: usize = args.next().unwrap_or_else(|| "1".into()).parse()?;
            for i in 0..n {
                let d = if std::env::var("PROBE_REUSE").is_ok() { dest.clone() } else { format!("{dest}-{i}") };
                if std::env::var("PROBE_REUSE").is_err() { let _ = std::fs::remove_dir_all(&d); }
                match nornir::gitio::clone_or_fetch(&src, std::path::Path::new(&d), None) {
                    Ok(sha) => println!("  [{i}] cloned {}", &sha[..8.min(sha.len())]),
                    Err(e) => println!("  [{i}] ERR {e:#}"),
                }
                let z = zombies();
                println!("  [{i}] zombies: {} {:?}", z.len(), z);
            }
        }
        other => println!("unknown mode {other}"),
    }
    let z = zombies();
    println!("FINAL zombies: {} {:?}", z.len(), z);
    Ok(())
}