1pub mod extended;
11pub mod hob;
12pub mod known;
13
14use crate::{BinaryGuid, performance::error::Error, performance_debug_assert};
15use alloc::vec::Vec;
16use core::{fmt, fmt::Debug, mem};
17use scroll::Pread;
18use zerocopy::{FromBytes, IntoBytes};
19use zerocopy_derive::*;
20
21pub const FPDT_MAX_PERF_RECORD_SIZE: usize = u8::MAX as usize;
23
24#[repr(C, packed)]
26#[derive(Debug, Clone, Copy, FromBytes, IntoBytes, Immutable)]
27pub struct PerformanceRecordHeader {
28 pub record_type: u16,
30 pub length: u8,
32 pub revision: u8,
34}
35
36impl PerformanceRecordHeader {
37 pub const SIZE: usize = core::mem::size_of::<Self>();
39
40 pub const fn new(record_type: u16, length: u8, revision: u8) -> Self {
42 Self { record_type, length, revision }
43 }
44
45 pub fn to_le(self) -> Self {
47 Self { record_type: self.record_type.to_le(), length: self.length, revision: self.revision }
48 }
49}
50
51impl TryFrom<&[u8]> for PerformanceRecordHeader {
52 type Error = &'static str;
53
54 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
55 if bytes.len() < Self::SIZE {
56 return Err("Insufficient bytes for PerformanceRecordHeader");
57 }
58
59 Self::read_from_prefix(bytes)
60 .map_err(|_| "Failed to parse PerformanceRecordHeader from bytes")
61 .map(|(header, _)| header.to_le())
62 }
63}
64
65impl From<PerformanceRecordHeader> for [u8; mem::size_of::<PerformanceRecordHeader>()] {
66 fn from(header: PerformanceRecordHeader) -> Self {
67 let le_header = header.to_le();
68 le_header.as_bytes().try_into().expect("Size mismatch in From implementation")
69 }
70}
71
72pub const PERFORMANCE_RECORD_HEADER_SIZE: usize = mem::size_of::<PerformanceRecordHeader>();
74
75pub trait PerformanceRecord {
79 fn record_type(&self) -> u16;
81
82 fn revision(&self) -> u8;
84
85 fn write_data_into(&self, buff: &mut [u8], offset: &mut usize) -> Result<(), Error>;
88
89 fn write_into(&self, buff: &mut [u8], offset: &mut usize) -> Result<usize, Error> {
98 let start = *offset;
99 if start + PERFORMANCE_RECORD_HEADER_SIZE > buff.len() {
100 return Err(Error::Serialization);
101 }
102
103 let mut header = PerformanceRecordHeader::new(self.record_type(), 0, self.revision());
105
106 *offset += PERFORMANCE_RECORD_HEADER_SIZE;
108 self.write_data_into(buff, offset)?;
109
110 let record_size = *offset - start;
112 if record_size > u8::MAX as usize {
113 return Err(Error::RecordTooLarge { size: record_size });
114 }
115 header.length = record_size as u8;
116
117 let header_bytes: [u8; mem::size_of::<PerformanceRecordHeader>()] = header.into();
119 buff[start..start + PERFORMANCE_RECORD_HEADER_SIZE].copy_from_slice(&header_bytes);
120
121 Ok(record_size)
122 }
123}
124
125#[derive(Debug)]
127pub struct GenericPerformanceRecord<T: AsRef<[u8]>> {
128 pub record_type: u16,
130 pub length: u8,
132 pub revision: u8,
138 pub data: T,
140}
141
142impl<T: AsRef<[u8]>> GenericPerformanceRecord<T> {
143 pub fn new(record_type: u16, length: u8, revision: u8, data: T) -> Self {
145 Self { record_type, length, revision, data }
146 }
147
148 pub fn header(&self) -> PerformanceRecordHeader {
150 PerformanceRecordHeader::new(self.record_type, self.length, self.revision)
151 }
152}
153
154impl<T: AsRef<[u8]>> PerformanceRecord for GenericPerformanceRecord<T> {
155 fn record_type(&self) -> u16 {
156 self.record_type
157 }
158
159 fn revision(&self) -> u8 {
160 self.revision
161 }
162
163 fn write_data_into(&self, buff: &mut [u8], offset: &mut usize) -> Result<(), Error> {
164 let remaining = buff.len().saturating_sub(*offset);
165 let data = self.data.as_ref();
166 if data.len() > remaining {
167 return Err(Error::Serialization);
168 }
169 buff[*offset..*offset + data.len()].copy_from_slice(data);
170 *offset += data.len();
171 Ok(())
172 }
173}
174
175pub enum PerformanceRecordBuffer {
177 Unpublished(Vec<u8>),
179 Published(&'static mut [u8], usize),
181}
182
183impl PerformanceRecordBuffer {
184 pub const fn new() -> Self {
186 Self::Unpublished(Vec::new())
187 }
188
189 pub fn push_record<T: PerformanceRecord>(&mut self, record: T) -> Result<usize, Error> {
191 match self {
192 Self::Unpublished(buffer) => {
193 let mut offset = buffer.len();
194 buffer.resize(offset + FPDT_MAX_PERF_RECORD_SIZE, 0);
195 let Ok(record_size) = record.write_into(buffer, &mut offset) else {
196 return performance_debug_assert!("Record size should not exceed FPDT_MAX_PERF_RECORD_SIZE");
197 };
198 buffer.truncate(offset);
199 Ok(record_size)
200 }
201 Self::Published(buffer, offset) => record.write_into(buffer, offset).map_err(|_| Error::OutOfResources),
202 }
203 }
204
205 pub fn report(&mut self, buffer: &'static mut [u8]) -> Result<(), Error> {
207 let current_buffer = match self {
208 PerformanceRecordBuffer::Unpublished(b) => b.as_slice(),
209 PerformanceRecordBuffer::Published(_, _) => {
210 return performance_debug_assert!("PerformanceRecordBuffer already reported.");
211 }
212 };
213 let size = current_buffer.len();
214 if buffer.len() < size {
215 return Err(Error::BufferTooSmall);
216 }
217 buffer[..size].clone_from_slice(current_buffer);
218 *self = Self::Published(buffer, size);
219 Ok(())
220 }
221
222 pub fn buffer(&self) -> &[u8] {
224 match &self {
225 Self::Unpublished(b) => b.as_slice(),
226 Self::Published(b, len) => &b[..*len],
227 }
228 }
229
230 pub fn iter(&self) -> Iter<'_> {
232 Iter::new(self.buffer())
233 }
234
235 pub fn size(&self) -> usize {
237 match &self {
238 Self::Unpublished(b) => b.len(),
239 Self::Published(_, len) => *len,
240 }
241 }
242
243 pub fn capacity(&self) -> usize {
245 match &self {
246 Self::Unpublished(b) => b.capacity(),
247 Self::Published(b, _) => b.len(),
248 }
249 }
250}
251
252impl Default for PerformanceRecordBuffer {
253 fn default() -> Self {
254 Self::new()
255 }
256}
257
258impl Debug for PerformanceRecordBuffer {
259 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
260 let size = self.size();
261 let capacity = self.capacity();
262 let nb_report = self.iter().count();
263 let records = self.iter().collect::<Vec<_>>();
264 f.debug_struct("PerformanceRecordBuffer")
265 .field("size", &size)
266 .field("capacity", &capacity)
267 .field("nb_report", &nb_report)
268 .field("records", &records)
269 .finish()
270 }
271}
272
273pub struct Iter<'a> {
275 buffer: &'a [u8],
276}
277
278impl<'a> Iter<'a> {
279 pub fn new(buffer: &'a [u8]) -> Self {
281 Self { buffer }
282 }
283}
284
285impl<'a> Iterator for Iter<'a> {
286 type Item = GenericPerformanceRecord<&'a [u8]>;
287
288 fn next(&mut self) -> Option<Self::Item> {
289 if self.buffer.is_empty() {
290 return None;
291 }
292 let mut offset = 0;
293 let record_type = self.buffer.gread::<u16>(&mut offset).unwrap();
294 let length = self.buffer.gread::<u8>(&mut offset).unwrap();
295 let revision = self.buffer.gread::<u8>(&mut offset).unwrap();
296
297 let data = &self.buffer[offset..length as usize];
298 self.buffer = &self.buffer[length as usize..];
299 Some(GenericPerformanceRecord::new(record_type, length, revision, data))
300 }
301}
302
303#[repr(C, packed)]
311#[derive(Debug, Clone, Copy)]
312pub struct GuidEventRecordData {
313 pub progress_id: u16,
315 pub apic_id: u32,
317 pub timestamp: u64,
319 pub guid: [u8; 16],
321}
322
323impl GuidEventRecordData {
324 pub const NAME: &'static str = "GUID Event";
326}
327
328impl fmt::Display for GuidEventRecordData {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 let progress_id = self.progress_id;
332 let apic_id = self.apic_id;
333 let timestamp = self.timestamp;
334 let guid = BinaryGuid::from_bytes(&self.guid);
335 write!(f, "progress_id={}, apic_id={}, timestamp={}, guid={}", progress_id, apic_id, timestamp, guid)
336 }
337}
338
339#[repr(C, packed)]
344#[derive(Debug, Clone, Copy)]
345pub struct DynamicStringEventRecordData {
346 pub progress_id: u16,
348 pub apic_id: u32,
350 pub timestamp: u64,
352 pub guid: [u8; 16],
354 }
356
357impl DynamicStringEventRecordData {
358 pub const NAME: &'static str = "Dynamic String Event";
360
361 pub fn extract_string(full_data: &[u8]) -> &str {
363 if full_data.len() <= core::mem::size_of::<Self>() {
364 return "";
365 }
366 let string_bytes = &full_data[core::mem::size_of::<Self>()..];
367 let string_len = string_bytes.iter().position(|&b| b == 0).unwrap_or(string_bytes.len());
369 core::str::from_utf8(&string_bytes[..string_len]).unwrap_or("<invalid UTF-8>")
370 }
371}
372
373impl fmt::Display for DynamicStringEventRecordData {
374 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 let progress_id = self.progress_id;
377 let apic_id = self.apic_id;
378 let timestamp = self.timestamp;
379 let guid = BinaryGuid::from_bytes(&self.guid);
380 write!(f, "progress_id: 0x{:04X}, apic_id: {}, timestamp: {}, guid: {}", progress_id, apic_id, timestamp, guid)
381 }
382}
383
384#[repr(C, packed)]
388#[derive(Debug, Clone, Copy)]
389pub struct DualGuidStringEventRecordData {
390 pub progress_id: u16,
392 pub apic_id: u32,
394 pub timestamp: u64,
396 pub guid_1: [u8; 16],
398 pub guid_2: [u8; 16],
400 }
402
403impl DualGuidStringEventRecordData {
404 pub const NAME: &'static str = "Dual GUID String Event";
406
407 pub fn extract_string(full_data: &[u8]) -> &str {
409 if full_data.len() <= core::mem::size_of::<Self>() {
410 return "";
411 }
412 let string_bytes = &full_data[core::mem::size_of::<Self>()..];
413 let string_len = string_bytes.iter().position(|&b| b == 0).unwrap_or(string_bytes.len());
415 core::str::from_utf8(&string_bytes[..string_len]).unwrap_or("<invalid UTF-8>")
416 }
417}
418
419impl fmt::Display for DualGuidStringEventRecordData {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 let progress_id = self.progress_id;
423 let apic_id = self.apic_id;
424 let timestamp = self.timestamp;
425 let guid_1 = BinaryGuid::from_bytes(&self.guid_1);
426 let guid_2 = BinaryGuid::from_bytes(&self.guid_2);
427 write!(
428 f,
429 "progress_id: 0x{:04X}, apic_id: {}, timestamp: {}, guid_1: {}, guid_2: {}",
430 progress_id, apic_id, timestamp, guid_1, guid_2
431 )
432 }
433}
434
435#[repr(C, packed)]
439#[derive(Debug, Clone, Copy)]
440pub struct GuidQwordEventRecordData {
441 pub progress_id: u16,
443 pub apic_id: u32,
445 pub timestamp: u64,
447 pub guid: [u8; 16],
449 pub qword: u64,
451}
452
453impl GuidQwordEventRecordData {
454 pub const NAME: &'static str = "GUID QWORD Event";
456}
457
458impl fmt::Display for GuidQwordEventRecordData {
459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 let progress_id = self.progress_id;
462 let apic_id = self.apic_id;
463 let timestamp = self.timestamp;
464 let guid = BinaryGuid::from_bytes(&self.guid);
465 let qword = self.qword;
466 write!(
467 f,
468 "progress_id: 0x{:04X}, apic_id: {}, timestamp: {}, guid: {}, qword: 0x{:016X}",
469 progress_id, apic_id, timestamp, guid, qword
470 )
471 }
472}
473
474#[repr(C, packed)]
478#[derive(Debug, Clone, Copy)]
479pub struct GuidQwordStringEventRecordData {
480 pub progress_id: u16,
482 pub apic_id: u32,
484 pub timestamp: u64,
486 pub guid: [u8; 16],
488 pub qword: u64,
490 }
492
493impl GuidQwordStringEventRecordData {
494 pub const NAME: &'static str = "GUID QWORD String Event";
496
497 pub fn extract_string(full_data: &[u8]) -> &str {
499 if full_data.len() <= core::mem::size_of::<Self>() {
500 return "";
501 }
502 let string_bytes = &full_data[core::mem::size_of::<Self>()..];
503 let string_len = string_bytes.iter().position(|&b| b == 0).unwrap_or(string_bytes.len());
505 core::str::from_utf8(&string_bytes[..string_len]).unwrap_or("<invalid UTF-8>")
506 }
507}
508
509impl fmt::Display for GuidQwordStringEventRecordData {
510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511 let progress_id = self.progress_id;
513 let apic_id = self.apic_id;
514 let timestamp = self.timestamp;
515 let guid = BinaryGuid::from_bytes(&self.guid);
516 let qword = self.qword;
517 write!(
518 f,
519 "progress_id: 0x{:04X}, apic_id: {}, timestamp: {}, guid: {}, qword: 0x{:016X}",
520 progress_id, apic_id, timestamp, guid, qword
521 )
522 }
523}
524
525pub trait PerformanceRecordDetails {
527 fn print_details(&self, record_number: usize);
529}
530
531impl PerformanceRecordDetails for GuidEventRecordData {
532 fn print_details(&self, record_number: usize) {
533 log::debug!(" Record #{}: {}", record_number, self);
534 }
535}
536
537impl PerformanceRecordDetails for DynamicStringEventRecordData {
538 fn print_details(&self, record_number: usize) {
539 log::debug!(" Record #{}: {}", record_number, self);
540 }
541}
542
543impl PerformanceRecordDetails for DualGuidStringEventRecordData {
544 fn print_details(&self, record_number: usize) {
545 log::debug!(" Record #{}: {}", record_number, self);
546 }
547}
548
549impl PerformanceRecordDetails for GuidQwordEventRecordData {
550 fn print_details(&self, record_number: usize) {
551 log::debug!(" Record #{}: {}", record_number, self);
552 }
553}
554
555impl PerformanceRecordDetails for GuidQwordStringEventRecordData {
556 fn print_details(&self, record_number: usize) {
557 log::debug!(" Record #{}: {}", record_number, self);
558 }
559}
560
561pub fn print_record_details(record_type: u16, record_number: usize, data: &[u8]) {
563 match record_type {
564 0x1010 => {
565 if data.len() >= core::mem::size_of::<GuidEventRecordData>() {
566 let record = unsafe { &*(data.as_ptr() as *const GuidEventRecordData) };
568 record.print_details(record_number);
569 }
570 }
571 0x1011 => {
572 if data.len() >= core::mem::size_of::<DynamicStringEventRecordData>() {
573 let record = unsafe { &*(data.as_ptr() as *const DynamicStringEventRecordData) };
575 record.print_details(record_number);
576 let string_data = DynamicStringEventRecordData::extract_string(data);
577 if !string_data.is_empty() {
578 log::debug!(" String: \"{}\"", string_data);
579 }
580 }
581 }
582 0x1012 => {
583 if data.len() >= core::mem::size_of::<DualGuidStringEventRecordData>() {
584 let record = unsafe { &*(data.as_ptr() as *const DualGuidStringEventRecordData) };
586 record.print_details(record_number);
587 let string_data = DualGuidStringEventRecordData::extract_string(data);
588 if !string_data.is_empty() {
589 log::debug!(" String: \"{}\"", string_data);
590 }
591 }
592 }
593 0x1013 => {
594 if data.len() >= core::mem::size_of::<GuidQwordEventRecordData>() {
595 let record = unsafe { &*(data.as_ptr() as *const GuidQwordEventRecordData) };
597 record.print_details(record_number);
598 }
599 }
600 0x1014 => {
601 if data.len() >= core::mem::size_of::<GuidQwordStringEventRecordData>() {
602 let record = unsafe { &*(data.as_ptr() as *const GuidQwordStringEventRecordData) };
604 record.print_details(record_number);
605 let string_data = GuidQwordStringEventRecordData::extract_string(data);
606 if !string_data.is_empty() {
607 log::debug!(" String: \"{}\"", string_data);
608 }
609 }
610 }
611 _ => {
612 log::debug!(" Record #{}: Unknown type 0x{:04X}", record_number, record_type);
613 }
614 }
615}
616
617pub fn record_type_name(record_type: u16) -> &'static str {
619 match record_type {
620 0x1010 => GuidEventRecordData::NAME,
621 0x1011 => DynamicStringEventRecordData::NAME,
622 0x1012 => DualGuidStringEventRecordData::NAME,
623 0x1013 => GuidQwordEventRecordData::NAME,
624 0x1014 => GuidQwordStringEventRecordData::NAME,
625 _ => "Unknown",
626 }
627}
628
629#[cfg(test)]
630#[coverage(off)]
631mod tests {
632 use super::*;
633 use core::{assert_eq, slice, unreachable};
634
635 use r_efi::efi;
636
637 use extended::{
638 DualGuidStringEventRecord, DynamicStringEventRecord, GuidEventRecord, GuidQwordEventRecord,
639 GuidQwordStringEventRecord,
640 };
641
642 #[test]
643 fn test_performance_record_buffer_new() {
644 let performance_record_buffer = PerformanceRecordBuffer::new();
645 println!("{performance_record_buffer:?}");
646 assert_eq!(0, performance_record_buffer.size());
647 }
648
649 #[test]
650 fn test_performance_record_buffer_push_record() {
651 let guid = efi::Guid::from_bytes(&[0; 16]);
652 let mut performance_record_buffer = PerformanceRecordBuffer::new();
653 let mut size = 0;
654
655 size += performance_record_buffer.push_record(GuidEventRecord::new(1, 0, 10, guid)).unwrap();
656 assert_eq!(size, performance_record_buffer.size());
657
658 size += performance_record_buffer.push_record(DynamicStringEventRecord::new(1, 0, 10, guid, "test")).unwrap();
659 assert_eq!(size, performance_record_buffer.size());
660
661 size += performance_record_buffer
662 .push_record(DualGuidStringEventRecord::new(1, 0, 10, guid, guid, "test"))
663 .unwrap();
664 assert_eq!(size, performance_record_buffer.size());
665
666 size += performance_record_buffer.push_record(GuidQwordEventRecord::new(1, 0, 10, guid, 64)).unwrap();
667 assert_eq!(size, performance_record_buffer.size());
668
669 size +=
670 performance_record_buffer.push_record(GuidQwordStringEventRecord::new(1, 0, 10, guid, 64, "test")).unwrap();
671 assert_eq!(size, performance_record_buffer.size());
672 }
673
674 #[test]
675 fn test_performance_record_buffer_iter() {
676 let guid = efi::Guid::from_bytes(&[0; 16]);
677 let mut performance_record_buffer = PerformanceRecordBuffer::new();
678
679 performance_record_buffer.push_record(GuidEventRecord::new(1, 0, 10, guid)).unwrap();
680 performance_record_buffer.push_record(DynamicStringEventRecord::new(1, 0, 10, guid, "test")).unwrap();
681 performance_record_buffer.push_record(DualGuidStringEventRecord::new(1, 0, 10, guid, guid, "test")).unwrap();
682 performance_record_buffer.push_record(GuidQwordEventRecord::new(1, 0, 10, guid, 64)).unwrap();
683 performance_record_buffer.push_record(GuidQwordStringEventRecord::new(1, 0, 10, guid, 64, "test")).unwrap();
684
685 for (i, record) in performance_record_buffer.iter().enumerate() {
686 match i {
687 _ if i == 0 => assert_eq!(
688 (GuidEventRecord::TYPE, GuidEventRecord::REVISION),
689 (record.record_type, record.revision)
690 ),
691 _ if i == 1 => assert_eq!(
692 (DynamicStringEventRecord::TYPE, DynamicStringEventRecord::REVISION),
693 (record.record_type, record.revision)
694 ),
695 _ if i == 2 => assert_eq!(
696 (DualGuidStringEventRecord::TYPE, DualGuidStringEventRecord::REVISION),
697 (record.record_type, record.revision)
698 ),
699 _ if i == 3 => assert_eq!(
700 (GuidQwordEventRecord::TYPE, GuidQwordEventRecord::REVISION),
701 (record.record_type, record.revision)
702 ),
703 _ if i == 4 => assert_eq!(
704 (GuidQwordStringEventRecord::TYPE, GuidQwordStringEventRecord::REVISION),
705 (record.record_type, record.revision)
706 ),
707 _ => unreachable!(),
708 }
709 }
710 }
711
712 #[test]
713 fn test_performance_record_buffer_reported_table() {
714 let guid = efi::Guid::from_bytes(&[0; 16]);
715 let mut performance_record_buffer = PerformanceRecordBuffer::new();
716
717 performance_record_buffer.push_record(GuidEventRecord::new(1, 0, 10, guid)).unwrap();
718 performance_record_buffer.push_record(DynamicStringEventRecord::new(1, 0, 10, guid, "test")).unwrap();
719
720 let mut buffer = vec![0_u8; 1000];
721 let buffer = unsafe { slice::from_raw_parts_mut(buffer.as_mut_ptr(), buffer.len()) };
723
724 performance_record_buffer.report(buffer).unwrap();
725
726 performance_record_buffer.push_record(DualGuidStringEventRecord::new(1, 0, 10, guid, guid, "test")).unwrap();
727 performance_record_buffer.push_record(GuidQwordEventRecord::new(1, 0, 10, guid, 64)).unwrap();
728 performance_record_buffer.push_record(GuidQwordStringEventRecord::new(1, 0, 10, guid, 64, "test")).unwrap();
729
730 for (i, record) in performance_record_buffer.iter().enumerate() {
731 match i {
732 _ if i == 0 => assert_eq!(
733 (GuidEventRecord::TYPE, GuidEventRecord::REVISION),
734 (record.record_type, record.revision)
735 ),
736 _ if i == 1 => assert_eq!(
737 (DynamicStringEventRecord::TYPE, DynamicStringEventRecord::REVISION),
738 (record.record_type, record.revision)
739 ),
740 _ if i == 2 => assert_eq!(
741 (DualGuidStringEventRecord::TYPE, DualGuidStringEventRecord::REVISION),
742 (record.record_type, record.revision)
743 ),
744 _ if i == 3 => assert_eq!(
745 (GuidQwordEventRecord::TYPE, GuidQwordEventRecord::REVISION),
746 (record.record_type, record.revision)
747 ),
748 _ if i == 4 => assert_eq!(
749 (GuidQwordStringEventRecord::TYPE, GuidQwordStringEventRecord::REVISION),
750 (record.record_type, record.revision)
751 ),
752 _ => unreachable!(),
753 }
754 }
755 }
756
757 #[test]
758 fn test_performance_record_header_try_from_valid_bytes() {
759 let original_header = PerformanceRecordHeader::new(0x1234, 42, 1);
760 let bytes: [u8; 4] = original_header.into();
761
762 let parsed_header = PerformanceRecordHeader::try_from(bytes.as_slice()).unwrap();
763
764 let parsed_type = parsed_header.record_type;
766 let parsed_length = parsed_header.length;
767 let parsed_revision = parsed_header.revision;
768
769 assert_eq!(parsed_type, 0x1234);
770 assert_eq!(parsed_length, 42);
771 assert_eq!(parsed_revision, 1);
772 }
773
774 #[test]
775 fn test_performance_record_header_try_from_insufficient_bytes() {
776 let bytes = [0x34, 0x12]; let result = PerformanceRecordHeader::try_from(bytes.as_slice());
778
779 assert!(result.is_err());
780 assert_eq!(result.unwrap_err(), "Insufficient bytes for PerformanceRecordHeader");
781 }
782
783 #[test]
784 fn test_performance_record_header_try_from_empty_bytes() {
785 let bytes: &[u8] = &[];
786 let result = PerformanceRecordHeader::try_from(bytes);
787
788 assert!(result.is_err());
789 assert_eq!(result.unwrap_err(), "Insufficient bytes for PerformanceRecordHeader");
790 }
791
792 #[test]
793 fn test_performance_record_header_from_trait_conversion_le() {
794 let header = PerformanceRecordHeader::new(0xABCD, 100, 2);
795 let bytes: [u8; mem::size_of::<PerformanceRecordHeader>()] = header.into();
796
797 assert_eq!(bytes[0], 0xCD); assert_eq!(bytes[1], 0xAB); assert_eq!(bytes[2], 100); assert_eq!(bytes[3], 2); }
803
804 #[test]
805 fn test_performance_record_header_roundtrip_conversion() {
806 let original_header = PerformanceRecordHeader::new(0x5678, 200, 3);
808
809 let bytes: [u8; mem::size_of::<PerformanceRecordHeader>()] = original_header.into();
810 let parsed_header = PerformanceRecordHeader::try_from(bytes.as_slice()).unwrap();
811
812 let orig_type = original_header.record_type;
813 let orig_length = original_header.length;
814 let orig_revision = original_header.revision;
815 let parsed_type = parsed_header.record_type;
816 let parsed_length = parsed_header.length;
817 let parsed_revision = parsed_header.revision;
818
819 assert_eq!(orig_type, parsed_type);
820 assert_eq!(orig_length, parsed_length);
821 assert_eq!(orig_revision, parsed_revision);
822 }
823
824 #[test]
825 fn test_performance_record_header_le_handling() {
826 let header = PerformanceRecordHeader::new(0x0102, 50, 1);
828 let bytes: [u8; mem::size_of::<PerformanceRecordHeader>()] = header.into();
829
830 assert_eq!(bytes[0], 0x02);
831 assert_eq!(bytes[1], 0x01);
832 assert_eq!(bytes[2], 50);
833 assert_eq!(bytes[3], 1);
834
835 let parsed = PerformanceRecordHeader::try_from(bytes.as_slice()).unwrap();
837
838 let parsed_type = parsed.record_type;
839 let parsed_length = parsed.length;
840 let parsed_revision = parsed.revision;
841
842 assert_eq!(parsed_type, 0x0102);
843 assert_eq!(parsed_length, 50);
844 assert_eq!(parsed_revision, 1);
845 }
846
847 #[test]
848 fn test_performance_record_header_try_from_extra_bytes() {
849 let mut bytes = vec![0x34, 0x12, 42, 1]; bytes.extend_from_slice(&[0xFF, 0xFF, 0xFF]); let parsed_header = PerformanceRecordHeader::try_from(bytes.as_slice()).unwrap();
854
855 let parsed_type = parsed_header.record_type;
856 let parsed_length = parsed_header.length;
857 let parsed_revision = parsed_header.revision;
858
859 assert_eq!(parsed_type, 0x1234);
860 assert_eq!(parsed_length, 42);
861 assert_eq!(parsed_revision, 1);
862 }
863
864 #[test]
865 fn test_guid_event_record_data_display() {
866 let record = GuidEventRecordData {
867 progress_id: 0x1234,
868 apic_id: 42,
869 timestamp: 1000000,
870 guid: [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10],
871 };
872
873 let display_str = format!("{}", record);
874 assert!(display_str.contains("progress_id=4660"));
875 assert!(display_str.contains("apic_id=42"));
876 assert!(display_str.contains("timestamp=1000000"));
877 }
878
879 #[test]
880 fn test_dynamic_string_event_record_data_display() {
881 let record = DynamicStringEventRecordData {
882 progress_id: 0x5678,
883 apic_id: 99,
884 timestamp: 2000000,
885 guid: [0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20],
886 };
887
888 let display_str = format!("{}", record);
889 assert!(display_str.contains("progress_id: 0x5678"));
890 assert!(display_str.contains("apic_id: 99"));
891 assert!(display_str.contains("timestamp: 2000000"));
892 }
893
894 #[test]
895 fn test_dynamic_string_event_record_data_extract_string() {
896 let mut data = vec![0u8; core::mem::size_of::<DynamicStringEventRecordData>()];
897 let test_string = b"Test String\0";
898 data.extend_from_slice(test_string);
899
900 let extracted = DynamicStringEventRecordData::extract_string(&data);
901 assert_eq!(extracted, "Test String");
902 }
903
904 #[test]
905 fn test_dynamic_string_event_record_data_extract_string_empty() {
906 let data = vec![0u8; core::mem::size_of::<DynamicStringEventRecordData>()];
907 let extracted = DynamicStringEventRecordData::extract_string(&data);
908 assert_eq!(extracted, "");
909 }
910
911 #[test]
912 fn test_dynamic_string_event_record_data_extract_string_invalid_utf8() {
913 let mut data = vec![0u8; core::mem::size_of::<DynamicStringEventRecordData>()];
914 data.extend_from_slice(&[0xFF, 0xFE, 0xFD, 0x00]); let extracted = DynamicStringEventRecordData::extract_string(&data);
917 assert_eq!(extracted, "<invalid UTF-8>");
918 }
919
920 #[test]
921 fn test_dynamic_string_event_record_data_extract_string_no_null_terminator() {
922 let mut data = vec![0u8; core::mem::size_of::<DynamicStringEventRecordData>()];
923 data.extend_from_slice(b"NoNull");
924
925 let extracted = DynamicStringEventRecordData::extract_string(&data);
926 assert_eq!(extracted, "NoNull");
927 }
928
929 #[test]
930 fn test_dual_guid_string_event_record_data_display() {
931 let record = DualGuidStringEventRecordData {
932 progress_id: 0xABCD,
933 apic_id: 123,
934 timestamp: 3000000,
935 guid_1: [0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30],
936 guid_2: [0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40],
937 };
938
939 let display_str = format!("{}", record);
940 assert!(display_str.contains("progress_id: 0xABCD"));
941 assert!(display_str.contains("apic_id: 123"));
942 assert!(display_str.contains("timestamp: 3000000"));
943 assert!(display_str.contains("guid_1:"));
944 assert!(display_str.contains("guid_2:"));
945 }
946
947 #[test]
948 fn test_dual_guid_string_event_record_data_extract_string() {
949 let mut data = vec![0u8; core::mem::size_of::<DualGuidStringEventRecordData>()];
950 let test_string = b"DualGuidTest\0";
951 data.extend_from_slice(test_string);
952
953 let extracted = DualGuidStringEventRecordData::extract_string(&data);
954 assert_eq!(extracted, "DualGuidTest");
955 }
956
957 #[test]
958 fn test_dual_guid_string_event_record_data_extract_string_empty() {
959 let data = vec![0u8; core::mem::size_of::<DualGuidStringEventRecordData>()];
960 let extracted = DualGuidStringEventRecordData::extract_string(&data);
961 assert_eq!(extracted, "");
962 }
963
964 #[test]
965 fn test_guid_qword_event_record_data_display() {
966 let record = GuidQwordEventRecordData {
967 progress_id: 0xEF01,
968 apic_id: 200,
969 timestamp: 4000000,
970 guid: [0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50],
971 qword: 0x123456789ABCDEF0,
972 };
973
974 let display_str = format!("{}", record);
975 assert!(display_str.contains("progress_id: 0xEF01"));
976 assert!(display_str.contains("apic_id: 200"));
977 assert!(display_str.contains("timestamp: 4000000"));
978 assert!(display_str.contains("qword: 0x123456789ABCDEF0"));
979 }
980
981 #[test]
982 fn test_guid_qword_string_event_record_data_display() {
983 let record = GuidQwordStringEventRecordData {
984 progress_id: 0x2345,
985 apic_id: 77,
986 timestamp: 5000000,
987 guid: [0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60],
988 qword: 0xFEDCBA9876543210,
989 };
990
991 let display_str = format!("{}", record);
992 assert!(display_str.contains("progress_id: 0x2345"));
993 assert!(display_str.contains("apic_id: 77"));
994 assert!(display_str.contains("timestamp: 5000000"));
995 assert!(display_str.contains("qword: 0xFEDCBA9876543210"));
996 }
997
998 #[test]
999 fn test_guid_qword_string_event_record_data_extract_string() {
1000 let mut data = vec![0u8; core::mem::size_of::<GuidQwordStringEventRecordData>()];
1001 let test_string = b"QwordString\0";
1002 data.extend_from_slice(test_string);
1003
1004 let extracted = GuidQwordStringEventRecordData::extract_string(&data);
1005 assert_eq!(extracted, "QwordString");
1006 }
1007
1008 #[test]
1009 fn test_guid_qword_string_event_record_data_extract_string_empty() {
1010 let data = vec![0u8; core::mem::size_of::<GuidQwordStringEventRecordData>()];
1011 let extracted = GuidQwordStringEventRecordData::extract_string(&data);
1012 assert_eq!(extracted, "");
1013 }
1014
1015 #[test]
1016 fn test_record_type_name_all_types() {
1017 assert_eq!(record_type_name(0x1010), GuidEventRecordData::NAME);
1018 assert_eq!(record_type_name(0x1011), DynamicStringEventRecordData::NAME);
1019 assert_eq!(record_type_name(0x1012), DualGuidStringEventRecordData::NAME);
1020 assert_eq!(record_type_name(0x1013), GuidQwordEventRecordData::NAME);
1021 assert_eq!(record_type_name(0x1014), GuidQwordStringEventRecordData::NAME);
1022 assert_eq!(record_type_name(0x9999), "Unknown");
1023 }
1024
1025 #[test]
1026 fn test_record_type_name_boundary_values() {
1027 assert_eq!(record_type_name(0x0000), "Unknown");
1028 assert_eq!(record_type_name(0xFFFF), "Unknown");
1029 assert_eq!(record_type_name(0x100F), "Unknown");
1030 assert_eq!(record_type_name(0x1015), "Unknown");
1031 }
1032
1033 #[test]
1034 fn test_guid_event_record_data_name_constant() {
1035 assert_eq!(GuidEventRecordData::NAME, "GUID Event");
1036 }
1037
1038 #[test]
1039 fn test_dynamic_string_event_record_data_name_constant() {
1040 assert_eq!(DynamicStringEventRecordData::NAME, "Dynamic String Event");
1041 }
1042
1043 #[test]
1044 fn test_dual_guid_string_event_record_data_name_constant() {
1045 assert_eq!(DualGuidStringEventRecordData::NAME, "Dual GUID String Event");
1046 }
1047
1048 #[test]
1049 fn test_guid_qword_event_record_data_name_constant() {
1050 assert_eq!(GuidQwordEventRecordData::NAME, "GUID QWORD Event");
1051 }
1052
1053 #[test]
1054 fn test_guid_qword_string_event_record_data_name_constant() {
1055 assert_eq!(GuidQwordStringEventRecordData::NAME, "GUID QWORD String Event");
1056 }
1057}