use linux_perf_event_reader::{Endianness, RawData};
use std::io::{Read, Seek};
use super::buffered_reader::BufferedReader;
use super::error::JitDumpError;
use super::header::JitDumpHeader;
use super::read_exact::ReadExactOrUntilEof;
use super::record::{JitDumpRawRecord, JitDumpRecordHeader, JitDumpRecordType};
#[derive(Debug, Clone)]
pub struct JitDumpReader<R: Read> {
reader: BufferedReader<R>,
header: JitDumpHeader,
endian: Endianness,
pending_record_header: Option<JitDumpRecordHeader>,
current_record_start_offset: u64,
}
impl<R: Read> JitDumpReader<R> {
pub fn new(reader: R) -> Result<Self, JitDumpError> {
Self::new_with_buffer_size(reader, 4 * 1024)
}
pub fn new_with_buffer_size(mut reader: R, buffer_size: usize) -> Result<Self, JitDumpError> {
let mut buf = vec![0; buffer_size];
let first_data_len = reader
.read_exact_or_until_eof(&mut buf)
.map_err(JitDumpError::Io)?;
let first_data = &buf[..first_data_len];
let header = JitDumpHeader::parse(RawData::Single(first_data))?;
let total_header_size = header.total_size;
let endian = match &header.magic {
b"DTiJ" => Endianness::LittleEndian,
b"JiTD" => Endianness::BigEndian,
_ => panic!(),
};
Ok(Self {
reader: BufferedReader::new_with_partially_read_buffer(
reader,
buf,
total_header_size as usize,
first_data_len,
),
header,
endian,
pending_record_header: None,
current_record_start_offset: total_header_size as u64,
})
}
pub fn header(&self) -> &JitDumpHeader {
&self.header
}
pub fn endian(&self) -> Endianness {
self.endian
}
pub fn next_record_header(&mut self) -> Result<Option<JitDumpRecordHeader>, std::io::Error> {
if self.pending_record_header.is_none() {
if let Some(record_header_bytes) =
self.reader.consume_data(JitDumpRecordHeader::SIZE)?
{
self.pending_record_header =
Some(JitDumpRecordHeader::parse(self.endian, record_header_bytes).unwrap());
}
};
Ok(self.pending_record_header.clone())
}
pub fn next_record_timestamp(&mut self) -> Result<Option<u64>, std::io::Error> {
Ok(self.next_record_header()?.map(|r| r.timestamp))
}
pub fn next_record_type(&mut self) -> Result<Option<JitDumpRecordType>, std::io::Error> {
Ok(self.next_record_header()?.map(|r| r.record_type))
}
pub fn next_record_offset(&self) -> u64 {
self.current_record_start_offset
}
pub fn next_record(&mut self) -> Result<Option<JitDumpRawRecord<'_>>, std::io::Error> {
let record_size = match self.next_record_header()? {
Some(header) => header.total_size,
None => return Ok(None),
};
let body_size = record_size as usize - JitDumpRecordHeader::SIZE;
match self.reader.consume_data(body_size)? {
Some(record_body_data) => {
let record_header = self.pending_record_header.take().unwrap();
let start_offset = self.current_record_start_offset;
self.current_record_start_offset += record_size as u64;
Ok(Some(JitDumpRawRecord {
endian: self.endian,
start_offset,
record_size,
record_type: record_header.record_type,
timestamp: record_header.timestamp,
body: record_body_data,
}))
}
None => Ok(None),
}
}
}
impl<R: Read + Seek> JitDumpReader<R> {
pub fn skip_next_record(&mut self) -> Result<bool, std::io::Error> {
let record_size = match self.next_record_header()? {
Some(record_header) => record_header.total_size,
None => return Ok(false),
};
let body_size = record_size as usize - JitDumpRecordHeader::SIZE;
self.reader.skip_bytes(body_size)?;
self.pending_record_header.take();
self.current_record_start_offset += record_size as u64;
Ok(true)
}
}