trace_recorder_parser/snapshot/
recorder_data.rs1use crate::snapshot::event::{Event, EventParser, EventRecord, EventType};
2use crate::snapshot::markers::{DebugMarker, MarkerBytes};
3use crate::snapshot::object_properties::{ObjectProperties, ObjectPropertyTable};
4use crate::snapshot::symbol_table::{SymbolCrc6, SymbolTable};
5use crate::snapshot::Error;
6use crate::time::Frequency;
7use crate::types::{
8 Endianness, FloatEncoding, KernelPortIdentity, KernelVersion, ObjectClass, ObjectHandle,
9 OffsetBytes, Protocol, TrimmedString,
10};
11use byteordered::ByteOrdered;
12use std::collections::{BTreeMap, VecDeque};
13use std::io::{Read, Seek, SeekFrom};
14use tracing::{debug, error, warn};
15
16#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
17pub struct RecorderData {
18 pub protocol: Protocol,
19 pub kernel_version: KernelVersion,
20 pub kernel_port: KernelPortIdentity,
21 pub endianness: Endianness,
22 pub minor_version: u8,
23 pub irq_priority_order: u8,
24 pub filesize: u32,
25 pub num_events: u32,
27 pub max_events: u32,
28 pub next_free_index: u32,
29 pub buffer_is_full: bool,
30 pub frequency: Frequency,
31 pub abs_time_last_event: u32,
32 pub abs_time_last_event_second: u32,
33 pub recorder_active: bool,
34 pub isr_tail_chaining_threshold: u32,
35 pub heap_mem_usage: u32,
36 pub heap_mem_max_usage: u32,
37 pub is_using_16bit_handles: bool,
38 pub object_property_table: ObjectPropertyTable,
39 pub symbol_table: SymbolTable,
40 pub float_encoding: FloatEncoding,
41 pub internal_error_occured: bool,
42 pub system_info: String,
43
44 start_offset: OffsetBytes,
46 event_data_offset: OffsetBytes,
48 }
50
51impl RecorderData {
52 pub fn locate_and_parse<R: Read + Seek>(r: &mut R) -> Result<Self, Error> {
53 let mut tmp_buffer = VecDeque::with_capacity(1024);
54 let mut r = ByteOrdered::native(r);
55
56 let mut offset = r.stream_position()?;
58 tmp_buffer.clear();
59 tmp_buffer.resize(MarkerBytes::SIZE, 0);
60 r.read_exact(tmp_buffer.make_contiguous())?;
61 let start_offset = loop {
62 if tmp_buffer.make_contiguous() == MarkerBytes::Start.as_bytes() {
63 break offset;
64 }
65
66 let _ = tmp_buffer.pop_front();
67 tmp_buffer.push_back(r.read_u8()?);
68 offset += 1;
69 };
70
71 debug!(start_offset = start_offset, "Found start markers");
72 r.seek(SeekFrom::Start(start_offset))?;
73 MarkerBytes::Start.read(&mut r)?;
74
75 let kvi_pos = r.stream_position()?;
76 let mut kernel_version_identity: [u8; 2] = [0; 2];
77 r.read_exact(&mut kernel_version_identity)?;
78 let kernel_version = KernelVersion(kernel_version_identity);
79 let kernel_port = kernel_version
80 .port_identity()
81 .map_err(|e| Error::KernelVersion(kvi_pos, e.0))?;
82 let endianness = kernel_version
83 .endianness()
84 .map_err(|e| Error::KernelVersion(kvi_pos, e.0))?;
85 debug!(kernel_version = %kernel_version, kernel_port = %kernel_port, endianness = ?endianness, "Found kernel version");
86 let minor_version = r.read_u8()?;
87 debug!(minor_version = minor_version, "Found minor version");
88
89 if kernel_port != KernelPortIdentity::FreeRtos {
90 warn!("Kernel port {kernel_port} is not officially supported");
91 }
92
93 if minor_version != 7 {
94 warn!("Version {minor_version} is not officially supported");
95 }
96
97 let irq_priority_order = r.read_u8()?;
98
99 let mut r = ByteOrdered::new(r.into_inner(), byteordered::Endianness::from(endianness));
101 let filesize = r.read_u32()?;
102 debug!(filesize = filesize, "Found recorder data region size");
103
104 let num_events = r.read_u32()?;
105 let max_events = r.read_u32()?;
106 let next_free_index = r.read_u32()?;
107 let buffer_is_full = r.read_u32()?;
108 let frequency = Frequency(r.read_u32()?);
109 let abs_time_last_event = r.read_u32()?;
110 let abs_time_last_event_second = r.read_u32()?;
111 let recorder_active = r.read_u32()?;
112 let isr_tail_chaining_threshold = r.read_u32()?;
113 let heap_mem_max_usage = r.read_u32()?;
114 let heap_mem_usage = r.read_u32()?;
115 DebugMarker::Marker0.read(&mut r)?;
116 let is_using_16bit_handles = r.read_u32()? != 0;
117
118 if is_using_16bit_handles {
119 return Err(Error::Unsupported16bitHandles);
120 }
121
122 if frequency.is_unitless() {
123 warn!("Time base frequency is zero, units will be in ticks only");
124 }
125
126 let object_property_table_offset = r.stream_position()?;
128 let num_object_classes = r.read_u32()?;
129 let object_property_table_size = r.read_u32()?;
130 debug!(
131 object_property_table_offset = object_property_table_offset,
132 num_object_classes = num_object_classes,
133 object_property_table_size = object_property_table_size,
134 "Found object property table region"
135 );
136
137 let num_object_classes_u16_allocation_size_words =
138 round_up_nearest_2(num_object_classes) as usize;
139 let num_object_classes_u8_allocation_size_words =
140 round_up_nearest_4(num_object_classes) as usize;
141
142 let num_objects_per_class: Vec<u16> = if is_using_16bit_handles {
145 let mut words = vec![0; num_object_classes_u16_allocation_size_words];
146 r.read_u16_into(&mut words)?;
147 words
148 } else {
149 let mut words = vec![0; num_object_classes_u8_allocation_size_words];
150 r.read_exact(&mut words)?;
151 words.into_iter().map(|w| w.into()).collect()
152 };
153
154 let mut name_len_per_class = vec![0; num_object_classes_u8_allocation_size_words];
155 r.read_exact(&mut name_len_per_class)?;
156
157 let mut total_bytes_per_class = vec![0; num_object_classes_u8_allocation_size_words];
158 r.read_exact(&mut total_bytes_per_class)?;
159
160 let mut start_index_of_class = vec![0; num_object_classes_u16_allocation_size_words];
161 r.read_u16_into(&mut start_index_of_class)?;
162
163 let pos_at_prop_table = r.stream_position()?;
164 let mut queue_object_properties = BTreeMap::new();
165 let mut semaphore_object_properties = BTreeMap::new();
166 let mut mutex_object_properties = BTreeMap::new();
167 let mut task_object_properties = BTreeMap::new();
168 let mut isr_object_properties = BTreeMap::new();
169 let mut timer_object_properties = BTreeMap::new();
170 let mut event_group_object_properties = BTreeMap::new();
171 let mut stream_buffer_object_properties = BTreeMap::new();
172 let mut message_buffer_object_properties = BTreeMap::new();
173 for obj_class in ObjectClass::enumerate().iter() {
174 let obj_class_index = obj_class.into_usize();
175 let num_objects = num_objects_per_class[obj_class_index];
176 let name_len = name_len_per_class[obj_class_index];
177 let total_bytes_per_obj = total_bytes_per_class[obj_class_index];
178 let start_index = start_index_of_class[obj_class_index];
179
180 if total_bytes_per_obj == 0 {
181 error!("Skipping empty object class {obj_class} property table entry");
182 continue;
184 }
185
186 if obj_class_index as u32 >= num_object_classes {
187 warn!("Skipping unsupported object class {obj_class} property table entry");
188 r.seek(SeekFrom::Current(i64::from(
189 total_bytes_per_obj as u32 * num_objects as u32,
190 )))?;
191 continue;
192 }
193
194 let class_offset = r.stream_position()?;
195 if (class_offset - pos_at_prop_table) != u64::from(start_index) {
196 warn!("Offset of object class {obj_class} {class_offset}, relative to the property table {} doesn't match the reported start index {start_index}", class_offset - pos_at_prop_table);
197 }
198 let end_of_class =
199 class_offset + u64::from(num_objects as u32 * total_bytes_per_obj as u32);
200
201 let mut raw_obj_handle = 1;
203
204 while r.stream_position()? < end_of_class {
206 let obj_start_pos = r.stream_position()?;
207
208 if name_len == 0 {
210 warn!("Skipping object class {obj_class} entry because name length is zero");
211 r.seek(SeekFrom::Current(i64::from(total_bytes_per_obj)))?;
212 continue;
213 }
214
215 tmp_buffer.clear();
217 tmp_buffer.resize(name_len as _, 0);
218 r.read_exact(tmp_buffer.make_contiguous())?;
219
220 if tmp_buffer[0] == 0 {
221 r.seek(SeekFrom::Current(i64::from(total_bytes_per_obj - name_len)))?;
223 continue;
224 }
225
226 let name = if tmp_buffer[0] == 0x01 {
228 None
229 } else {
230 Some(TrimmedString::from_raw(tmp_buffer.make_contiguous()).into())
231 };
232
233 let mut properties = [0; 4];
235 for p in properties.iter_mut().take(obj_class.properties_size()) {
236 *p = r.read_u8()?;
237 }
238
239 let obj_handle = ObjectHandle::new_unchecked(raw_obj_handle);
242 raw_obj_handle = raw_obj_handle.saturating_add(1);
243
244 match obj_class {
245 ObjectClass::Queue => {
246 let obj = ObjectProperties::new(name, properties);
247 debug!("Found object property {obj} at {obj_start_pos}");
248 queue_object_properties.insert(obj_handle, obj);
249 }
250 ObjectClass::Semaphore => {
251 let obj = ObjectProperties::new(name, properties);
252 debug!("Found object property {obj} at {obj_start_pos}");
253 semaphore_object_properties.insert(obj_handle, obj);
254 }
255 ObjectClass::Mutex => {
256 let obj = ObjectProperties::new(name, properties);
257 debug!("Found object property {obj} at {obj_start_pos}");
258 mutex_object_properties.insert(obj_handle, obj);
259 }
260 ObjectClass::Task => {
261 let obj = ObjectProperties::new(name, properties);
262 debug!("Found object property {obj} at {obj_start_pos}");
263 task_object_properties.insert(obj_handle, obj);
264 }
265 ObjectClass::Isr => {
266 let obj = ObjectProperties::new(name, properties);
267 debug!("Found object property {obj} at {obj_start_pos}");
268 isr_object_properties.insert(obj_handle, obj);
269 }
270 ObjectClass::Timer => {
271 let obj = ObjectProperties::new(name, properties);
272 debug!("Found object property {obj} at {obj_start_pos}");
273 timer_object_properties.insert(obj_handle, obj);
274 }
275 ObjectClass::EventGroup => {
276 let obj = ObjectProperties::new(name, properties);
277 debug!("Found object property {obj} at {obj_start_pos}");
278 event_group_object_properties.insert(obj_handle, obj);
279 }
280 ObjectClass::StreamBuffer => {
281 let obj = ObjectProperties::new(name, properties);
282 debug!("Found object property {obj} at {obj_start_pos}");
283 stream_buffer_object_properties.insert(obj_handle, obj);
284 }
285 ObjectClass::MessageBuffer => {
286 let obj = ObjectProperties::new(name, properties);
287 debug!("Found object property {obj} at {obj_start_pos}");
288 message_buffer_object_properties.insert(obj_handle, obj);
289 }
290 ObjectClass::StateMachine => {
291 }
293 }
294 }
295 }
296
297 let pos_after_prop_table = r.stream_position()?;
299 let prop_table_bytes_read = (pos_after_prop_table - pos_at_prop_table) as i64;
300 let prop_table_allocation_size = i64::from(round_up_nearest_4(object_property_table_size));
301 if prop_table_bytes_read < prop_table_allocation_size {
302 r.seek(SeekFrom::Current(
303 prop_table_allocation_size - prop_table_bytes_read,
304 ))?;
305 }
306
307 DebugMarker::Marker1.read(&mut r)?;
308
309 let symbol_table_offset = r.stream_position()?;
311 let symbol_table_size = r.read_u32()?;
312 debug!(
313 symbol_table_offset = symbol_table_offset,
314 symbol_table_size = symbol_table_size,
315 "Found symbol table region"
316 );
317
318 let next_free_symbol_index = r.read_u32()?;
322 if next_free_symbol_index > symbol_table_size {
323 warn!("Next free symbol index {next_free_symbol_index} exceeds symbol table size {symbol_table_size}");
324 }
325 let end_of_symbol_table_region =
326 r.stream_position()? + u64::from(round_up_nearest_4(symbol_table_size));
327 let start_of_symbol_table_bytes = r.stream_position()?;
328 let end_of_symbol_entries = start_of_symbol_table_bytes + u64::from(next_free_symbol_index);
329
330 let unused_index_slot = r.read_u8()?;
331 if unused_index_slot != 0 {
332 warn!(
333 "Reserved symbol table entry 0 contains an invalid value 0x{unused_index_slot:X}"
334 );
335 }
336
337 let mut symbol_table = SymbolTable::default();
339 while r.stream_position()? < end_of_symbol_entries {
340 let start_of_symbol_table_entry = r.stream_position()?;
341
342 let _next_entry_index = r.read_u16()?;
344 let channel = r.read_u16()?;
345 tmp_buffer.clear();
347 loop {
348 let sym_byte = r.read_u8()?;
349 if sym_byte == 0 {
350 let extra_null = r.read_u8()?;
352 if extra_null != 0 {
353 warn!(
354 "Found non-zero NULL terminated symbol table entry at offeset {}",
355 r.stream_position()?
356 );
357 }
358 break;
359 } else {
360 tmp_buffer.push_back(sym_byte);
361 }
362 }
363 let crc = SymbolCrc6::new(tmp_buffer.make_contiguous());
364 symbol_table.insert(
365 ObjectHandle::new(
366 ((start_of_symbol_table_entry - start_of_symbol_table_bytes) & 0xFFFF) as u32,
367 )
368 .ok_or(Error::InvalidSymbolTableIndex(start_of_symbol_table_entry))?,
369 ObjectHandle::new(channel.into()),
370 crc,
371 TrimmedString::from_raw(tmp_buffer.make_contiguous()).into(),
372 );
373 }
374
375 r.seek(SeekFrom::Start(end_of_symbol_table_region))?;
377
378 r.seek(SeekFrom::Current(
384 (std::mem::size_of::<u16>() * SymbolTable::NUM_LATEST_ENTRY_OF_CHECKSUMS) as _,
385 ))?;
386
387 let float_encoding = FloatEncoding::from_bits(r.read_u32()?);
391
392 let internal_error_occured = r.read_u32()?;
393 if internal_error_occured != 0 {
394 warn!("The 'internal_error_occured' field is set to {internal_error_occured}");
395 }
396
397 DebugMarker::Marker2.read(&mut r)?;
398
399 tmp_buffer.clear();
401 tmp_buffer.resize(NUM_SYSTEM_INFO_BYTES, 0);
402 r.read_exact(tmp_buffer.make_contiguous())?;
403 let system_info = TrimmedString::from_raw(tmp_buffer.make_contiguous()).0;
404 if !system_info.is_empty() {
405 debug!(system_info = %system_info, "Found system info");
406 }
407
408 DebugMarker::Marker3.read(&mut r)?;
409
410 let event_data_offset = r.stream_position()?;
412 r.seek(SeekFrom::Current(4 * i64::from(max_events)))?;
413
414 let maybe_user_event_buffer_id = r.read_u16()?;
417 if maybe_user_event_buffer_id == 0 {
418 let end_of_secondary_blocks = r.read_u16()?;
421 if end_of_secondary_blocks != 0 {
422 warn!("End of secondary blocks field ({end_of_secondary_blocks}) should be zero");
423 }
424 } else {
425 return Err(Error::UnsupportedUserEventBuffer);
427 }
428
429 MarkerBytes::End.read(&mut r)?;
430
431 Ok(RecorderData {
432 protocol: Protocol::Snapshot,
433 kernel_version,
434 kernel_port,
435 endianness,
436 minor_version,
437 irq_priority_order,
438 filesize,
439 num_events,
440 max_events,
441 next_free_index,
442 buffer_is_full: buffer_is_full != 0,
443 frequency,
444 abs_time_last_event,
445 abs_time_last_event_second,
446 recorder_active: recorder_active != 0,
447 isr_tail_chaining_threshold,
448 heap_mem_usage,
449 heap_mem_max_usage,
450 is_using_16bit_handles,
451 object_property_table: ObjectPropertyTable {
452 queue_object_properties,
453 semaphore_object_properties,
454 mutex_object_properties,
455 task_object_properties,
456 isr_object_properties,
457 timer_object_properties,
458 event_group_object_properties,
459 stream_buffer_object_properties,
460 message_buffer_object_properties,
461 },
462 symbol_table,
463 float_encoding,
464 internal_error_occured: internal_error_occured != 0,
465 system_info,
466
467 start_offset,
469 event_data_offset,
470 })
471 }
472
473 pub fn event_records<'r, R: Read + Seek + Send>(
474 &'r self,
475 r: &'r mut R,
476 ) -> Result<Box<dyn Iterator<Item = Result<EventRecord, Error>> + Send + 'r>, Error> {
477 if (self.num_events < self.max_events) || ((self.num_events % self.max_events) == 0) {
478 let num_events_clamped = std::cmp::min(self.num_events, self.max_events);
480 r.seek(SeekFrom::Start(self.event_data_offset))?;
481 Ok(Box::new((0..num_events_clamped).map(|_| {
482 let mut record = [0; EventRecord::SIZE];
483 r.read_exact(&mut record)?;
484 Ok(EventRecord::new(record))
485 })))
486 } else {
487 let num_tail_region_events = self.max_events - self.next_free_index;
493 let tail_offset = self.next_free_index * EventRecord::SIZE as u32;
494 r.seek(SeekFrom::Start(
495 self.event_data_offset + u64::from(tail_offset),
496 ))?;
497
498 let iter = (0..self.max_events).map(move |event_index| {
499 let mut record = [0; EventRecord::SIZE];
500 r.read_exact(&mut record)?;
501
502 if event_index + 1 == num_tail_region_events {
504 r.seek(SeekFrom::Start(self.event_data_offset))?;
505 }
506
507 Ok(EventRecord::new(record))
508 });
509
510 Ok(Box::new(iter))
511 }
512 }
513
514 pub fn events<'r, R: Read + Seek + Send>(
515 &'r self,
516 r: &'r mut R,
517 ) -> Result<impl Iterator<Item = Result<(EventType, Event), Error>> + 'r, Error> {
518 let mut parser = EventParser::new(self.endianness.into());
519 let iter = self.event_records(r)?.filter_map(move |item| match item {
520 Ok(er) => match parser
521 .parse(&self.object_property_table, &self.symbol_table, er)
522 .map_err(Error::from)
523 {
524 Ok(maybe_ev) => maybe_ev.map(Ok),
525 Err(e) => Some(Err(e)),
526 },
527 Err(e) => Some(Err(e)),
528 });
529 Ok(iter)
530 }
531}
532
533const NUM_SYSTEM_INFO_BYTES: usize = 80;
535
536fn round_up_nearest_2(n: u32) -> u32 {
539 2 * ((n + 1) / 2)
540}
541
542fn round_up_nearest_4(n: u32) -> u32 {
545 4 * ((n + 3) / 4)
546}