vyre_runtime/megakernel/protocol/codec/
debug_log.rs1use super::{debug, read_required_word, read_word_from_optional_words, validate_word_aligned};
2use super::{DebugRecord, ProtocolError};
3
4#[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
14pub 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
25pub 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
37pub 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}