Skip to main content

intel_crashlog/
header.rs

1// Copyright (C) 2025 Intel Corporation
2// SPDX-License-Identifier: MIT
3
4//! Data structures used in the Crash Log record headers.
5
6#[cfg(feature = "collateral_manager")]
7use crate::collateral::{CollateralManager, CollateralTree, ItemPath, PVSS};
8use crate::errata::{Errata, SERVER_LEGACY_PRODUCT_IDS};
9use crate::error::Error;
10use crate::node::Node;
11#[cfg(not(feature = "std"))]
12use alloc::{
13    fmt, format,
14    string::{String, ToString},
15    vec,
16    vec::Vec,
17};
18#[cfg(feature = "std")]
19use std::fmt;
20
21/// Lists all the Crash Log record types.
22///
23/// cbindgen:ignore
24pub mod record_types {
25    pub const PMC: u8 = 0x1;
26    pub const PMC_FW_TRACE: u8 = 0x2;
27    pub const PUNIT: u8 = 0x3;
28    pub const PCORE: u8 = 0x4;
29    pub const ECORE: u8 = 0x6;
30    pub const UNCORE: u8 = 0x8;
31    pub const PMC_TRACE: u8 = 0x11;
32    pub const TCSS: u8 = 0x16;
33    pub const PMC_RST: u8 = 0x17;
34    pub const PCODE: u8 = 0x19;
35    pub const CRASHLOG_AGENT: u8 = 0x1C;
36    pub const BOX: u8 = 0x3D;
37    pub const MCA: u8 = 0x3E;
38}
39
40#[derive(Debug, Default, Clone)]
41pub enum HeaderType {
42    Type0,
43    #[default]
44    Type1,
45    Type2 {
46        timestamp: u64,
47        agent_version: u32,
48        reason: u32,
49    },
50    Type3 {
51        timestamp: u64,
52        agent_version: u32,
53        reason: u32,
54        completion_status: u32,
55        collection_complete: bool,
56    },
57    Type4 {
58        timestamp: u64,
59        agent_version: u32,
60        reason: u32,
61        whoami: u32,
62        misc: u32,
63    },
64    Type5 {
65        timestamp: u64,
66        agent_version: u32,
67        reason: u32,
68        completion_status: u32,
69        collection_complete: bool,
70        error_status: u32,
71    },
72    Type6 {
73        timestamp: u64,
74        agent_version: u32,
75        reason: u32,
76        die_id: u8,
77        socket_id: u8,
78        completion_status_size: u16,
79        completion_status: Vec<u32>,
80        collection_complete: bool,
81    },
82
83    Type0LegacyServer {
84        timestamp: u64,
85        agent_version: u32,
86        reason: u32,
87        die_id: u8,
88        socket_id: u8,
89        completion_status: u32,
90        collection_complete: bool,
91    },
92}
93
94impl HeaderType {
95    fn type2_from_slice(slice: &[u8]) -> Option<Self> {
96        let timestamp = u64::from_le_bytes(slice.get(8..16)?.try_into().ok()?);
97        let agent_version = u32::from_le_bytes(slice.get(16..20)?.try_into().ok()?);
98        let reason = u32::from_le_bytes(slice.get(20..24)?.try_into().ok()?);
99
100        Some(HeaderType::Type2 {
101            timestamp,
102            agent_version,
103            reason,
104        })
105    }
106
107    fn type3_from_slice(slice: &[u8]) -> Option<Self> {
108        let timestamp = u64::from_le_bytes(slice.get(8..16)?.try_into().ok()?);
109        let agent_version = u32::from_le_bytes(slice.get(16..20)?.try_into().ok()?);
110        let reason = u32::from_le_bytes(slice.get(20..24)?.try_into().ok()?);
111        let cs_data = u32::from_le_bytes(slice.get(24..28)?.try_into().ok()?);
112        let completion_status = cs_data & 0x7FFFFFFF;
113        let collection_complete = (cs_data >> 31) != 0;
114
115        Some(HeaderType::Type3 {
116            timestamp,
117            agent_version,
118            reason,
119            completion_status,
120            collection_complete,
121        })
122    }
123
124    fn type4_from_slice(slice: &[u8]) -> Option<Self> {
125        let timestamp = u64::from_le_bytes(slice.get(8..16)?.try_into().ok()?);
126        let agent_version = u32::from_le_bytes(slice.get(16..20)?.try_into().ok()?);
127        let reason = u32::from_le_bytes(slice.get(20..24)?.try_into().ok()?);
128        let whoami = u32::from_le_bytes(slice.get(24..28)?.try_into().ok()?);
129        let misc = u32::from_le_bytes(slice.get(28..32)?.try_into().ok()?);
130
131        Some(HeaderType::Type4 {
132            timestamp,
133            agent_version,
134            reason,
135            whoami,
136            misc,
137        })
138    }
139
140    fn type5_from_slice(slice: &[u8]) -> Option<Self> {
141        let timestamp = u64::from_le_bytes(slice.get(8..16)?.try_into().ok()?);
142        let agent_version = u32::from_le_bytes(slice.get(16..20)?.try_into().ok()?);
143        let reason = u32::from_le_bytes(slice.get(20..24)?.try_into().ok()?);
144        let cs_data = u32::from_le_bytes(slice.get(24..28)?.try_into().ok()?);
145        let completion_status = cs_data & 0x7FFFFFFF;
146        let collection_complete = (cs_data >> 31) != 0;
147        let error_status = u32::from_le_bytes(slice.get(28..32)?.try_into().ok()?);
148
149        Some(HeaderType::Type5 {
150            timestamp,
151            agent_version,
152            reason,
153            completion_status,
154            collection_complete,
155            error_status,
156        })
157    }
158
159    fn type6_from_slice(slice: &[u8]) -> Option<Self> {
160        let timestamp = u64::from_le_bytes(slice.get(8..16)?.try_into().ok()?);
161        let agent_version = u32::from_le_bytes(slice.get(16..20)?.try_into().ok()?);
162        let reason = u32::from_le_bytes(slice.get(20..24)?.try_into().ok()?);
163        let die_skt_info = slice.get(24..28)?;
164
165        let die_id = die_skt_info[0];
166        let socket_id = die_skt_info[1];
167        let completion_status_size = u16::from_le_bytes(die_skt_info[2..4].try_into().ok()?) & 0x7F;
168        let collection_complete = (die_skt_info[3] & 0x80) != 0;
169
170        let completion_status = (0..completion_status_size)
171            .map(|dword| {
172                let index = (28 + dword * 4) as usize;
173                Some(u32::from_le_bytes(
174                    slice.get(index..index + 4)?.try_into().ok()?,
175                ))
176            })
177            .collect::<Option<Vec<u32>>>()?;
178
179        Some(HeaderType::Type6 {
180            timestamp,
181            agent_version,
182            reason,
183            die_id,
184            socket_id,
185            completion_status_size,
186            completion_status,
187            collection_complete,
188        })
189    }
190
191    fn type0_legacy_server_from_slice(slice: &[u8]) -> Option<Self> {
192        let reason = u32::from_le_bytes(slice.get(4..8)?.try_into().ok()?);
193        let timestamp = u64::from_le_bytes(slice.get(8..16)?.try_into().ok()?);
194        let agent_version = u32::from_le_bytes(slice.get(20..24)?.try_into().ok()?);
195        let socket_id = *slice.get(24)?;
196        let cs_data = u32::from_le_bytes(slice.get(28..32)?.try_into().ok()?);
197        let completion_status = cs_data & 0x7FFFFFFF;
198        let collection_complete = (cs_data >> 31) != 0;
199
200        // Encoded die_id
201        let revision = slice.first()?;
202        let die_idx = revision & 0x3;
203
204        let die_id = if ((revision >> 7) & 1) == 1 {
205            die_idx + 9
206        } else {
207            die_idx << 2
208        };
209
210        Some(HeaderType::Type0LegacyServer {
211            timestamp,
212            agent_version,
213            reason,
214            die_id,
215            socket_id,
216            completion_status,
217            collection_complete,
218        })
219    }
220
221    pub fn from_slice(header_type_value: u16, slice: &[u8]) -> Result<Self, Error> {
222        match header_type_value {
223            0 => Ok(HeaderType::Type0),
224            1 => Ok(HeaderType::Type1),
225            2 => Self::type2_from_slice(slice).ok_or(Error::InvalidHeader),
226            3 => Self::type3_from_slice(slice).ok_or(Error::InvalidHeader),
227            4 => Self::type4_from_slice(slice).ok_or(Error::InvalidHeader),
228            5 => Self::type5_from_slice(slice).ok_or(Error::InvalidHeader),
229            6 => Self::type6_from_slice(slice).ok_or(Error::InvalidHeader),
230            type_value => Err(Error::InvalidHeaderType(type_value)),
231        }
232    }
233
234    pub fn from_slice_type0_legacy_server(slice: &[u8]) -> Result<Self, Error> {
235        Self::type0_legacy_server_from_slice(slice).ok_or(Error::InvalidHeader)
236    }
237}
238
239/// Header of a Crash Log record
240#[derive(Debug, Default, Clone)]
241pub struct Header {
242    /// Version ID
243    pub version: Version,
244    /// Size of the record
245    pub size: RecordSize,
246    /// Optional fields
247    pub header_type: HeaderType,
248}
249
250impl Header {
251    /// Decodes a header of a raw Crash Log record.
252    pub fn from_slice(slice: &[u8]) -> Result<Option<Self>, Error> {
253        let Some(version) = Version::from_slice(slice) else {
254            // Termination marker
255            return Ok(None);
256        };
257        let errata = Errata::from_version(&version);
258
259        let size = if errata.type0_legacy_server {
260            RecordSize::from_slice_type0_legacy_server(slice).ok_or(Error::InvalidHeader)?
261        } else {
262            RecordSize::from_slice(slice).ok_or(Error::InvalidHeader)?
263        };
264
265        let header_type = if errata.type0_legacy_server {
266            HeaderType::from_slice_type0_legacy_server(slice)?
267        } else {
268            HeaderType::from_slice(version.header_type, slice)?
269        };
270
271        Ok(Some(Header {
272            version,
273            size,
274            header_type,
275        }))
276    }
277
278    /// Returns the granularity of the record size fields in bytes
279    #[inline]
280    fn record_size_granularity(&self) -> usize {
281        if self.version.into_errata().core_record_size_bytes {
282            return 1;
283        }
284        4
285    }
286
287    /// Returns the size of the record in bytes.
288    #[inline]
289    pub fn record_size(&self) -> usize {
290        (self.size.record_size as usize + self.size.extended_record_size as usize)
291            * self.record_size_granularity()
292    }
293
294    /// Returns the offset of the extended record in bytes if present.
295    #[inline]
296    pub fn extended_record_offset(&self) -> Option<usize> {
297        if self.size.extended_record_size > 0 {
298            Some(self.size.record_size as usize * self.record_size_granularity())
299        } else {
300            None
301        }
302    }
303
304    /// Returns the size of the record in bytes.
305    #[inline]
306    pub fn revision(&self) -> u32 {
307        self.version.revision
308    }
309
310    /// Returns the product ID of the record.
311    #[inline]
312    pub fn product_id(&self) -> u32 {
313        self.version.product_id
314    }
315
316    /// Returns the Three-Letter Acronym associated to the product ID specified in the header.
317    #[cfg(feature = "collateral_manager")]
318    pub fn product<'a, T: CollateralTree>(
319        &self,
320        cm: &'a CollateralManager<T>,
321    ) -> Result<&'a str, Error> {
322        cm.target_info
323            .get(&self.product_id())
324            .map(|target_info| target_info.product.as_str())
325            .ok_or_else(|| Error::InvalidProductID(self.product_id()))
326    }
327
328    /// Returns the product variant associated to the product ID specified in the header.
329    #[cfg(feature = "collateral_manager")]
330    pub fn variant<'a, T: CollateralTree>(&self, cm: &'a CollateralManager<T>) -> Option<&'a str> {
331        cm.target_info
332            .get(&self.product_id())
333            .map(|target_info| target_info.variant.as_str())
334    }
335
336    /// Returns the ID of the socket that generated the record.
337    pub fn socket_id(&self) -> u8 {
338        match self.header_type {
339            HeaderType::Type6 { socket_id, .. } => socket_id,
340            HeaderType::Type0LegacyServer { socket_id, .. } => socket_id,
341            _ => 0,
342        }
343    }
344
345    /// Returns the ID of the die that generated the record.
346    pub fn die_id(&self) -> Option<u8> {
347        match self.header_type {
348            HeaderType::Type6 { die_id, .. } => Some(die_id),
349            HeaderType::Type0LegacyServer { die_id, .. } => Some(die_id),
350            _ => None,
351        }
352    }
353
354    /// Returns the name of the die that generated the record.
355    ///
356    /// This requires a [CollateralManager] as the die names are product-specific.
357    #[cfg(feature = "collateral_manager")]
358    pub fn die<'a, T: CollateralTree>(&self, cm: &'a CollateralManager<T>) -> Option<&'a str> {
359        self.get_die_name(&self.die_id()?, cm)
360    }
361
362    #[cfg(feature = "collateral_manager")]
363    pub(crate) fn get_die_name<'a, T: CollateralTree>(
364        &self,
365        die_id: &u8,
366        cm: &'a CollateralManager<T>,
367    ) -> Option<&'a str> {
368        let target_info = cm.target_info.get(&self.product_id())?;
369        let die_name = target_info.die_id.get(die_id)?;
370        Some(die_name)
371    }
372
373    /// Returns the type of the record.
374    pub fn record_type(&self) -> Result<&'static str, Error> {
375        self.version.record_type_as_str()
376    }
377
378    #[cfg(feature = "collateral_manager")]
379    pub(super) fn decode_definitions_paths<T: CollateralTree>(
380        &self,
381        cm: &CollateralManager<T>,
382    ) -> Result<Vec<ItemPath>, Error> {
383        let record_type = self.record_type()?;
384        let revision = self.revision().to_string();
385
386        Ok(if let Some(die) = self.die(cm) {
387            let die_id = die.trim_end_matches(char::is_numeric);
388            vec![ItemPath::new([
389                "decode-defs",
390                record_type,
391                die_id,
392                &revision,
393            ])]
394        } else {
395            vec![
396                ItemPath::new(["decode-defs", record_type, &revision]),
397                ItemPath::new(["decode-defs", record_type, "all"]),
398            ]
399        })
400    }
401
402    #[cfg(feature = "collateral_manager")]
403    /// Returns the [PVSS] associated to this header.
404    pub fn pvss<T: CollateralTree>(&self, cm: &CollateralManager<T>) -> Result<PVSS, Error> {
405        let product = match self.product(cm) {
406            Err(Error::InvalidProductID(0)) => "all",
407            res => res?,
408        };
409        let variant = self.variant(cm).unwrap_or("all");
410
411        Ok(PVSS {
412            product: product.into(),
413            variant: variant.into(),
414            ..PVSS::default()
415        })
416    }
417
418    /// Returns the size of the header in bytes.
419    pub fn header_size(&self) -> usize {
420        match self.header_type {
421            HeaderType::Type0 | HeaderType::Type1 => 8,
422            HeaderType::Type2 { .. } => 24,
423            HeaderType::Type3 { .. } => 28,
424            HeaderType::Type4 { .. } => 32,
425            HeaderType::Type5 { .. } => 32,
426            HeaderType::Type6 {
427                completion_status_size,
428                ..
429            } => 28 + completion_status_size as usize * 4,
430            HeaderType::Type0LegacyServer { .. } => 32,
431        }
432    }
433
434    #[cfg(feature = "collateral_manager")]
435    pub(super) fn get_root_path_using_cm<T: CollateralTree>(
436        &self,
437        cm: &CollateralManager<T>,
438    ) -> Option<String> {
439        if let HeaderType::Type6 { socket_id, .. } | HeaderType::Type0LegacyServer { socket_id, .. } =
440            self.header_type
441            && let Some(die) = self.die(cm)
442        {
443            return Some(format!("processors.cpu{socket_id}.{die}"));
444        }
445
446        None
447    }
448
449    pub(super) fn get_root_path(&self) -> Option<String> {
450        if let HeaderType::Type6 {
451            socket_id, die_id, ..
452        }
453        | HeaderType::Type0LegacyServer {
454            socket_id, die_id, ..
455        } = self.header_type
456        {
457            return Some(format!("processors.cpu{socket_id}.die{die_id}"));
458        }
459
460        None
461    }
462}
463
464impl fmt::Display for Header {
465    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
466        let record_type = self.record_type().unwrap_or("RECORD");
467        let version = format!(
468            "product_id=0x{0:x}, record_type=0x{1:x}, revision=0x{2:x}",
469            self.version.product_id, self.version.record_type, self.version.revision
470        );
471        let header_type = match self.header_type {
472            HeaderType::Type6 {
473                socket_id, die_id, ..
474            }
475            | HeaderType::Type0LegacyServer {
476                socket_id, die_id, ..
477            } => {
478                format!("die_id={die_id}, socket_id={socket_id}")
479            }
480            _ => "..".to_string(),
481        };
482
483        write!(f, "{record_type} - ({version}, {header_type})")
484    }
485}
486
487/// Version of the Crash Log record
488#[derive(Clone, Debug, Default)]
489pub struct Version {
490    /// Revision of the record
491    pub revision: u32,
492    /// Type of the header
493    pub header_type: u16,
494    /// Product that generated the record
495    pub product_id: u32,
496    /// Type of the record
497    pub record_type: u8,
498    /// Indicates if the record has been consumed by IAFW
499    pub consumed: bool,
500    /// Integrity checker present
501    pub cldic: bool,
502}
503
504impl Version {
505    /// Creates a [Version] from the raw record
506    pub fn from_slice(slice: &[u8]) -> Option<Self> {
507        let version = u32::from_le_bytes(slice.get(0..4)?.try_into().ok()?);
508        log::trace!("Decoding record version: {:#x}", version);
509
510        if version == 0 || version == 0xdeadbeef {
511            // Termination marker
512            return None;
513        }
514
515        Some(Version {
516            cldic: (version >> 30) & 1 == 1,
517            consumed: (version >> 31) & 1 == 1,
518            revision: version & 0xFF,
519            header_type: ((version >> 8) & 0xF) as u16,
520            product_id: (version >> 12) & 0xFFF,
521            record_type: ((version >> 24) & 0x3F) as u8,
522        })
523    }
524
525    pub fn as_u32(&self) -> u32 {
526        ((self.consumed as u32) << 31)
527            | ((self.cldic as u32) << 30)
528            | ((self.record_type as u32) << 24)
529            | (self.product_id << 12)
530            | ((self.header_type as u32) << 8)
531            | self.revision
532    }
533
534    fn record_type_as_str(&self) -> Result<&'static str, Error> {
535        Ok(match self.record_type {
536            record_types::PMC => "PMC",
537            record_types::PMC_FW_TRACE => "PMC_FW_Trace",
538            record_types::PUNIT => "Punit",
539            record_types::PCORE => "PCORE",
540            record_types::ECORE => "ECORE",
541            record_types::UNCORE => "UNCORE",
542            record_types::PMC_TRACE => "PMC_TRACE",
543            record_types::TCSS => "TCSS",
544            record_types::PMC_RST => "PMC_RST",
545            record_types::PCODE => "PCODE",
546            record_types::CRASHLOG_AGENT => "CRASHLOG_AGENT",
547            record_types::BOX => "BOX",
548            record_types::MCA => "MCA",
549            rt => return Err(Error::InvalidRecordType(rt)),
550        })
551    }
552
553    pub fn into_errata(&self) -> Errata {
554        let type0_legacy_server =
555            self.header_type == 0 && SERVER_LEGACY_PRODUCT_IDS.contains(&self.product_id);
556        let type0_legacy_server_box = type0_legacy_server && self.record_type == 0x4;
557        let core_record_size_bytes = !type0_legacy_server
558            && ((self.record_type == record_types::ECORE && self.product_id < 0x96)
559                || (self.record_type == record_types::PCORE && self.product_id < 0x71));
560
561        Errata {
562            type0_legacy_server,
563            type0_legacy_server_box,
564            core_record_size_bytes,
565        }
566    }
567}
568
569impl fmt::Display for Version {
570    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
571        let record_type = self.record_type_as_str().unwrap_or("UNKNOWN");
572        write!(f, "{} revision {}", record_type, self.revision)
573    }
574}
575
576/// Size of the Crash Log record
577#[derive(Debug, Default, Clone)]
578pub struct RecordSize {
579    /// Size of the main section of the record in dwords
580    pub record_size: u16,
581    /// Size of the extended section of the record in dwords
582    pub extended_record_size: u16,
583}
584
585impl RecordSize {
586    /// Creates a [RecordSize] from the raw record
587    pub fn from_slice(slice: &[u8]) -> Option<Self> {
588        Some(RecordSize {
589            record_size: u16::from_le_bytes(slice.get(4..6)?.try_into().ok()?),
590            extended_record_size: u16::from_le_bytes(slice.get(6..8)?.try_into().ok()?),
591        })
592    }
593
594    /// Creates a [RecordSize] from the raw record of a server product with legacy header type0
595    pub fn from_slice_type0_legacy_server(slice: &[u8]) -> Option<Self> {
596        Some(RecordSize {
597            record_size: u16::from_le_bytes(slice.get(16..18)?.try_into().ok()?),
598            extended_record_size: 0,
599        })
600    }
601}
602
603impl From<&RecordSize> for Node {
604    fn from(size: &RecordSize) -> Self {
605        let mut node = Node::section("record_size");
606        node.add(Node::field("record_size", size.record_size as u64));
607        node.add(Node::field(
608            "extended_record_size",
609            size.extended_record_size as u64,
610        ));
611        node
612    }
613}
614
615impl From<&Version> for Node {
616    fn from(version: &Version) -> Self {
617        let mut node = Node::field("version", version.as_u32() as u64);
618        node.add(Node::field("revision", version.revision as u64));
619        node.add(Node::field("header_type", version.header_type as u64));
620        node.add(Node::field("product_id", version.product_id as u64));
621        node.add(Node::field("record_type", version.record_type as u64));
622        node
623    }
624}
625
626impl From<&Header> for Node {
627    fn from(header: &Header) -> Self {
628        let mut node = Node::section("hdr");
629        node.add(Node::from(&header.version));
630        node.add(Node::from(&header.size));
631
632        match header.header_type {
633            HeaderType::Type2 {
634                timestamp,
635                agent_version,
636                reason,
637            } => {
638                node.add(Node::field("timestamp", timestamp));
639                node.add(Node::field("agent_version", agent_version as u64));
640                node.add(Node::field("reason", reason as u64));
641            }
642            HeaderType::Type3 {
643                timestamp,
644                agent_version,
645                reason,
646                completion_status,
647                collection_complete,
648            } => {
649                node.add(Node::field("timestamp", timestamp));
650                node.add(Node::field("agent_version", agent_version as u64));
651                node.add(Node::field("reason", reason as u64));
652
653                let mut completion_status_node = Node::section("completion_status");
654                completion_status_node
655                    .add(Node::field("completion_status", completion_status as u64));
656                completion_status_node.add(Node::field(
657                    "record_collection_completed",
658                    collection_complete as u64,
659                ));
660                node.add(completion_status_node);
661            }
662            HeaderType::Type4 {
663                timestamp,
664                agent_version,
665                reason,
666                whoami,
667                misc,
668            } => {
669                node.add(Node::field("timestamp", timestamp));
670                node.add(Node::field("agent_version", agent_version as u64));
671                node.add(Node::field("reason", reason as u64));
672                node.add(Node::field("whoami", whoami as u64));
673                node.add(Node::field("misc", misc as u64));
674            }
675            HeaderType::Type5 {
676                timestamp,
677                agent_version,
678                reason,
679                completion_status,
680                collection_complete,
681                error_status,
682            } => {
683                node.add(Node::field("timestamp", timestamp));
684                node.add(Node::field("agent_version", agent_version as u64));
685                node.add(Node::field("reason", reason as u64));
686                node.add(Node::field("error_status", error_status as u64));
687
688                let mut completion_status_node = Node::section("completion_status");
689                completion_status_node
690                    .add(Node::field("completion_status", completion_status as u64));
691                completion_status_node.add(Node::field(
692                    "record_collection_completed",
693                    collection_complete as u64,
694                ));
695                node.add(completion_status_node);
696            }
697            HeaderType::Type6 {
698                timestamp,
699                agent_version,
700                reason,
701                die_id,
702                socket_id,
703                ref completion_status,
704                completion_status_size,
705                collection_complete,
706            } => {
707                node.add(Node::field("timestamp", timestamp));
708                node.add(Node::field("agent_version", agent_version as u64));
709                node.add(Node::field("reason", reason as u64));
710                let mut die_skt_info = Node::section("die_skt_info");
711                die_skt_info.add(Node::field("die_id", die_id as u64));
712                die_skt_info.add(Node::field("socket_id", socket_id as u64));
713                die_skt_info.add(Node::field(
714                    "completion_status_size",
715                    completion_status_size as u64,
716                ));
717                die_skt_info.add(Node::field(
718                    "record_collection_completed",
719                    collection_complete as u64,
720                ));
721                node.add(die_skt_info);
722
723                for (i, completion_status) in completion_status.iter().enumerate() {
724                    node.add(Node::field(
725                        &format!("completion_status{i}"),
726                        *completion_status as u64,
727                    ));
728                }
729            }
730            HeaderType::Type0LegacyServer {
731                timestamp,
732                agent_version,
733                reason,
734                die_id,
735                socket_id,
736                completion_status,
737                collection_complete,
738            } => {
739                node.add(Node::field("timestamp", timestamp));
740                node.add(Node::field("agent_version", agent_version as u64));
741                node.add(Node::field("reason", reason as u64));
742                node.add(Node::field("die_id", die_id as u64));
743                node.add(Node::field("socket_id", socket_id as u64));
744                node.add(Node::field("completion_status", completion_status as u64));
745                node.add(Node::field(
746                    "record_collection_completed",
747                    collection_complete as u64,
748                ));
749            }
750            _ => (),
751        }
752
753        node
754    }
755}