Skip to main content

vyre_runtime/megakernel/protocol/codec/
debug_log.rs

1use super::{debug, read_required_word, read_word_from_optional_words, validate_word_aligned};
2use super::{DebugRecord, ProtocolError};
3
4/// Decode PRINTF records out of the debug-log buffer.
5#[must_use]
6pub fn read_debug_log(debug_bytes: &[u8]) -> Vec<DebugRecord> {
7    try_read_debug_log(debug_bytes).unwrap_or_else(|source| {
8        panic!(
9            "megakernel debug-log decode failed: {source}. Fix: use a complete debug-log readback produced by the matching megakernel protocol encoder."
10        )
11    })
12}
13
14/// Decode PRINTF records into caller-owned storage.
15///
16/// Clears `out`, then reuses its allocation.
17pub fn read_debug_log_into(debug_bytes: &[u8], out: &mut Vec<DebugRecord>) {
18    try_read_debug_log_into(debug_bytes, out).unwrap_or_else(|source| {
19        panic!(
20            "megakernel debug-log decode failed: {source}. Fix: use a complete debug-log readback produced by the matching megakernel protocol encoder."
21        )
22    });
23}
24
25/// Strictly decode PRINTF records out of the debug-log buffer.
26///
27/// # Errors
28///
29/// Returns [`ProtocolError`] when the buffer is not word-aligned, too short for
30/// the cursor word, or the cursor points at a partial record.
31pub fn try_read_debug_log(debug_bytes: &[u8]) -> Result<Vec<DebugRecord>, ProtocolError> {
32    let mut records = Vec::new();
33    try_read_debug_log_into(debug_bytes, &mut records)?;
34    Ok(records)
35}
36
37/// Strictly decode PRINTF records into caller-owned storage.
38///
39/// Clears `out`, then reuses its allocation.
40///
41/// # Errors
42///
43/// Returns [`ProtocolError`] when the buffer is not word-aligned, too short for
44/// the cursor word, or the cursor points at a partial record.
45pub fn try_read_debug_log_into(
46    debug_bytes: &[u8],
47    out: &mut Vec<DebugRecord>,
48) -> Result<(), ProtocolError> {
49    validate_word_aligned("debug_log", debug_bytes)?;
50    let cursor = read_required_word(
51        "debug_log",
52        debug_bytes,
53        debug_word_index(debug::CURSOR_WORD, "cursor word")?,
54    )?;
55    let record_words = debug_word_index(debug::RECORD_WORDS, "record word count")?;
56    let records_start = debug_word_index(debug::RECORDS_BASE, "records base word")?;
57    let total_word_capacity = debug_bytes.len() / 4;
58    if total_word_capacity < records_start {
59        return Err(ProtocolError::MissingWord {
60            buffer: "debug_log",
61            word_idx: records_start,
62            byte_len: debug_bytes.len(),
63            fix: "build debug-log bytes with encode_empty_debug_log",
64        });
65    }
66    let capacity_words = total_word_capacity - records_start;
67    let cursor = usize::try_from(cursor).map_err(|_| ProtocolError::ByteLengthOverflow {
68        buffer: "debug_log",
69        fix: "debug-log cursor does not fit host usize; keep protocol buffers within host addressable range",
70    })?;
71    if cursor > capacity_words {
72        return Err(ProtocolError::MissingWord {
73            buffer: "debug_log",
74            word_idx: records_start + cursor,
75            byte_len: debug_bytes.len(),
76            fix: "debug-log cursor must stay within the encoded record capacity",
77        });
78    }
79    let available = cursor;
80    if available % record_words != 0 {
81        return Err(ProtocolError::MissingWord {
82            buffer: "debug_log",
83            word_idx: records_start + available,
84            byte_len: debug_bytes.len(),
85            fix: "debug-log cursor must advance in whole PRINTF records",
86        });
87    }
88    let record_count = available / record_words;
89    out.clear();
90    try_reserve_record_capacity(out, record_count)?;
91    let words = bytemuck::try_cast_slice::<u8, u32>(debug_bytes).ok();
92    for i in 0..record_count {
93        let w = records_start + i * record_words;
94        out.push(DebugRecord {
95            fmt_id: read_word_from_optional_words(words, debug_bytes, w).ok_or(
96                ProtocolError::MissingWord {
97                buffer: "debug_log",
98                word_idx: w,
99                byte_len: debug_bytes.len(),
100                fix: "decode only debug-log buffers produced by the matching megakernel protocol encoder",
101            })?,
102            args: [
103                read_word_from_optional_words(words, debug_bytes, w + 1).ok_or(
104                    ProtocolError::MissingWord {
105                    buffer: "debug_log",
106                    word_idx: w + 1,
107                    byte_len: debug_bytes.len(),
108                    fix: "decode only debug-log buffers produced by the matching megakernel protocol encoder",
109                })?,
110                read_word_from_optional_words(words, debug_bytes, w + 2).ok_or(
111                    ProtocolError::MissingWord {
112                    buffer: "debug_log",
113                    word_idx: w + 2,
114                    byte_len: debug_bytes.len(),
115                    fix: "decode only debug-log buffers produced by the matching megakernel protocol encoder",
116                })?,
117                read_word_from_optional_words(words, debug_bytes, w + 3).ok_or(
118                    ProtocolError::MissingWord {
119                    buffer: "debug_log",
120                    word_idx: w + 3,
121                    byte_len: debug_bytes.len(),
122                    fix: "decode only debug-log buffers produced by the matching megakernel protocol encoder",
123                })?,
124            ],
125        });
126    }
127    Ok(())
128}
129
130fn debug_word_index(word: u32, label: &'static str) -> Result<usize, ProtocolError> {
131    usize::try_from(word).map_err(|_| ProtocolError::ByteLengthOverflow {
132        buffer: "debug_log",
133        fix: match label {
134            "cursor word" => "debug-log cursor word cannot fit host usize",
135            "record word count" => "debug-log record word count cannot fit host usize",
136            "records base word" => "debug-log records base word cannot fit host usize",
137            _ => "debug-log word index cannot fit host usize",
138        },
139    })
140}
141
142fn try_reserve_record_capacity(
143    out: &mut Vec<DebugRecord>,
144    target_capacity: usize,
145) -> Result<(), ProtocolError> {
146    vyre_foundation::allocation::try_reserve_vec_to_capacity(out, target_capacity).map_err(|_| {
147        ProtocolError::ByteLengthOverflow {
148            buffer: "debug_log",
149            fix: "host debug-log decode could not reserve output records; reduce debug-log capacity or decode into a reused scratch vector",
150        }
151    })
152}