use std::process::ExitCode;
use std::time::Duration;

use perf_event::events::x86::{Msr, MsrId};
use perf_event::Builder;

fn run() -> std::io::Result<()> {
    let tsc_event = Msr::new(MsrId::TSC)?;
    let aperf_event = Msr::new(MsrId::APERF)?;
    let mperf_event = Msr::new(MsrId::MPERF)?;

    let mut tsc = Builder::new(tsc_event)
        .one_cpu(0)
        .any_pid()
        .exclude_hv(false)
        .exclude_kernel(false)
        .build()?;
    let mut aperf = Builder::new(aperf_event)
        .one_cpu(0)
        .any_pid()
        .exclude_hv(false)
        .exclude_kernel(false)
        .build_with_group(&mut tsc)?;
    let mut mperf = Builder::new(mperf_event)
        .one_cpu(0)
        .any_pid()
        .exclude_hv(false)
        .exclude_kernel(false)
        .build_with_group(&mut tsc)?;

    tsc.enable_group()?;
    std::thread::sleep(Duration::from_secs(1));
    tsc.disable_group()?;

    let tsc_val: u64 = tsc.read()?;
    let ghz = tsc_val as f64 / (1000000000.0);
    let aperf_val = aperf.read()?;
    let mperf_val = mperf.read()?;
    let ratio = aperf_val as f64 / mperf_val as f64;
    let run_freq = ghz * ratio;

    println!(
        "{tsc_val} ref cycles passed in one second ({ghz:.3} GHz)\n\
        APERF: {aperf_val}\n\
        MPERF: {mperf_val}\n\
        Ratio: {ratio:.4}\n\
        Running Frequency: {run_freq:.3} GHz"
    );

    Ok(())
}

fn main() -> ExitCode {
    if let Err(e) = run() {
        eprintln!("{e}");
        ExitCode::FAILURE
    } else {
        ExitCode::SUCCESS
    }
}
