Skip to content

Commit d59bd37

Browse files
authored
Fix/improve defmt parser around edge cases (#496)
* Slightly refactor defmt framing * Add tests, fix issues around raw data and partial frames * Add to changelog
1 parent 6bd7379 commit d59bd37

File tree

2 files changed

+216
-68
lines changed

2 files changed

+216
-68
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
### Fixed
1717

18+
- Fixed printing panic backtraces when using `esp-println` and `defmt` (#496)
19+
1820
### Changed
1921

2022
### Removed

espflash/src/cli/monitor/parser/esp_defmt.rs

Lines changed: 214 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -8,71 +8,78 @@ use defmt_decoder::{Frame, Table};
88

99
use crate::cli::monitor::parser::InputParser;
1010

11+
#[derive(Debug, PartialEq)]
1112
enum FrameKind<'a> {
12-
Defmt(Frame<'a>),
13+
Defmt(&'a [u8]),
1314
Raw(&'a [u8]),
1415
}
1516

1617
struct FrameDelimiter {
1718
buffer: Vec<u8>,
18-
table: Option<Table>,
1919
in_frame: bool,
2020
}
2121

2222
// Framing info added by esp-println
2323
const FRAME_START: &[u8] = &[0xFF, 0x00];
2424
const FRAME_END: &[u8] = &[0x00];
2525

26-
fn search(haystack: &[u8], look_for_end: bool) -> Option<(&[u8], usize)> {
27-
let needle = if look_for_end { FRAME_END } else { FRAME_START };
28-
let start = if look_for_end {
29-
// skip leading zeros
30-
haystack.iter().position(|&b| b != 0)?
31-
} else {
32-
0
33-
};
34-
35-
let end = haystack[start..]
36-
.windows(needle.len())
37-
.position(|window| window == needle)?;
38-
39-
Some((&haystack[start..][..end], start + end + needle.len()))
40-
}
41-
4226
impl FrameDelimiter {
43-
pub fn feed(&mut self, buffer: &[u8], mut process: impl FnMut(FrameKind<'_>)) {
44-
let Some(table) = self.table.as_mut() else {
45-
process(FrameKind::Raw(buffer));
46-
return;
27+
fn new() -> Self {
28+
Self {
29+
buffer: Vec::new(),
30+
in_frame: false,
31+
}
32+
}
33+
34+
fn search(haystack: &[u8], look_for_end: bool) -> Option<(&[u8], usize)> {
35+
let needle = if look_for_end { FRAME_END } else { FRAME_START };
36+
let start = if look_for_end {
37+
// skip leading zeros
38+
haystack.iter().position(|&b| b != 0)?
39+
} else {
40+
0
4741
};
4842

49-
let mut decoder = table.new_stream_decoder();
43+
let end = haystack[start..]
44+
.windows(needle.len())
45+
.position(|window| window == needle)?;
46+
47+
Some((&haystack[start..][..end], start + end + needle.len()))
48+
}
5049

50+
pub fn feed(&mut self, buffer: &[u8], mut process: impl FnMut(FrameKind<'_>)) {
5151
self.buffer.extend_from_slice(buffer);
5252

53-
while let Some((frame, consumed)) = search(&self.buffer, self.in_frame) {
54-
if !self.in_frame {
53+
while let Some((frame, consumed)) = Self::search(&self.buffer, self.in_frame) {
54+
if self.in_frame {
55+
process(FrameKind::Defmt(frame));
56+
} else if !frame.is_empty() {
5557
process(FrameKind::Raw(frame));
56-
self.in_frame = true;
58+
}
59+
self.in_frame = !self.in_frame;
60+
61+
self.buffer.drain(..consumed);
62+
}
63+
64+
if !self.in_frame {
65+
// If we have a 0xFF byte at the end, we should assume it's the start of a new frame.
66+
let consume = if self.buffer.ends_with(&[0xFF]) {
67+
&self.buffer[..self.buffer.len() - 1]
5768
} else {
58-
decoder.received(frame);
59-
// small reliance on rzcobs internals: we need to feed the terminating zero
60-
decoder.received(FRAME_END);
61-
if let Ok(frame) = decoder.decode() {
62-
process(FrameKind::Defmt(frame));
63-
} else {
64-
log::warn!("Failed to decode defmt frame");
65-
}
66-
self.in_frame = false;
69+
self.buffer.as_slice()
6770
};
6871

69-
self.buffer.drain(..consumed);
72+
if !consume.is_empty() {
73+
process(FrameKind::Raw(consume));
74+
self.buffer.drain(..consume.len());
75+
}
7076
}
7177
}
7278
}
7379

7480
pub struct EspDefmt {
7581
delimiter: FrameDelimiter,
82+
table: Option<Table>,
7683
}
7784

7885
impl EspDefmt {
@@ -94,48 +101,187 @@ impl EspDefmt {
94101

95102
pub fn new(elf: Option<&[u8]>) -> Self {
96103
Self {
97-
delimiter: FrameDelimiter {
98-
buffer: Vec::new(),
99-
table: Self::load_table(elf),
100-
in_frame: false,
101-
},
104+
delimiter: FrameDelimiter::new(),
105+
table: Self::load_table(elf),
102106
}
103107
}
108+
109+
fn handle_raw(bytes: &[u8], out: &mut impl Write) {
110+
out.write_all(bytes).unwrap();
111+
}
112+
113+
fn handle_defmt(frame: Frame<'_>, out: &mut impl Write) {
114+
match frame.level() {
115+
Some(level) => {
116+
let color = match level {
117+
defmt_parser::Level::Trace => Color::Cyan,
118+
defmt_parser::Level::Debug => Color::Blue,
119+
defmt_parser::Level::Info => Color::Green,
120+
defmt_parser::Level::Warn => Color::Yellow,
121+
defmt_parser::Level::Error => Color::Red,
122+
};
123+
124+
// Print the level before each line.
125+
let level = level.as_str().to_uppercase();
126+
for line in frame.display_message().to_string().lines() {
127+
out.queue(PrintStyledContent(
128+
format!("[{level}] - {line}\r\n").with(color),
129+
))
130+
.unwrap();
131+
}
132+
}
133+
None => {
134+
out.queue(Print(frame.display_message().to_string()))
135+
.unwrap();
136+
out.queue(Print("\r\n")).unwrap();
137+
}
138+
}
139+
140+
out.flush().unwrap();
141+
}
104142
}
105143

106144
impl InputParser for EspDefmt {
107145
fn feed(&mut self, bytes: &[u8], out: &mut impl Write) {
146+
let Some(table) = self.table.as_mut() else {
147+
Self::handle_raw(bytes, out);
148+
return;
149+
};
150+
151+
let mut decoder = table.new_stream_decoder();
152+
108153
self.delimiter.feed(bytes, |frame| match frame {
109154
FrameKind::Defmt(frame) => {
110-
match frame.level() {
111-
Some(level) => {
112-
let color = match level {
113-
defmt_parser::Level::Trace => Color::Cyan,
114-
defmt_parser::Level::Debug => Color::Blue,
115-
defmt_parser::Level::Info => Color::Green,
116-
defmt_parser::Level::Warn => Color::Yellow,
117-
defmt_parser::Level::Error => Color::Red,
118-
};
119-
120-
// Print the level before each line.
121-
let level = level.as_str().to_uppercase();
122-
for line in frame.display_message().to_string().lines() {
123-
out.queue(PrintStyledContent(
124-
format!("[{level}] - {line}\r\n").with(color),
125-
))
126-
.unwrap();
127-
}
128-
}
129-
None => {
130-
out.queue(Print(frame.display_message().to_string()))
131-
.unwrap();
132-
out.queue(Print("\r\n")).unwrap();
133-
}
134-
};
155+
decoder.received(frame);
156+
// small reliance on rzcobs internals: we need to feed the terminating zero
157+
decoder.received(FRAME_END);
158+
159+
if let Ok(frame) = decoder.decode() {
160+
Self::handle_defmt(frame, out);
161+
} else {
162+
log::warn!("Failed to decode defmt frame");
163+
}
164+
}
165+
FrameKind::Raw(bytes) => Self::handle_raw(bytes, out),
166+
});
167+
}
168+
}
169+
170+
#[cfg(test)]
171+
mod test {
172+
use super::*;
173+
174+
#[test]
175+
fn framing_prints_raw_data_by_default() {
176+
let mut parser = FrameDelimiter::new();
177+
178+
let mut asserted = 0;
179+
parser.feed(b"hello", |frame| {
180+
assert_eq!(frame, FrameKind::Raw(b"hello"));
181+
asserted += 1;
182+
});
183+
assert_eq!(asserted, 1);
184+
}
185+
186+
#[test]
187+
fn start_byte_on_end_is_not_part_of_the_raw_sequence() {
188+
let mut parser = FrameDelimiter::new();
189+
190+
let mut asserted = 0;
191+
parser.feed(b"hello\xFF", |frame| {
192+
assert_eq!(frame, FrameKind::Raw(b"hello"));
193+
asserted += 1;
194+
});
195+
assert_eq!(asserted, 1);
196+
}
197+
198+
#[test]
199+
fn frame_start_on_end_is_not_part_of_the_raw_sequence() {
200+
let mut parser = FrameDelimiter::new();
201+
202+
let mut asserted = 0;
203+
parser.feed(b"hello\xFF\x00", |frame| {
204+
assert_eq!(frame, FrameKind::Raw(b"hello"));
205+
asserted += 1;
206+
});
207+
assert_eq!(asserted, 1);
208+
}
209+
210+
#[test]
211+
fn process_data_after_frame() {
212+
let mut parser = FrameDelimiter::new();
135213

136-
out.flush().unwrap();
214+
let mut asserted = 0;
215+
parser.feed(b"\xFF\x00frame data\x00hello", |frame| {
216+
match asserted {
217+
0 => assert_eq!(frame, FrameKind::Defmt(b"frame data")),
218+
1 => assert_eq!(frame, FrameKind::Raw(b"hello")),
219+
_ => panic!("Too many frames"),
137220
}
138-
FrameKind::Raw(bytes) => out.write_all(bytes).unwrap(),
221+
asserted += 1;
222+
});
223+
assert_eq!(asserted, 2);
224+
}
225+
226+
#[test]
227+
fn can_concatenate_partial_defmt_frames() {
228+
let mut parser = FrameDelimiter::new();
229+
230+
let mut asserted = 0;
231+
parser.feed(b"\xFF\x00frame", |_| {
232+
panic!("Should not have a frame yet");
233+
});
234+
parser.feed(b" data\x00\xFF", |frame| {
235+
assert_eq!(frame, FrameKind::Defmt(b"frame data"));
236+
asserted += 1;
237+
});
238+
parser.feed(b"\x00second frame", |_| {
239+
panic!("Should not have a frame yet");
139240
});
241+
parser.feed(b"\x00last part", |frame| {
242+
match asserted {
243+
1 => assert_eq!(frame, FrameKind::Defmt(b"second frame")),
244+
2 => assert_eq!(frame, FrameKind::Raw(b"last part")),
245+
_ => panic!("Too many frames"),
246+
}
247+
asserted += 1;
248+
});
249+
assert_eq!(asserted, 3);
250+
}
251+
252+
#[test]
253+
fn defmt_frames_back_to_back() {
254+
let mut parser = FrameDelimiter::new();
255+
256+
let mut asserted = 0;
257+
parser.feed(b"\xFF\x00frame data1\x00\xFF\x00frame data2\x00", |frame| {
258+
match asserted {
259+
0 => assert_eq!(frame, FrameKind::Defmt(b"frame data1")),
260+
1 => assert_eq!(frame, FrameKind::Defmt(b"frame data2")),
261+
_ => panic!("Too many frames"),
262+
}
263+
asserted += 1;
264+
});
265+
assert_eq!(asserted, 2);
266+
}
267+
268+
#[test]
269+
fn output_includes_ff_and_0_bytes() {
270+
let mut parser = FrameDelimiter::new();
271+
272+
let mut asserted = 0;
273+
parser.feed(
274+
b"some message\xFF with parts of\0 a defmt \0\xFF frame delimiter",
275+
|frame| {
276+
assert_eq!(
277+
frame,
278+
FrameKind::Raw(
279+
b"some message\xFF with parts of\0 a defmt \0\xFF frame delimiter"
280+
)
281+
);
282+
asserted += 1;
283+
},
284+
);
285+
assert_eq!(asserted, 1);
140286
}
141287
}

0 commit comments

Comments
 (0)