1use std::{
2 io::Read,
3 path::Path,
4 sync::{atomic::AtomicBool, Arc},
5 time::Duration,
6};
7
8use defmt_decoder::{DecodeError, Frame, Locations, Table};
9use defmt_json_schema::v1::{JsonFrame, Location as JsonLocation, ModulePath};
10
11#[derive(Debug, thiserror::Error)]
12pub enum DefmtError {
13 #[error("Received a malformend frame.")]
14 MalformedFrame,
15 #[error("No frames received.")]
16 NoFramesReceived,
17 #[error("TCP error: {}", .0)]
18 TcpError(String),
19 #[error("TCP connection error: {}", .0)]
20 TcpConnect(String),
21 #[error("Failed reading binary. Cause: {}", .0)]
22 ReadBinary(std::io::Error),
23 #[error("Missing defmt data in given binary.")]
24 MissingDefmt,
25}
26
27pub fn read_defmt_frames(
28 binary: &Path,
29 workspace_root: &Path,
30 mut stream: std::net::TcpStream,
31 end_signal: Arc<AtomicBool>,
32) -> Result<Vec<JsonFrame>, DefmtError> {
33 let bytes = std::fs::read(binary).map_err(DefmtError::ReadBinary)?;
34 let table = Table::parse(&bytes)
35 .map_err(|_| DefmtError::MissingDefmt)?
36 .ok_or(DefmtError::MissingDefmt)?;
37 let locs = table
38 .get_locations(&bytes)
39 .map_err(|_| DefmtError::MissingDefmt)?;
40
41 let locs = if table.indices().all(|idx| locs.contains_key(&(idx as u64))) {
43 Some(locs)
44 } else {
45 log::warn!("(BUG) location info is incomplete; it will be omitted from the output");
46 None
47 };
48
49 const READ_BUFFER_SIZE: usize = 1024;
50 let mut buf = [0; READ_BUFFER_SIZE];
51 let mut decoder = table.new_stream_decoder();
52 let mut stream_decoder = Box::pin(&mut decoder);
53
54 let _ = stream.set_read_timeout(Some(Duration::from_secs(2)));
55 let mut json_frames = Vec::new();
56
57 loop {
58 if end_signal.load(std::sync::atomic::Ordering::Relaxed) {
60 return Ok(json_frames);
61 }
62
63 let n = match stream.read(&mut buf) {
64 Ok(len) => {
65 if len == 0 {
66 continue;
67 } else {
68 len
69 }
70 }
71 Err(err) => {
72 if err.kind() == std::io::ErrorKind::TimedOut {
73 continue;
74 } else if matches!(
75 err.kind(),
76 std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::ConnectionReset
77 ) {
78 return Ok(json_frames);
79 } else {
80 return Err(DefmtError::TcpError(err.to_string()));
81 }
82 }
83 };
84
85 stream_decoder.received(&buf[..n]);
86
87 loop {
89 match stream_decoder.decode() {
90 Ok(frame) => {
91 let json_frame = create_json_frame(workspace_root, &frame, &locs);
92
93 let mod_path = if let Some(mod_path) = &json_frame.location.module_path {
94 if mod_path.modules.is_empty() {
95 Some(format!("{}::{}", mod_path.crate_name, mod_path.function))
96 } else {
97 Some(format!(
98 "{}::{}::{}",
99 mod_path.crate_name,
100 mod_path.modules.join("::"),
101 mod_path.function
102 ))
103 }
104 } else {
105 None
106 };
107
108 let val = Some([("msg", log::kv::Value::from_display(&json_frame.data))]);
110
111 match json_frame.level {
112 Some(level) => {
113 let log_record = log::RecordBuilder::new()
114 .level(level)
115 .file(json_frame.location.file.as_deref())
116 .line(json_frame.location.line)
117 .module_path(mod_path.as_deref())
118 .target("embedded")
119 .key_values(&val)
120 .build();
121 log::logger().log(&log_record);
122 }
123 None => {
124 if mantra_rust_macros::extract::extract_first_coverage(&json_frame.data)
126 .is_none()
127 {
128 println!("TARGET-PRINT | {}", json_frame.data);
129
130 if log::Level::Trace <= log::STATIC_MAX_LEVEL
131 && log::Level::Trace <= log::max_level()
132 {
133 let location = if json_frame.location.file.is_some()
134 && json_frame.location.line.is_some()
135 && mod_path.is_some()
136 {
137 format!(
138 "{} in {}:{}",
139 mod_path.unwrap(),
140 json_frame.location.file.as_ref().unwrap(),
141 json_frame.location.line.unwrap(),
142 )
143 } else {
144 "no-location info available".to_string()
145 };
146
147 println!(" | => {location}");
148 }
149 }
150 }
151 }
152
153 json_frames.push(json_frame);
154 }
155 Err(DecodeError::UnexpectedEof) => break,
156 Err(DecodeError::Malformed) => match table.encoding().can_recover() {
157 false => return Err(DefmtError::MalformedFrame),
159 true => {
161 log::warn!("Malformed defmt frame skipped!");
162 continue;
163 }
164 },
165 }
166 }
167 }
168}
169
170pub type LocationInfo = (Option<String>, Option<u32>, Option<String>);
171
172pub fn location_info(
173 workspace_root: &Path,
174 frame: &Frame,
175 locs: &Option<Locations>,
176) -> LocationInfo {
177 let (mut file, mut line, mut mod_path) = (None, None, None);
178
179 let loc = locs.as_ref().map(|locs| locs.get(&frame.index()));
180
181 if let Some(Some(loc)) = loc {
182 let path = mantra_lang_tracing::path::make_relative(&loc.file, workspace_root)
184 .unwrap_or(loc.file.to_path_buf());
185
186 file = Some(path.display().to_string());
187 line = Some(loc.line as u32);
188 mod_path = Some(loc.module.clone());
189 }
190
191 (file, line, mod_path)
192}
193
194pub fn create_json_frame(
196 workspace_root: &Path,
197 frame: &Frame,
198 locs: &Option<Locations>,
199) -> JsonFrame {
200 let (file, line, mod_path) = location_info(workspace_root, frame, locs);
201 let host_timestamp = time::OffsetDateTime::now_utc()
202 .unix_timestamp_nanos()
203 .min(i64::MAX as i128) as i64;
204
205 JsonFrame {
206 data: frame.display_message().to_string(),
207 host_timestamp,
208 level: frame.level().map(to_json_level),
209 location: JsonLocation {
210 file,
211 line,
212 module_path: create_module_path(mod_path.as_deref()),
213 },
214 target_timestamp: frame
215 .display_timestamp()
216 .map(|ts| ts.to_string())
217 .unwrap_or_default(),
218 }
219}
220
221fn create_module_path(module_path: Option<&str>) -> Option<ModulePath> {
222 let mut path = module_path?.split("::").collect::<Vec<_>>();
223
224 if path.len() < 2 {
226 return None;
227 };
228
229 let function = path.pop()?.to_string();
231 let crate_name = path.remove(0).to_string();
233
234 Some(ModulePath {
235 crate_name,
236 modules: path.into_iter().map(|a| a.to_string()).collect(),
237 function,
238 })
239}
240
241pub fn to_json_level(level: defmt_parser::Level) -> log::Level {
242 match level {
243 defmt_parser::Level::Trace => log::Level::Trace,
244 defmt_parser::Level::Debug => log::Level::Debug,
245 defmt_parser::Level::Info => log::Level::Info,
246 defmt_parser::Level::Warn => log::Level::Warn,
247 defmt_parser::Level::Error => log::Level::Error,
248 }
249}