diff --git a/Cargo.lock b/Cargo.lock index aa9ab02..35503b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", ] diff --git a/build.rs b/build.rs index e2d5cc7..759e7db 100644 --- a/build.rs +++ b/build.rs @@ -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() { diff --git a/pt-dump-decoder/Cargo.toml b/pt-dump-decoder/Cargo.toml index 738990f..8d5df37 100644 --- a/pt-dump-decoder/Cargo.toml +++ b/pt-dump-decoder/Cargo.toml @@ -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" diff --git a/pt-dump-decoder/src/lib.rs b/pt-dump-decoder/src/lib.rs index 264ddba..1c06453 100644 --- a/pt-dump-decoder/src/lib.rs +++ b/pt-dump-decoder/src/lib.rs @@ -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, + }, } pub fn analyze_dump(path: impl AsRef) -> Result> { let trace_file = File::open(path)?; let mmap = unsafe { Mmap::map(&trace_file)? }; - let builder = EncoderDecoderBuilder::>::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::(); + 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::(), + ); + 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::() 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) -> Result> { +// let trace_file = File::open(path)?; +// let mmap = unsafe { Mmap::map(&trace_file)? }; +// +// let builder = EncoderDecoderBuilder::>::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), +// }) +// }