#![deny(missing_docs, rustdoc::broken_intra_doc_links)]
#[allow(
clippy::upper_case_acronyms,
clippy::useless_transmute,
non_upper_case_globals,
non_camel_case_types,
non_snake_case,
dead_code
)]
mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("failed to open .dat file")]
Open,
#[error("failed to get tep_handle")]
Handle,
#[error("failed to find tep_event")]
FindEvent,
#[error("failed to find tep_field")]
FindField,
#[error("invalid PID: {0}")]
InvalidPid(String),
#[error("invalid timestamp: {0}")]
InvalidTimestamp(String),
#[error("invalid string: {0}")]
InvalidString(std::str::Utf8Error),
#[error("failed to read a field")]
ReadField,
}
type Result<T> = std::result::Result<T, Error>;
unsafe fn cptr_to_string(ptr: *mut i8) -> Result<String> {
let c_str: &std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(ptr) };
Ok(c_str.to_str().map_err(Error::InvalidString)?.to_string())
}
pub struct Input(*mut bindings::tracecmd_input);
impl Input {
pub fn new(path: &str) -> Result<Self> {
let handle = unsafe { bindings::tracecmd_open(path.as_ptr() as *mut i8, 0) };
if handle.is_null() {
return Err(Error::Open);
}
Ok(Input(handle))
}
pub fn handle_ref(&self) -> Result<HandleRef> {
let ret = unsafe { bindings::tracecmd_get_tep(self.0) };
if ret.is_null() {
Err(Error::Handle)
} else {
Ok(HandleRef(ret))
}
}
pub fn find_event(&self, rec: &Record) -> Result<Event> {
let handle = self.handle_ref()?;
let ptr = unsafe { bindings::tep_find_event_by_record(handle.0, rec.0) };
if ptr.is_null() {
return Err(Error::FindEvent);
}
let name = unsafe { cptr_to_string((*ptr).name) }.expect("string");
Ok(Event { ptr, name })
}
}
impl Drop for Input {
fn drop(&mut self) {
unsafe {
bindings::tracecmd_close(self.0);
}
}
}
pub struct HandleRef(*mut bindings::tep_handle);
impl HandleRef {
pub fn pid(&self, rec: &Record) -> i32 {
unsafe { bindings::tep_data_pid(self.0, rec.0) }
}
}
pub struct Record(*mut bindings::tep_record);
impl Record {
pub fn ts(&self) -> u64 {
unsafe { *self.0 }.ts
}
}
pub struct Event {
ptr: *mut bindings::tep_event,
pub name: String,
}
impl Event {
pub fn print_fields(&self, rec: &Record) {
println!("fields: {:?}", self.get_fields(rec));
}
pub fn get_fields(&self, rec: &Record) -> String {
let mut seq: bindings::trace_seq = Default::default();
unsafe {
bindings::trace_seq_init(&mut seq);
bindings::trace_seq_reset(&mut seq);
bindings::tep_record_print_fields(&mut seq, rec.0, self.ptr);
bindings::trace_seq_terminate(&mut seq);
};
let msg = unsafe { std::slice::from_raw_parts(seq.buffer as *mut u8, seq.len as usize) };
std::str::from_utf8(msg).unwrap().to_string()
}
}
pub trait Handler {
type AccumulatedData: Default;
fn callback(
input: &mut Input,
rec: &mut Record,
cpu: i32,
data: &mut Self::AccumulatedData,
) -> i32;
fn process(input: &mut Input) -> std::result::Result<Self::AccumulatedData, i32> {
let mut data: Self::AccumulatedData = Default::default();
let ret = unsafe {
bindings::tracecmd_iterate_events(
input.0,
std::ptr::null_mut(),
0,
Some(c_callback::<Self>),
&mut data as *mut _ as *mut std::ffi::c_void,
)
};
if ret == 0 {
Ok(data)
} else {
Err(ret)
}
}
fn process_multi(inputs: &mut [Input]) -> std::result::Result<Self::AccumulatedData, i32> {
let mut data: Self::AccumulatedData = Default::default();
let nr_handles = inputs.len() as i32;
let mut handles = inputs.iter().map(|input| input.0).collect::<Vec<_>>();
let ret = unsafe {
bindings::tracecmd_iterate_events_multi(
handles.as_mut_ptr(),
nr_handles,
Some(c_callback::<Self>),
&mut data as *mut _ as *mut std::ffi::c_void,
)
};
if ret == 0 {
Ok(data)
} else {
Err(ret)
}
}
}
unsafe extern "C" fn c_callback<T: Handler + ?Sized>(
input: *mut bindings::tracecmd_input,
rec: *mut bindings::tep_record,
cpu: i32,
raw_data: *mut std::ffi::c_void,
) -> i32 {
let mut input = Input(input);
let mut rec = Record(rec);
let mut data: T::AccumulatedData = Default::default();
std::ptr::copy_nonoverlapping(
raw_data,
&mut data as *mut _ as *mut std::ffi::c_void,
std::mem::size_of::<T::AccumulatedData>(),
);
let res = T::callback(&mut input, &mut rec, cpu, &mut data);
std::ptr::copy_nonoverlapping(
&mut data as *mut _ as *mut std::ffi::c_void,
raw_data,
std::mem::size_of::<T::AccumulatedData>(),
);
std::mem::forget(input);
std::mem::forget(data);
res
}