Compare commits

...

1 Commits

Author SHA1 Message Date
2d8e696909 Failed experiment to use the event layer
Unfortunately the time stamps did not properly match up
2025-09-03 11:44:26 +02:00
4 changed files with 262 additions and 118 deletions

65
Cargo.lock generated
View File

@ -265,26 +265,6 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "derive_more"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "discard"
version = "1.0.4"
@ -442,18 +422,6 @@ version = "0.2.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
[[package]]
name = "libipt"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c3143c4dae9794d23fa2bbc6315847fdf3ef718caa09a7ba09238bce19fe9d4"
dependencies = [
"bitflags 2.9.1",
"derive_more",
"libipt-sys",
"num_enum",
]
[[package]]
name = "libipt-sys"
version = "0.2.4"
@ -550,28 +518,6 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "num_enum"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a"
dependencies = [
"num_enum_derive",
"rustversion",
]
[[package]]
name = "num_enum_derive"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d"
dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.104",
]
[[package]]
name = "once_cell"
version = "1.21.3"
@ -609,15 +555,6 @@ dependencies = [
"syn 2.0.104",
]
[[package]]
name = "proc-macro-crate"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
dependencies = [
"toml_edit",
]
[[package]]
name = "proc-macro-hack"
version = "0.5.20+deprecated"
@ -637,7 +574,7 @@ dependencies = [
name = "pt-dump-decoder"
version = "0.1.0"
dependencies = [
"libipt",
"libipt-sys",
"memmap2",
]

View File

@ -61,7 +61,8 @@ fn setup_nyx(out_dir: &str) {
println!("cargo:warning=Cloning and building QEMU-Nyx. This may take a while...");
shell(
out_dir,
"git clone https://github.com/nyx-fuzz/QEMU-Nyx --depth 1",
// "git clone https://github.com/nyx-fuzz/QEMU-Nyx --depth 1",
"git clone /fs/scratch/smdavenh/bachelor-project/QEMU-Nyx --depth 1",
);
let is_debug_build = match env::var("DEBUG").unwrap().as_str() {

View File

@ -7,5 +7,6 @@ edition = "2024"
[dependencies]
libipt = { version = "0.4.0", features = ["libipt_master"] }
#libipt = { version = "0.4.0", features = ["libipt_master"] }
libipt-sys = "0.2.4"
memmap2 = "0.9.7"

View File

@ -1,5 +1,3 @@
use libipt::enc_dec_builder::EncoderDecoderBuilder;
use libipt::packet::{Packet, PacketDecoder};
use memmap2::Mmap;
use std::error::Error;
use std::fs::File;
@ -8,71 +6,278 @@ use std::time::Duration;
#[derive(Debug)]
pub struct AnalyzeData {
pub packets: u64,
pub cycles: u64,
pub time: Duration,
pub time_outside_hypervisor: Duration,
pub total_time: Duration,
pub time_in_client: Duration,
}
#[derive(Debug)]
enum Scope {
#[derive(Debug, Clone)]
struct Scope {
kind: ScopeKind,
start_tsc: u64,
end_tsc: u64,
}
impl Scope {
#[track_caller]
fn new(kind: ScopeKind, start_tsc: u64, end_tsc: u64) -> Self {
assert!(
start_tsc <= end_tsc,
"start_tsc {start_tsc} is greater than end_tsc {end_tsc}"
);
Self {
kind,
start_tsc,
end_tsc,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ScopeKind {
Main,
PreRun,
PostRun,
PacketGenerationDisabled,
}
const PT_EVENT_ENABLED: u32 = 0;
const PT_EVENT_DISABLED: u32 = 1;
const PT_EVENT_ASYNC_DISABLED: u32 = 2;
const PT_EVENT_PTWRITE: u32 = 16;
#[derive(Debug, Clone, Copy)]
enum DecoderState {
Enabled {
kind: ScopeKind,
start_tsc: u64,
},
Disabled {
enabled_kind: ScopeKind,
start_tsc: Option<u64>,
},
}
pub fn analyze_dump(path: impl AsRef<Path>) -> Result<AnalyzeData, Box<dyn Error>> {
let trace_file = File::open(path)?;
let mmap = unsafe { Mmap::map(&trace_file)? };
let builder = EncoderDecoderBuilder::<PacketDecoder<()>>::new();
// I hope this is safe if the buffer is never written to
let builder = unsafe { builder.buffer_from_raw(mmap.as_ptr() as *mut _, mmap.len()) };
let mut events = vec![];
let mut state = DecoderState::Disabled {
enabled_kind: ScopeKind::PreRun,
start_tsc: None,
};
let mut decoder = builder.build()?;
// Required before it can be used
decoder.sync_forward()?;
unsafe {
let mut config: libipt_sys::pt_config = std::mem::zeroed();
config.size = size_of::<libipt_sys::pt_config>();
config.begin = mmap.as_ptr() as *mut _;
config.end = config.begin.add(mmap.len());
let mut segments = vec![(Scope::PreRun, 0u64)];
let mut total = 0u64;
for packet in decoder {
total += 1;
match packet {
Ok(Packet::Cyc(cyc)) => {
segments.last_mut().unwrap().1 += cyc.value();
}
Ok(Packet::Vmcs(_)) => {
// last_packet_vmcs = true;
}
Ok(Packet::Ptw(ptwrite)) => {
if ptwrite.payload() == 42 {
// Main enter
segments.push((Scope::Main, 0));
} else if ptwrite.payload() == 43 {
// Main exit
segments.push((Scope::PostRun, 0));
} else {
println!("GOT PTWRITE!!!!!: {ptwrite:?}");
}
}
Ok(_) => {}
Err(error) => println!("Got error: {error:?}"),
let decoder = libipt_sys::pt_evt_alloc_decoder(&config);
if decoder.is_null() {
panic!("Could not create decoder");
}
let status = libipt_sys::pt_evt_sync_forward(decoder);
if status < 0 {
panic!("Could not sync: {status}");
}
let mut last_tsc = 0;
let mut last_event = 0;
loop {
let mut event = std::mem::MaybeUninit::uninit();
let status = libipt_sys::pt_evt_next(
decoder,
event.as_mut_ptr(),
size_of::<libipt_sys::pt_event>(),
);
if status < 0 {
if (-status & (1 << 2)) == 0 {
println!("Expected EOI, got {status}");
}
break;
}
let event = event.assume_init();
if (event.has_tsc() > 0 && event.tsc < last_tsc && event.type_ != 20 && event.type_ != 21) {
println!("WARN: Event {} happens before last event ({last_event})!", event.type_);
continue;
}
last_tsc = event.tsc;
last_event = event.type_;
// println!("Has tsc: {}, tsc: {}, {}, {}", event.has_tsc(), event.tsc, event.lost_mtc, event.lost_cyc);
let next_state = match event.type_ {
PT_EVENT_ENABLED | PT_EVENT_ASYNC_DISABLED => match state {
DecoderState::Disabled { enabled_kind, .. } => {
assert!(event.has_tsc() > 0);
DecoderState::Enabled {
kind: enabled_kind,
start_tsc: event.tsc,
}
}
DecoderState::Enabled { .. } => continue,
},
PT_EVENT_DISABLED => match state {
DecoderState::Enabled { kind, .. } => {
assert!(event.has_tsc() > 0);
DecoderState::Disabled {
enabled_kind: kind,
start_tsc: Some(event.tsc),
}
}
DecoderState::Disabled { .. } => {
continue;
}
},
PT_EVENT_PTWRITE => {
assert!(event.has_tsc() > 0);
let value = event.variant.ptwrite.payload;
let new_scope = match value {
42 => ScopeKind::Main,
43 => ScopeKind::PostRun,
other => panic!("Unknown ptwrite {other}"),
};
match state {
DecoderState::Disabled { .. } => {
unreachable!("Ptwrite cannot happen if the decoder is deactivate")
}
DecoderState::Enabled { .. } => DecoderState::Enabled {
kind: new_scope,
start_tsc: event.tsc,
},
}
}
_ => continue,
};
println!("{state:?}, {next_state:?}, lost cyc: {}, lost mtc: {}", event.lost_cyc, event.lost_mtc);
match (state, next_state) {
(
DecoderState::Enabled { kind, start_tsc },
DecoderState::Enabled {
start_tsc: end_tsc, ..
}
| DecoderState::Disabled {
start_tsc: Some(end_tsc),
..
},
) => {
events.push(Scope::new(kind, start_tsc, end_tsc));
}
(
DecoderState::Disabled {
start_tsc: Some(start_tsc),
..
},
DecoderState::Enabled {
start_tsc: end_tsc, ..
},
) => events.push(Scope::new(
ScopeKind::PacketGenerationDisabled,
start_tsc,
end_tsc,
)),
(
DecoderState::Disabled {
start_tsc: None, ..
},
DecoderState::Enabled { .. },
) => {}
_ => unreachable!("{state:?}, {next_state:?}"),
}
state = next_state;
}
// TODO: Add last event!
libipt_sys::pt_evt_free_decoder(decoder);
}
// dbg!(&segments);
let cycles_main: u64 = segments
.iter()
.filter(|(scope, _)| matches!(scope, Scope::Main))
.map(|(_, cycles)| cycles)
.sum();
let cycles_total = segments.iter().map(|(_, cycles)| cycles).sum();
let total_seconds = cycles_total as f64 / 2_700_000_000.0;
let total_seconds_no_hypervisor = cycles_main as f64 / 2_700_000_000.0;
let total_duration = filter_time(&events, |_| true);
let duration_in_client = filter_time(&events, |kind| matches!(kind, ScopeKind::Main));
Ok(AnalyzeData {
packets: total,
cycles: cycles_total,
time: Duration::from_secs_f64(total_seconds),
time_outside_hypervisor: Duration::from_secs_f64(total_seconds_no_hypervisor),
total_time: total_duration,
time_in_client: duration_in_client,
})
}
fn filter_time(events: &[Scope], mut filter: impl FnMut(ScopeKind) -> bool) -> Duration {
let iter = events.iter().filter(|scope| filter(scope.kind));
let total_cycles = iter
.map(|scope| scope.end_tsc - scope.start_tsc)
.sum::<u64>() as f64;
let cycles_per_second = 2_700_000_000.0;
Duration::from_secs_f64(total_cycles / cycles_per_second)
}
// pub fn analyze_dump(path: impl AsRef<Path>) -> Result<AnalyzeData, Box<dyn Error>> {
// let trace_file = File::open(path)?;
// let mmap = unsafe { Mmap::map(&trace_file)? };
//
// let builder = EncoderDecoderBuilder::<PacketDecoder<()>>::new();
// // I hope this is safe if the buffer is never written to
// let builder = unsafe { builder.buffer_from_raw(mmap.as_ptr() as *mut _, mmap.len()) };
//
// let mut decoder = builder.build()?;
// // Required before it can be used
// decoder.sync_forward()?;
//
// let mut segments = vec![(Scope::PreRun, 0u64)];
// let mut total = 0u64;
//
// for packet in decoder {
// total += 1;
// match packet {
// Ok(Packet::Cyc(cyc)) => {
// segments.last_mut().unwrap().1 += cyc.value();
// }
// Ok(Packet::TipPgd(pgd)) => {
// segments.push((Scope::PacketGenerationDisabled, 0));
// }
// Ok(Packet::TipPge(pge)) => {
// match segments.as_slice() {
// [.., (previous_segment, _), (Scope::PacketGenerationDisabled, _)] => {
// segments.push((*previous_segment, 0));
// }
// [_] | [] => {}
// [.., _, (other_scope, _)] => {
// panic!("Invalid segments layout: {other_scope:?}");
// }
// }
// }
// Ok(Packet::Ptw(ptwrite)) => {
// if ptwrite.payload() == 42 {
// // Main enter
// segments.push((Scope::Main, 0));
// } else if ptwrite.payload() == 43 {
// // Main exit
// segments.push((Scope::PostRun, 0));
// } else {
// println!("GOT PTWRITE!!!!!: {ptwrite:?}");
// }
// }
// Ok(_) => {}
// Err(error) => println!("Got error: {error:?}"),
// }
// }
// dbg!(&segments);
// let cycles_main: u64 = segments
// .iter()
// .filter(|(scope, _)| matches!(scope, Scope::Main))
// .map(|(_, cycles)| cycles)
// .sum();
// let cycles_total = segments.iter().map(|(_, cycles)| cycles).sum();
// let total_seconds = cycles_total as f64 / 2_700_000_000.0;
// let total_seconds_no_hypervisor = cycles_main as f64 / 2_700_000_000.0;
// Ok(AnalyzeData {
// packets: total,
// cycles: cycles_total,
// time: Duration::from_secs_f64(total_seconds),
// time_outside_hypervisor: Duration::from_secs_f64(total_seconds_no_hypervisor),
// })
// }