2024-06-24 15:53:45 +02:00
|
|
|
#![deny(unsafe_op_in_unsafe_fn)]
|
|
|
|
|
|
|
|
use std::arch::x86_64::_mm_clflush;
|
|
|
|
use crate::arch::CpuClass::{IntelCore, IntelXeon, IntelXeonSP};
|
2024-06-24 16:58:32 +02:00
|
|
|
use crate::arch::{get_performance_counters_core, get_performance_counters_xeon};
|
2024-06-24 15:53:45 +02:00
|
|
|
use crate::Error::UnsupportedCPU;
|
|
|
|
use crate::msr::{read_msr_on_cpu, write_msr_on_cpu};
|
|
|
|
|
2024-06-24 09:40:43 +02:00
|
|
|
pub mod msr;
|
2024-06-24 15:53:45 +02:00
|
|
|
pub mod utils;
|
|
|
|
mod arch;
|
|
|
|
|
2024-06-24 16:58:32 +02:00
|
|
|
#[derive(Debug)]
|
2024-06-24 15:53:45 +02:00
|
|
|
pub enum Error {
|
|
|
|
UnsupportedCPU,
|
|
|
|
InvalidParameter,
|
|
|
|
IO(std::io::Error),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<std::io::Error> for Error {
|
|
|
|
fn from(value: std::io::Error) -> Self {
|
|
|
|
Error::IO(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const NUM_POKE: usize = 10000;
|
|
|
|
|
|
|
|
unsafe fn poke(addr: *const u8) {
|
|
|
|
for _i in 0..NUM_POKE {
|
|
|
|
unsafe { _mm_clflush(addr) };
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:58:32 +02:00
|
|
|
unsafe fn monitor_xeon(addr: *const u8, cpu: u8, max_cbox: usize) -> Result<Vec<u64>, Error> {
|
2024-06-24 15:53:45 +02:00
|
|
|
let performance_counters = if let Some(p) = get_performance_counters_xeon() {
|
|
|
|
p
|
|
|
|
} else {
|
|
|
|
return Err(UnsupportedCPU);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (performance_counters.max_slice as usize) < max_cbox {
|
|
|
|
return Err(Error::InvalidParameter);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Freeze counters
|
|
|
|
for i in 0..max_cbox {
|
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_ctr0[i], cpu, performance_counters.val_box_freeze)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset counters
|
|
|
|
for i in 0..max_cbox {
|
2024-06-24 17:37:14 +02:00
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_box_ctl[i], cpu, performance_counters.val_box_reset)?;
|
2024-06-24 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Enable counting
|
|
|
|
for i in 0..max_cbox {
|
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_ctl0[i], cpu, performance_counters.val_enable_counting)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select event
|
|
|
|
for i in 0..max_cbox {
|
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_ctl0[i], cpu, performance_counters.val_select_event)?;
|
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_box_filter[i], cpu, performance_counters.val_filter)?;
|
|
|
|
}
|
2024-06-24 09:40:43 +02:00
|
|
|
|
2024-06-24 15:53:45 +02:00
|
|
|
// Unfreeze
|
|
|
|
for i in 0..max_cbox {
|
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_box_ctl[i], cpu, performance_counters.val_box_unfreeze)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsafe { poke(addr) };
|
|
|
|
|
|
|
|
// Freeze counters
|
|
|
|
for i in 0..max_cbox {
|
2024-06-24 17:37:14 +02:00
|
|
|
write_msr_on_cpu(performance_counters.msr_pmon_box_ctl[i], cpu, performance_counters.val_box_freeze)?;
|
2024-06-24 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Read counters
|
2024-06-24 16:58:32 +02:00
|
|
|
let mut results = Vec::new();
|
2024-06-24 15:53:45 +02:00
|
|
|
for i in 0..max_cbox {
|
|
|
|
let result = read_msr_on_cpu(performance_counters.msr_pmon_ctr0[i], cpu)?;
|
2024-06-24 17:37:14 +02:00
|
|
|
if (result - NUM_POKE as u64) < 0 {
|
|
|
|
results.push(0);
|
|
|
|
} else {
|
|
|
|
results.push(result - NUM_POKE as u64);
|
|
|
|
}
|
2024-06-24 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
2024-06-24 16:58:32 +02:00
|
|
|
Ok(results)
|
2024-06-24 15:53:45 +02:00
|
|
|
}
|
|
|
|
|
2024-06-24 16:58:32 +02:00
|
|
|
fn monitor_core(addr: *const u8, cpu: u8, max_cbox: usize) -> Result<Vec<u64>, Error> {
|
2024-06-24 15:53:45 +02:00
|
|
|
// Note, we need to add the workaround for one missing perf counter here.
|
2024-06-24 16:58:32 +02:00
|
|
|
let performance_counters = if let Some(p) = get_performance_counters_core() {
|
|
|
|
p
|
|
|
|
} else {
|
|
|
|
return Err(UnsupportedCPU);
|
|
|
|
};
|
|
|
|
|
|
|
|
let workaround = if (performance_counters.max_slice as usize) + 1 == max_cbox {
|
|
|
|
true
|
|
|
|
} else if (performance_counters.max_slice as usize) >= max_cbox {
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
return Err(Error::InvalidParameter);
|
|
|
|
};
|
|
|
|
|
2024-06-24 15:53:45 +02:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
2024-06-24 16:58:32 +02:00
|
|
|
pub unsafe fn monitor_address(addr: *const u8, cpu: u8, max_cbox: u16) -> Result<Vec<u64>, Error> {
|
2024-06-24 15:53:45 +02:00
|
|
|
match arch::determine_cpu_class() {
|
|
|
|
Some(IntelCore) => {
|
2024-06-24 16:58:32 +02:00
|
|
|
unsafe { monitor_core(addr, cpu, max_cbox as usize) }
|
2024-06-24 15:53:45 +02:00
|
|
|
}
|
|
|
|
Some(IntelXeon) => {
|
|
|
|
unsafe { monitor_xeon(addr, cpu, max_cbox as usize) }
|
|
|
|
}
|
|
|
|
Some(IntelXeonSP) => { // TODO
|
|
|
|
Err(UnsupportedCPU)
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
Err(UnsupportedCPU)
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 09:40:43 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn it_works() {
|
2024-06-24 15:53:45 +02:00
|
|
|
let result = 2;
|
|
|
|
assert_eq!(result, 2);
|
2024-06-24 09:40:43 +02:00
|
|
|
}
|
|
|
|
}
|