linux_perf_data/jitdump/
jitdump_reader.rs1use linux_perf_event_reader::{Endianness, RawData};
2use std::io::{Read, Seek};
3
4use super::buffered_reader::BufferedReader;
5use super::error::JitDumpError;
6use super::header::JitDumpHeader;
7use super::read_exact::ReadExactOrUntilEof;
8use super::record::{JitDumpRawRecord, JitDumpRecordHeader, JitDumpRecordType};
9
10#[derive(Debug, Clone)]
18pub struct JitDumpReader<R: Read> {
19 reader: BufferedReader<R>,
20 header: JitDumpHeader,
21 endian: Endianness,
22 pending_record_header: Option<JitDumpRecordHeader>,
25 current_record_start_offset: u64,
26}
27
28impl<R: Read> JitDumpReader<R> {
29 pub fn new(reader: R) -> Result<Self, JitDumpError> {
32 Self::new_with_buffer_size(reader, 4 * 1024)
33 }
34
35 pub fn new_with_buffer_size(mut reader: R, buffer_size: usize) -> Result<Self, JitDumpError> {
37 let mut buf = vec![0; buffer_size];
38 let first_data_len = reader
39 .read_exact_or_until_eof(&mut buf)
40 .map_err(JitDumpError::Io)?;
41
42 let first_data = &buf[..first_data_len];
43 let header = JitDumpHeader::parse(RawData::Single(first_data))?;
44 let total_header_size = header.total_size;
45 let endian = match &header.magic {
46 b"DTiJ" => Endianness::LittleEndian,
47 b"JiTD" => Endianness::BigEndian,
48 _ => panic!(),
49 };
50
51 Ok(Self {
52 reader: BufferedReader::new_with_partially_read_buffer(
53 reader,
54 buf,
55 total_header_size as usize,
56 first_data_len,
57 ),
58 header,
59 endian,
60 pending_record_header: None,
61 current_record_start_offset: total_header_size as u64,
62 })
63 }
64
65 pub fn header(&self) -> &JitDumpHeader {
67 &self.header
68 }
69
70 pub fn endian(&self) -> Endianness {
72 self.endian
73 }
74
75 pub fn next_record_header(&mut self) -> Result<Option<JitDumpRecordHeader>, std::io::Error> {
77 if self.pending_record_header.is_none() {
78 if let Some(record_header_bytes) =
79 self.reader.consume_data(JitDumpRecordHeader::SIZE)?
80 {
81 self.pending_record_header =
82 Some(JitDumpRecordHeader::parse(self.endian, record_header_bytes).unwrap());
83 }
84 };
85 Ok(self.pending_record_header.clone())
86 }
87
88 pub fn next_record_timestamp(&mut self) -> Result<Option<u64>, std::io::Error> {
97 Ok(self.next_record_header()?.map(|r| r.timestamp))
98 }
99
100 pub fn next_record_type(&mut self) -> Result<Option<JitDumpRecordType>, std::io::Error> {
102 Ok(self.next_record_header()?.map(|r| r.record_type))
103 }
104
105 pub fn next_record_offset(&self) -> u64 {
107 self.current_record_start_offset
108 }
109
110 pub fn next_record(&mut self) -> Result<Option<JitDumpRawRecord<'_>>, std::io::Error> {
116 let record_size = match self.next_record_header()? {
117 Some(header) => header.total_size,
118 None => return Ok(None),
119 };
120 let body_size = record_size as usize - JitDumpRecordHeader::SIZE;
121
122 match self.reader.consume_data(body_size)? {
123 Some(record_body_data) => {
124 let record_header = self.pending_record_header.take().unwrap();
125 let start_offset = self.current_record_start_offset;
126 self.current_record_start_offset += record_size as u64;
127 Ok(Some(JitDumpRawRecord {
128 endian: self.endian,
129 start_offset,
130 record_size,
131 record_type: record_header.record_type,
132 timestamp: record_header.timestamp,
133 body: record_body_data,
134 }))
135 }
136 None => Ok(None),
137 }
138 }
139}
140
141impl<R: Read + Seek> JitDumpReader<R> {
142 pub fn skip_next_record(&mut self) -> Result<bool, std::io::Error> {
152 let record_size = match self.next_record_header()? {
153 Some(record_header) => record_header.total_size,
154 None => return Ok(false),
155 };
156 let body_size = record_size as usize - JitDumpRecordHeader::SIZE; self.reader.skip_bytes(body_size)?;
159 self.pending_record_header.take();
160 self.current_record_start_offset += record_size as u64;
161 Ok(true)
162 }
163}