Skip to content
This repository has been archived by the owner on Nov 9, 2022. It is now read-only.

Commit

Permalink
Start work on our own Intel PT decoder.
Browse files Browse the repository at this point in the history
This is the first step: a packet parser, i.e. taking the raw bytes given
to us by the PT chip and parsing them into Rust structs representing PT
packets.

I arrived at the approach you see in this change after trying several
other, less-satisfying, ways of parsing binary packets (with bitwise
field granularity).

Initially I was parsing packets by hand, which was very fiddly indeed
and was what prompted me to look for tools to help. The `packed_struct`
crate worked, but lead to some quite verbose code (and I wrote lots of
macros to try and work around that). Still unsatisfied, I found
`binrw`, which was almost perfect, only let down by the unmaintained and
bit-rotted `modular_bitfield` crate, which is required for bitwise
parsing (see Robbepop/modular-bitfield#65).

This led me to a solution using `deku`, which I've been very happy with:
https://github.com/sharksforarms/deku

Not all kinds of packets are implemented: only enough to parse a tiny
little trace to completion. We can add new packets on-demand as we see
them crop up in the wild.
  • Loading branch information
vext01 committed Nov 2, 2022
1 parent 9e517be commit eec7ad9
Show file tree
Hide file tree
Showing 8 changed files with 747 additions and 3 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ tempfile = "3.1.0"
phdrs = { git = "https://github.com/softdevteam/phdrs" }
strum = { version = "0.24.1", features = ["derive", "strum_macros"] }
strum_macros = "0.24.3"
deku = "0.14.1"

[build-dependencies]
cc = "1.0.62"
Expand Down
4 changes: 4 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ fn main() {
}
println!("cargo:rustc-link-lib=static=ipt");
}

#[cfg(target_arch = "x86_64")]
println!("cargo:rustc-cfg=decoder_ykpt");

c_build.include("src/util");
c_build.include("src"); // to find `hwtracer_private.h`.
c_build.compile("hwtracer_c");
Expand Down
2 changes: 1 addition & 1 deletion src/collect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ impl TraceCollectorBuilder {
_pt_conf,
)?)));
#[cfg(not(collector_perf))]
unreachable!();
return Err(HWTracerError::CollectorUnavailable(self.kind));
}
}
}
Expand Down
27 changes: 25 additions & 2 deletions src/decode/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ pub(crate) mod libipt;
#[cfg(decoder_libipt)]
use libipt::LibIPTTraceDecoder;

#[cfg(decoder_ykpt)]
mod ykpt;
#[cfg(decoder_ykpt)]
use ykpt::YkPTTraceDecoder;

#[derive(Clone, Copy, Debug, EnumIter)]
pub enum TraceDecoderKind {
LibIPT,
YkPT,
}

impl TraceDecoderKind {
Expand All @@ -29,10 +35,16 @@ impl TraceDecoderKind {
fn match_platform(&self) -> Result<(), HWTracerError> {
match self {
Self::LibIPT => {
#[cfg(decoder_libipt)]
return Ok(());
#[cfg(not(decoder_libipt))]
return Err(HWTracerError::DecoderUnavailable(Self::LibIPT));
#[cfg(decoder_libipt)]
}
Self::YkPT => {
#[cfg(decoder_ykpt)]
return Ok(());
#[cfg(not(decoder_ykpt))]
return Err(HWTracerError::DecoderUnavailable(Self::YkPT));
}
}
}
Expand Down Expand Up @@ -76,7 +88,18 @@ impl TraceDecoderBuilder {
pub fn build(self) -> Result<Box<dyn TraceDecoder>, HWTracerError> {
self.kind.match_platform()?;
match self.kind {
TraceDecoderKind::LibIPT => Ok(Box::new(LibIPTTraceDecoder::new())),
TraceDecoderKind::LibIPT => {
#[cfg(decoder_libipt)]
return Ok(Box::new(LibIPTTraceDecoder::new()));
#[cfg(not(decoder_libipt))]
return Err(HWTracerError::DecoderUnavailable(self.kind));
}
TraceDecoderKind::YkPT => {
#[cfg(decoder_ykpt)]
return Ok(Box::new(YkPTTraceDecoder::new()));
#[cfg(not(decoder_ykpt))]
return Err(HWTracerError::DecoderUnavailable(self.kind));
}
}
}
}
Expand Down
61 changes: 61 additions & 0 deletions src/decode/ykpt/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! The Yk PT trace decoder.
use crate::{decode::TraceDecoder, errors::HWTracerError, Block, Trace};

mod packet_parser;
use packet_parser::PacketParser;

pub(crate) struct YkPTTraceDecoder {}

impl TraceDecoder for YkPTTraceDecoder {
fn new() -> Self {
Self {}
}

fn iter_blocks<'t>(
&'t self,
trace: &'t dyn Trace,
) -> Box<dyn Iterator<Item = Result<Block, HWTracerError>> + '_> {
let itr = YkPTBlockIterator {
errored: false,
parser: PacketParser::new(trace.bytes()),
};
Box::new(itr)
}
}

/// Iterate over the blocks of an Intel PT trace using the fast Yk PT decoder.
struct YkPTBlockIterator<'t> {
/// Set to true when an error has occured.
errored: bool,
/// PT packet iterator.
parser: PacketParser<'t>,
}

impl<'t> Iterator for YkPTBlockIterator<'t> {
type Item = Result<Block, HWTracerError>;

fn next(&mut self) -> Option<Self::Item> {
if !self.errored {
// FIXME: For now this is dummy code to prevent dead-code warnings. Later block binding
// logic will go here.
self.parser.next().unwrap().unwrap();
}
None
}
}

#[cfg(test)]
mod tests {
use crate::{
collect::TraceCollectorBuilder,
decode::{test_helpers, TraceDecoderKind},
};

#[ignore] // FIXME
#[test]
fn ten_times_as_many_blocks() {
let tc = TraceCollectorBuilder::new().build().unwrap();
test_helpers::ten_times_as_many_blocks(tc, TraceDecoderKind::YkPT);
}
}
Loading

0 comments on commit eec7ad9

Please sign in to comment.