use std::{
env,
os::unix::process::ExitStatusExt,
process::{Command, ExitCode},
time::Instant,
};
#[cfg(all(
not(coverage),
not(feature = "prof"),
not(target_os = "android"),
not(target_arch = "riscv64"),
target_page_size_4k,
target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
#[cfg(target_arch = "x86_64")]
use tick_counter::x86_64_processor_id;
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
use tick_counter::{frequency, precision_nanoseconds, TickCounter};
syd::main! {
syd::set_sigpipe_dfl()?;
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
help();
return Ok(ExitCode::FAILURE);
} else if args[1] == "-h" || args[1] == "--help" {
help();
return Ok(ExitCode::SUCCESS);
}
let arg0 = &args[1];
let args = &args[2..];
let start = Instant::now();
let tick_start = current_tick();
let status = Command::new(arg0).args(args).status()?;
let tick_duration = elapsed_tick(&tick_start);
let duration = start.elapsed();
let code = status
.code()
.unwrap_or_else(|| 128 + status.signal().unwrap_or(127));
let (freq, precision) = tick_info();
let duration = duration.as_secs_f64();
let extra_info = get_arch_specific_info();
eprintln!("{arg0}\tcode:{code} total:{duration:.2}s td:{tick_duration} freq:{freq}Hz prec:{precision:.2}ns{extra_info}");
Ok(ExitCode::from(code as u8))
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
fn current_tick() -> TickCounter {
TickCounter::current()
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
fn current_tick() -> Instant {
Instant::now()
}
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
fn elapsed_tick(start_tick: &TickCounter) -> u64 {
start_tick.elapsed()
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
fn elapsed_tick(start_tick: &Instant) -> u64 {
start_tick.elapsed().as_secs_f64() as u64
}
#[expect(clippy::needless_return)]
fn tick_info() -> (u64, f64) {
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
{
let (freq, _) = frequency();
let precision = precision_nanoseconds(freq);
return (freq, precision);
}
#[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
{
return (1, 1_000_000_000.0);
}
}
#[cfg(target_arch = "x86_64")]
fn get_arch_specific_info() -> String {
let (tc, pid) = x86_64_processor_id();
format!(" pid:{pid} tc:{tc}")
}
#[cfg(not(target_arch = "x86_64"))]
fn get_arch_specific_info() -> String {
String::new() }
fn help() {
println!("Usage: syd-tck {{command [arg...]}}");
println!("Given a command with optional arguments, measures runtime in hardware ticks.");
}