#![cfg(feature = "profiling-memory-probe")]
use std::process::{Command, Output};
fn run_probe(malloc_conf: &str) -> Output {
Command::new(env!("CARGO_BIN_EXE_heap-probe"))
.env("_RJEM_MALLOC_CONF", malloc_conf)
.output()
.expect("spawn heap-probe")
}
fn describe(out: &Output) -> String {
format!(
"status={:?} signal={:?}\n--- stdout ---\n{}\n--- stderr ---\n{}",
out.status.code(),
exit_signal(out),
String::from_utf8_lossy(&out.stdout),
String::from_utf8_lossy(&out.stderr),
)
}
#[cfg(unix)]
fn exit_signal(out: &Output) -> Option<i32> {
use std::os::unix::process::ExitStatusExt;
out.status.signal()
}
#[cfg(not(unix))]
fn exit_signal(_out: &Output) -> Option<i32> {
None
}
#[test]
fn activates_and_produces_a_profile_when_armed_inactive() {
let out = run_probe("prof:true,prof_active:false,lg_prof_sample:19");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("HEAP_PROBE_OK"),
"heap profiling did not produce a profile.\n{}",
describe(&out)
);
assert!(
out.status.success(),
"probe exited non-zero.\n{}",
describe(&out)
);
let bytes: usize = stdout
.split("pprof_bytes=")
.nth(1)
.and_then(|s| s.split_whitespace().next())
.and_then(|s| s.parse().ok())
.unwrap_or(0);
assert!(bytes > 0, "empty pprof payload.\n{}", describe(&out));
}
#[test]
fn arming_before_main_does_not_kill_the_process() {
let out = run_probe("prof:true,prof_active:true,lg_prof_sample:19");
assert!(
exit_signal(&out).is_none(),
"process died by signal with sampling armed before main.\n{}",
describe(&out)
);
assert!(
!String::from_utf8_lossy(&out.stdout).contains("HEAP_PROBE_PANIC"),
"activation panicked.\n{}",
describe(&out)
);
}
#[test]
fn reports_inactive_when_prof_not_armed() {
let out = run_probe("prof:false");
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
!stdout.contains("HEAP_PROBE_PANIC"),
"activation panicked instead of erroring.\n{}",
describe(&out)
);
assert!(
!stdout.contains("HEAP_PROBE_OK"),
"claimed a profile without profiling armed.\n{}",
describe(&out)
);
}