| Crates.io | iptr-decoder |
| lib.rs | iptr-decoder |
| version | 0.1.2 |
| created_at | 2026-01-17 14:17:21.692569+00 |
| updated_at | 2026-01-18 12:03:46.785585+00 |
| description | Idiomatic Rust-style low-level Intel PT trace handler. |
| homepage | https://github.com/Evian-Zhang/iptr/ |
| repository | https://github.com/Evian-Zhang/iptr/ |
| max_upload_size | |
| id | 2050495 |
| size | 104,871 |
iptr-decoder is a crate of iptr project, providing idiomatic Rust-style low-level Intel PT trace handling APIs.
To use this crate, add this crate to your Cargo.toml:
[dependencies]
iptr-decoder = "0.1"
The core functionalities are designed within the trait HandlePacket. A typical usage example is like the following code snippet.
use iptr_decoder::{DecoderContext, DecodeOptions, HandlePacket};
struct MyPtHandler;
impl HandlePacket for MyPtHandler {
// We don't produce high-level errors for simplicity
type Error = std::convert::Infallible;
// Required method, will be invoked at the begining of decoding
fn at_decode_begin(&mut self) -> Result<(), Self::Error> {
Ok(())
}
// One of the provided methods, will be invoked when a short
// TNT packet is encountered. The actual byte is `packet_byte`, and
// `highest_bit` refers to the index of highest bit that represents
// a Taken/Not-Taken bit.
fn on_short_tnt_packet(
&mut self,
context: &DecoderContext,
packet_byte: std::num::NonZero<u8>,
highest_bit: u32,
) -> Result<(), Self::Error> {
println!("Short TNT packet get! Byte is {packet_byte:x}");
Ok(())
}
}
// Use the defined `MyPtHandler` to decode Intel PT traces
fn handle_pt_trace(pt: &[u8]) {
let mut packet_handler = MyPtHandler;
iptr_decoder::decode(pt, DecodeOptions::default(), &mut packet_handler).unwrap();
}
The HandlePacket trait has a lot of provided methods, each of which is corresponding to a type of PT packet. When a PT packet is decoded, the right method will be invoked with extracted values. The default implementation for each packet handlers is an NOP, and you can override each implementation like the code snippet above.
Apart from customized HandlePacket implementors, this crate also provides some common packet handlers, which are organized in the iptr_decoder::packet_handler module.
For example, the PacketHandlerRawLogger logs all packet's information, and PacketCounter can tell us how many PT packets are decoded in total.
Moreover, we provide a powerful CombinedPacketHandler. With this structure, you can use the provided common packet handlers alongwith your own customized HandlePacket implementors:
use iptr_decoder::{
DecodeOptions, DecoderContext, HandlePacket,
packet_handler::{combined::CombinedPacketHandler, log::PacketHandlerRawLogger},
};
struct MyPtHandler;
impl HandlePacket for MyPtHandler {
type Error = std::convert::Infallible;
fn at_decode_begin(&mut self) -> Result<(), Self::Error> {
Ok(())
}
// Other methods...
}
fn handle_pt_trace(pt: &[u8]) {
let my_packet_handler = MyPtHandler;
let log_handler = PacketHandlerRawLogger::default();
let mut packet_handler = CombinedPacketHandler::new(log_handler, my_packet_handler);
iptr_decoder::decode(pt, DecodeOptions::default(), &mut packet_handler).unwrap();
}
When running the handle_pt_trace, both the log_handler and my_packet_handler will be invoked, which is very useful when debugging your own packet handler.
If you want to get the branch and basic block information, you can refer to the iptr-edge-analyzer crate, which provides a more comprehensive, complex and efficient solution.
This crate has the following features:
log_handler
Enable iptr_decoder::packet_handler::log, which includes handler for logging low level packets.
This feature is not enabled by default.
alloc
Enable the alloc dependency. Used only for log_handler feature for now.
This feature is not enabled by default.