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