use byteorder::ByteOrder;
use crate::{BranchSampleFormat, CpuMode, RawData, RawDataU64, ReadFormat, SampleFormat};
use super::{RecordParseInfo, Regs};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SampleRecord<'a> {
pub id: Option<u64>,
pub addr: Option<u64>,
pub stream_id: Option<u64>,
pub raw: Option<RawData<'a>>,
pub ip: Option<u64>,
pub timestamp: Option<u64>,
pub pid: Option<i32>,
pub tid: Option<i32>,
pub cpu: Option<u32>,
pub period: Option<u64>,
pub user_regs: Option<Regs<'a>>,
pub user_stack: Option<(RawData<'a>, u64)>,
pub callchain: Option<RawDataU64<'a>>,
pub phys_addr: Option<u64>,
pub data_page_size: Option<u64>,
pub code_page_size: Option<u64>,
pub intr_regs: Option<Regs<'a>>,
pub cpu_mode: CpuMode,
}
impl<'a> SampleRecord<'a> {
pub fn parse<T: ByteOrder>(
data: RawData<'a>,
misc: u16,
parse_info: &RecordParseInfo,
) -> Result<Self, std::io::Error> {
let sample_format = parse_info.sample_format;
let branch_sample_format = parse_info.branch_sample_format;
let read_format = parse_info.read_format;
let sample_regs_user = parse_info.sample_regs_user;
let user_regs_count = parse_info.user_regs_count;
let sample_regs_intr = parse_info.sample_regs_intr;
let intr_regs_count = parse_info.intr_regs_count;
let cpu_mode = CpuMode::from_misc(misc);
let mut cur = data;
let identifier = if sample_format.contains(SampleFormat::IDENTIFIER) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let ip = if sample_format.contains(SampleFormat::IP) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let (pid, tid) = if sample_format.contains(SampleFormat::TID) {
let pid = cur.read_i32::<T>()?;
let tid = cur.read_i32::<T>()?;
(Some(pid), Some(tid))
} else {
(None, None)
};
let timestamp = if sample_format.contains(SampleFormat::TIME) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let addr = if sample_format.contains(SampleFormat::ADDR) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let id = if sample_format.contains(SampleFormat::ID) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let id = identifier.or(id);
let stream_id = if sample_format.contains(SampleFormat::STREAM_ID) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let cpu = if sample_format.contains(SampleFormat::CPU) {
let cpu = cur.read_u32::<T>()?;
let _reserved = cur.read_u32::<T>()?;
Some(cpu)
} else {
None
};
let period = if sample_format.contains(SampleFormat::PERIOD) {
let period = cur.read_u64::<T>()?;
Some(period)
} else {
None
};
if sample_format.contains(SampleFormat::READ) {
if read_format.contains(ReadFormat::GROUP) {
let _value = cur.read_u64::<T>()?;
if read_format.contains(ReadFormat::TOTAL_TIME_ENABLED) {
let _time_enabled = cur.read_u64::<T>()?;
}
if read_format.contains(ReadFormat::TOTAL_TIME_RUNNING) {
let _time_running = cur.read_u64::<T>()?;
}
if read_format.contains(ReadFormat::ID) {
let _id = cur.read_u64::<T>()?;
}
} else {
let nr = cur.read_u64::<T>()?;
if read_format.contains(ReadFormat::TOTAL_TIME_ENABLED) {
let _time_enabled = cur.read_u64::<T>()?;
}
if read_format.contains(ReadFormat::TOTAL_TIME_RUNNING) {
let _time_running = cur.read_u64::<T>()?;
}
for _ in 0..nr {
let _value = cur.read_u64::<T>()?;
if read_format.contains(ReadFormat::ID) {
let _id = cur.read_u64::<T>()?;
}
}
}
}
let callchain = if sample_format.contains(SampleFormat::CALLCHAIN) {
let callchain_length = cur.read_u64::<T>()?;
let callchain =
cur.split_off_prefix(callchain_length as usize * std::mem::size_of::<u64>())?;
Some(RawDataU64::from_raw_data::<T>(callchain))
} else {
None
};
let raw = if sample_format.contains(SampleFormat::RAW) {
let size = cur.read_u32::<T>()?;
Some(cur.split_off_prefix(size as usize)?)
} else {
None
};
if sample_format.contains(SampleFormat::BRANCH_STACK) {
let nr = cur.read_u64::<T>()?;
if branch_sample_format.contains(BranchSampleFormat::HW_INDEX) {
let _hw_idx = cur.read_u64::<T>()?;
}
for _ in 0..nr {
let _from = cur.read_u64::<T>()?;
let _to = cur.read_u64::<T>()?;
let _flags = cur.read_u64::<T>()?;
}
}
let user_regs = if sample_format.contains(SampleFormat::REGS_USER) {
let regs_abi = cur.read_u64::<T>()?;
if regs_abi == 0 {
None
} else {
let regs_data =
cur.split_off_prefix(user_regs_count as usize * std::mem::size_of::<u64>())?;
let raw_regs = RawDataU64::from_raw_data::<T>(regs_data);
let user_regs = Regs::new(sample_regs_user, raw_regs);
Some(user_regs)
}
} else {
None
};
let user_stack = if sample_format.contains(SampleFormat::STACK_USER) {
let stack_size = cur.read_u64::<T>()?;
let stack = cur.split_off_prefix(stack_size as usize)?;
let dynamic_size = if stack_size != 0 {
cur.read_u64::<T>()?
} else {
0
};
Some((stack, dynamic_size))
} else {
None
};
if sample_format.contains(SampleFormat::WEIGHT) {
let _weight = cur.read_u64::<T>()?;
}
if sample_format.contains(SampleFormat::DATA_SRC) {
let _data_src = cur.read_u64::<T>()?;
}
if sample_format.contains(SampleFormat::TRANSACTION) {
let _transaction = cur.read_u64::<T>()?;
}
let intr_regs = if sample_format.contains(SampleFormat::REGS_INTR) {
let regs_abi = cur.read_u64::<T>()?;
if regs_abi == 0 {
None
} else {
let regs_data =
cur.split_off_prefix(intr_regs_count as usize * std::mem::size_of::<u64>())?;
let raw_regs = RawDataU64::from_raw_data::<T>(regs_data);
let intr_regs = Regs::new(sample_regs_intr, raw_regs);
Some(intr_regs)
}
} else {
None
};
let phys_addr = if sample_format.contains(SampleFormat::PHYS_ADDR) {
Some(cur.read_u64::<T>()?)
} else {
None
};
if sample_format.contains(SampleFormat::AUX) {
let size = cur.read_u64::<T>()?;
cur.skip(size as usize)?;
}
let data_page_size = if sample_format.contains(SampleFormat::DATA_PAGE_SIZE) {
Some(cur.read_u64::<T>()?)
} else {
None
};
let code_page_size = if sample_format.contains(SampleFormat::CODE_PAGE_SIZE) {
Some(cur.read_u64::<T>()?)
} else {
None
};
Ok(Self {
id,
ip,
addr,
stream_id,
raw,
user_regs,
user_stack,
callchain,
cpu,
timestamp,
pid,
tid,
period,
intr_regs,
phys_addr,
data_page_size,
code_page_size,
cpu_mode,
})
}
}