#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn rss_mib() -> u64 {
let s = std::fs::read_to_string("/proc/self/status").unwrap_or_default();
for line in s.lines() {
if let Some(rest) = line.strip_prefix("VmRSS:") {
let kb: u64 = rest
.split_whitespace()
.next()
.and_then(|v| v.parse().ok())
.unwrap_or(0);
return kb / 1024;
}
}
0
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn boot_n_reading_blob(image: &supermachine::Image, n: usize, label: &str) {
use std::time::Duration;
use supermachine::VmConfig;
let base = rss_mib();
eprintln!("[{label}] baseline RSS = {base} MiB");
let mut held = Vec::new();
for i in 0..n {
let vm = image.start(&VmConfig::new()).expect("start");
std::thread::sleep(Duration::from_millis(3500));
let o = vm
.exec_builder()
.argv([
"/bin/sh",
"-c",
"dd if=/data/blob of=/dev/null bs=1M 2>/dev/null; echo read-ok",
])
.output()
.expect("exec read");
let ok = String::from_utf8_lossy(&o.stdout).contains("read-ok");
held.push(vm);
let now = rss_mib();
eprintln!(
"[{label}] after VM #{i} (read-ok={ok}): RSS = {now} MiB (+{} since baseline, {} MiB/VM avg)",
now - base,
(now - base) / (i as u64 + 1)
);
}
let total = rss_mib() - base;
eprintln!(
"[{label}] === {n} VMs: +{total} MiB total, ~{} MiB/VM ===",
total / n as u64
);
drop(held);
std::thread::sleep(Duration::from_millis(500));
}
#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
fn main() {
use supermachine::{builder, Image};
let n: usize = std::env::args()
.nth(1)
.and_then(|s| s.parse().ok())
.unwrap_or(4);
let mib = 96u64;
let dockerfile = format!(
"FROM alpine\n\
RUN mkdir -p /data && dd if=/dev/urandom of=/data/blob bs=1M count={mib} 2>/dev/null\n"
);
let df = builder::parse(&dockerfile).expect("parse");
let ctx = std::env::temp_dir().join("sm-dens-ctx");
let _ = std::fs::create_dir_all(&ctx);
let build_dest = std::env::temp_dir().join("sm-dens-build");
let _ = std::fs::remove_dir_all(&build_dest);
let commit_dir = std::env::temp_dir().join("sm-dens-commit");
let _ = std::fs::remove_dir_all(&commit_dir);
let base = Image::from_oci("alpine").expect("from_oci");
eprintln!("=== build ({mib} MiB blob) ===");
let out = builder::build_linear(&df, &base, &ctx, &build_dest).expect("build_linear");
eprintln!("=== commit -> read-only squashfs bootable image ===");
let (commit, bootable) =
builder::commit_kvm_bootable(&out.image, &commit_dir).expect("commit_kvm_bootable");
eprintln!("squashfs = {} MiB", commit.bytes / (1024 * 1024));
eprintln!("\n##### Scenario A: snapshot-layer image (restore.snap CoW) #####");
boot_n_reading_blob(&out.image, n, "snap-layer");
eprintln!("\n##### Scenario B: committed read-only squashfs image #####");
boot_n_reading_blob(&bootable, n, "committed");
eprintln!("\n(Interpretation: lower MiB/VM = better density. A small slope means");
eprintln!(" the blob pages are SHARED across VMs; ~{mib} MiB/VM means PRIVATE.)");
}
#[cfg(not(all(target_os = "linux", target_arch = "x86_64")))]
fn main() {
eprintln!("kvm_density_scale is Linux/x86_64 only");
}