xentrace_parser/trace.rs
1use std::{fs, io, ops::Deref, path::Path};
2
3use self::parse::parse_trace;
4use crate::{record::Record, Error, Result};
5
6/// Represents a parsed XenTrace binary file.
7///
8/// The trace is truncated to the last readable record,
9/// returning no errors.
10///
11/// # Examples
12///
13/// ```
14/// use xentrace_parser::{Result, Trace};
15///
16/// fn function() -> Result<()> {
17/// let trace = Trace::from_file("/path/to/xentrace.bin")?;
18///
19/// // Alternatively, you can create a trace from a reader:
20/// // let file = std::fs::File::open("/path/to/xentrace.bin")?;
21/// // let bufreader = std::io::BufReader::new(file);
22/// // let trace = Trace::from_reader(bufreader);
23///
24/// for record in trace.iter() {
25/// println!("{:?}", record);
26/// }
27///
28/// Ok(())
29/// }
30/// ```
31#[derive(Debug)]
32pub struct Trace {
33 records: Box<[Record]>,
34 cpu_count: u32,
35}
36
37impl Trace {
38 /// Constructs a `Trace` from a file specified by its path.
39 ///
40 /// # Errors
41 ///
42 /// This function will return an error if it fails to open
43 /// the trace file or if it fails to parse the file contents.
44 ///
45 /// # Examples
46 ///
47 /// ```
48 /// use xentrace_parser::{Result, Trace};
49 ///
50 /// fn main() -> Result<()> {
51 /// let trace = Trace::from_file("/path/to/xentrace.bin")?;
52 /// println!("{:?}", trace);
53 /// Ok(())
54 /// }
55 /// ```
56 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
57 fs::File::open(path)
58 .map_err(|e| Error::io_error("Failed to open trace file", e))
59 .map(io::BufReader::new)
60 .and_then(parse_trace)
61 }
62
63 /// Constructs a `Trace` from a byte slice.
64 ///
65 /// # Errors
66 ///
67 /// This function will return an error if it fails to parse the trace data.
68 ///
69 /// # Examples
70 ///
71 /// ```
72 /// use xentrace_parser::{Trace, Result};
73 ///
74 /// fn main() -> Result<()> {
75 /// let bytes: Vec<u8> = vec![/* byte data */];
76 /// let trace = Trace::from_bytes(&bytes)?;
77 /// println!("{:?}", trace);
78 /// Ok(())
79 /// }
80 /// ```
81 pub fn from_bytes<B: AsRef<[u8]>>(bytes: B) -> Result<Self> {
82 let reader = io::Cursor::new(bytes);
83 parse_trace(reader)
84 }
85
86 /// Constructs a `Trace` from any type that implements `io::Read`.
87 ///
88 /// # Errors
89 ///
90 /// This function will return an error if it fails to parse the trace data.
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// use std::fs::File;
96 /// use std::io::BufReader;
97 /// use xentrace_parser::{Trace, Result};
98 ///
99 /// fn main() -> Result<()> {
100 /// let file = File::open("/path/to/xentrace.bin")?;
101 /// let bufreader = BufReader::new(file);
102 /// let trace = Trace::from_reader(bufreader)?;
103 /// println!("{:?}", trace);
104 /// Ok(())
105 /// }
106 /// ```
107 pub fn from_reader<R: io::Read>(reader: R) -> Result<Self> {
108 parse_trace(reader)
109 }
110
111 /// Returns the number of [Records](crate::record::Record) parsed from the trace file.
112 ///
113 /// # Examples
114 ///
115 /// ```
116 /// use xentrace_parser::{Trace, Result};
117 ///
118 /// fn main() -> Result<()> {
119 /// let trace = Trace::from_file("/path/to/xentrace.bin")?;
120 /// let record_count = trace.record_count();
121 /// println!("Record count: {}", record_count);
122 /// Ok(())
123 /// }
124 /// ```
125 pub fn record_count(&self) -> usize {
126 self.records.len()
127 }
128
129 /// Returns the count of CPUs used in the trace file.
130 ///
131 /// **Note:** The value is calculated based on the
132 /// number of unique CPUs found in the trace data.
133 ///
134 /// # Examples
135 ///
136 /// ```
137 /// use xentrace_parser::{Trace, Result};
138 ///
139 /// fn main() -> Result<()> {
140 /// let trace = Trace::from_file("/path/to/xentrace.bin")?;
141 /// let cpu_count = trace.cpu_count();
142 /// println!("CPU count: {}", cpu_count);
143 /// Ok(())
144 /// }
145 /// ```
146 pub fn cpu_count(&self) -> u32 {
147 self.cpu_count
148 }
149}
150
151impl Deref for Trace {
152 type Target = [Record];
153
154 fn deref(&self) -> &Self::Target {
155 &self.records
156 }
157}
158
159// Functions for trace parsing logic
160mod parse {
161 use std::collections::HashMap;
162
163 use fxhash::FxBuildHasher;
164
165 use super::*;
166 use crate::{
167 record::{Domain, Event, EventCode, EVENT_EXTRA_CAPACITY},
168 util::IoReadUtil,
169 };
170
171 const TRC_TRACE_CPU_CHANGE: u32 = 0x0001F003;
172 const TRC_SCHED_TO_RUN: u32 = 0x00021F0F;
173
174 struct ParserData {
175 domains: HashMap<u32, Domain, FxBuildHasher>,
176 last_cpu: u32,
177 records: Vec<Record>,
178 last_tsc: u64,
179 }
180
181 pub(super) fn parse_trace<R: io::Read>(mut rdr: R) -> Result<Trace> {
182 let mut data = ParserData {
183 domains: HashMap::with_capacity_and_hasher(
184 u16::BITS as usize,
185 FxBuildHasher::default(),
186 ),
187 last_cpu: 0,
188 records: Vec::with_capacity((u16::MAX / 2) as usize),
189 last_tsc: 0,
190 };
191
192 while let Some(record) = next_record(&mut rdr, &mut data)? {
193 if record.event.code == TRC_TRACE_CPU_CHANGE {
194 data.last_cpu = record.event.extra[0].unwrap_or(0);
195 continue;
196 }
197
198 data.records.push(record);
199 }
200
201 let records = {
202 data.records.sort();
203 data.records.into_boxed_slice()
204 };
205
206 match data.domains.len().try_into() {
207 Ok(cpu_count) => Ok(Trace { records, cpu_count }),
208 Err(_) => Err(Error::new(format_args!(
209 "Failed to set host CPU count: {} > u32::MAX",
210 data.domains.len()
211 ))),
212 }
213 }
214
215 fn next_record<R: io::Read>(rdr: &mut R, data: &mut ParserData) -> Result<Option<Record>> {
216 fn read_event<R: io::Read>(rdr: &mut R, last_tsc: &mut u64) -> Result<Option<Event>> {
217 // Truncate the reader at the first misread header
218 let Some(header) = rdr.read_ne_u32().ok() else {
219 return Ok(None);
220 };
221
222 let code = EventCode::from(header & 0x0FFFFFF);
223
224 let tsc = {
225 // has "tsc" value ?
226 if header & (1 << 31) > 0 {
227 *last_tsc = rdr
228 .read_ne_u64()
229 .map_err(|e| Error::io_error("Failed to read tsc value", e))?;
230 }
231
232 *last_tsc
233 };
234
235 let extra = {
236 let len = ((header >> 28) as usize) & EVENT_EXTRA_CAPACITY;
237 let mut extra = [None; EVENT_EXTRA_CAPACITY];
238
239 for entry in extra.iter_mut().take(len) {
240 *entry = rdr
241 .read_ne_u32()
242 .map(Some)
243 .map_err(|e| Error::io_error("Failed to read extra value", e))?;
244 }
245
246 extra
247 };
248
249 Ok(Some(Event { code, tsc, extra }))
250 }
251
252 // "next_record" function
253 let Some(event) = read_event(rdr, &mut data.last_tsc)? else {
254 return Ok(None);
255 };
256
257 let cpu = data.last_cpu;
258 let domain = if event.code == (event.code & TRC_SCHED_TO_RUN) {
259 let extra_0 = event.extra[0].unwrap_or(0);
260 let domain = Domain::from(extra_0);
261 data.domains.insert(cpu, domain);
262 domain
263 } else {
264 data.domains.get(&cpu).copied().unwrap_or_default()
265 };
266
267 Ok(Some(Record { cpu, domain, event }))
268 }
269}