-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlib.rs
More file actions
333 lines (293 loc) · 10.1 KB
/
Copy pathlib.rs
File metadata and controls
333 lines (293 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
pub mod gui;
pub mod metric;
pub mod setting;
pub mod symbol;
use std::{io::Read, sync::mpsc, time::Duration};
use defmt_decoder::DecodeError;
use defmt_parser::Level;
use object::{Object, ObjectSymbol};
use probe_rs::{
Core, MemoryInterface,
rtt::{self, ChannelMode, Rtt},
};
use rerun::TextLogLevel;
use serde::Deserialize;
use shunting::{MathContext, ShuntingParser};
use crate::{metric::Metric, setting::Setting, symbol::Symbol};
pub fn read_value(core: &mut Core, address: u64, ty: Type) -> Result<f64, probe_rs::Error> {
let x = match ty {
Type::u8 => core.read_word_8(address)? as f64,
Type::u16 => core.read_word_16(address)? as f64,
Type::u32 => core.read_word_32(address)? as f64,
Type::i8 => core.read_word_8(address)? as i8 as f64,
Type::i16 => core.read_word_16(address)? as i16 as f64,
Type::i32 => core.read_word_32(address)? as i32 as f64,
Type::f32 => f32::from_bits(core.read_word_32(address)?) as f64,
};
Ok(x)
}
#[allow(non_camel_case_types)]
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Hash, Eq)]
pub enum Type {
u8,
u16,
u32,
i8,
i16,
i32,
f32,
}
// Most of this is taken from https://github.com/knurling-rs/defmt/blob/8e517f8d7224237893e39337a61de8ef98b341f2/decoder/src/elf2table/mod.rs and modified
pub fn parse(elf_bytes: &[u8]) -> (Vec<Metric>, Vec<Setting>, rtt::ScanRegion) {
let elf = object::File::parse(elf_bytes).unwrap();
let mut metrics = Vec::new();
let mut settings = Vec::new();
let mut scan_region = rtt::ScanRegion::Ram;
for entry in elf.symbols() {
let Ok(name) = entry.name() else {
continue;
};
if name == "_SEGGER_RTT" {
scan_region = rtt::ScanRegion::Exact(entry.address());
continue;
}
let Ok(sym) = Symbol::demangle(name) else {
continue;
};
// TODO: Why does this assert not succeed?
//assert_eq!(entry.size(), 4);
match sym {
Symbol::Metric { name, expr, ty } => {
let expr = ShuntingParser::parse_str(&expr).unwrap();
let math_ctx = MathContext::new();
math_ctx.setvar(&name, shunting::MathOp::Number(0.0));
math_ctx
.eval(&expr)
.expect("Use the metrics name as name for the value in the expression");
metrics.push(Metric {
name,
expr,
ty,
address: entry.address(),
last_value: f64::NAN,
});
}
Symbol::Setting {
name,
ty,
range,
step_size,
} => {
settings.push(Setting {
name,
ty,
address: entry.address(),
value: f64::NAN,
range,
step_size,
});
}
}
}
(metrics, settings, scan_region)
}
/// Parse elf file into a set of Metrics and Settings
pub fn parse_elf_file(elf_path: &str) -> (Vec<Metric>, Vec<Setting>, rtt::ScanRegion) {
let mut buffer = Vec::new();
std::fs::File::open(elf_path)
.unwrap()
.read_to_end(&mut buffer)
.unwrap();
parse(&buffer)
}
/// A background task which communicates with the probe
///
/// This handles
/// * defmt logging
/// * reading metrics
/// * reading initial values for settings
/// * writing updated settings
#[allow(clippy::too_many_arguments)]
pub fn probe_background_thread(
update_rate: Duration,
channel_mode: Option<ChannelMode>,
target: &str,
elf_bytes: &[u8],
mut settings: Vec<Setting>,
mut metrics: Vec<Metric>,
scan_region: rtt::ScanRegion,
settings_update_receiver: mpsc::Receiver<Setting>,
initial_settings_sender: mpsc::Sender<Vec<Setting>>,
) {
let mut session = probe_rs::Session::auto_attach(target, Default::default()).unwrap();
let mut core = session.core(0).unwrap();
let mut rtt = Rtt::attach_region(&mut core, &scan_region).unwrap();
let table = defmt_decoder::Table::parse(elf_bytes).unwrap().unwrap();
// TODO: Get this to work
// This produces ascii escape codes which egui/rerun does not seem to understand
/*let show_timestamps = true;
let show_location = true;
let log_format = None;
let has_timestamp = table.has_timestamp();
// Format options:
// 1. Oneline format with optional location
// 2. Custom format for the channel
// 3. Default with optional location
let format = match log_format {
None | Some("oneline") => FormatterFormat::OneLine {
with_location: show_location,
},
Some("full") => FormatterFormat::Default {
with_location: show_location,
},
Some(format) => FormatterFormat::Custom(format),
};
let formatter = Formatter::new(FormatterConfig {
format,
is_timestamp_available: has_timestamp && show_timestamps,
});*/
let rec = rerun::RecordingStreamBuilder::new("probe-plotter")
.spawn()
.unwrap();
let locs = match table.get_locations(elf_bytes) {
Ok(locs) if locs.is_empty() => {
rec.log(
"log",
&rerun::TextLog::new("Insufficient DWARF info; compile your program with `debug = 2` to enable location info.").with_level(TextLogLevel::WARN)).unwrap();
None
}
Ok(locs) if table.indices().all(|idx| locs.contains_key(&(idx as u64))) => Some(locs),
Ok(_) => {
rec.log(
"log",
&rerun::TextLog::new(
"Location info is incomplete; it will be omitted from the output.",
)
.with_level(TextLogLevel::WARN),
)
.unwrap();
None
}
Err(e) => {
rec.log(
"log",
&rerun::TextLog::new(format!(
"Failed to parse location data: {e:?}; it will be omitted from the output."
))
.with_level(TextLogLevel::WARN),
)
.unwrap();
None
}
};
if let Some(channel_mode) = channel_mode {
for ch in &mut rtt.up_channels {
ch.set_mode(&mut core, channel_mode).unwrap();
}
}
let mut decoders: Vec<_> = rtt
.up_channels
.iter()
.map(|_| table.new_stream_decoder())
.collect();
// Load initial values from device
for setting in &mut settings {
setting.read(&mut core).unwrap();
}
// Send initial settings back to main thread
initial_settings_sender.send(settings).unwrap();
let mut math_ctx = MathContext::new();
loop {
for mut setting in settings_update_receiver.try_iter() {
setting.write(setting.value, &mut core).unwrap();
}
receive_defmt_messages(&mut rtt, &mut core, &mut decoders);
log_defmt_messages(&rec, &locs, &mut decoders);
for m in &mut metrics {
m.read(&mut core, &mut math_ctx).unwrap();
let (x, _s) = m.compute(&mut math_ctx);
rec.log(m.name.clone(), &rerun::Scalars::single(x)).unwrap();
}
std::thread::sleep(update_rate);
}
}
/// Receive defmt messages frome probe
pub fn receive_defmt_messages<'a>(
rtt: &mut Rtt,
core: &mut Core,
decoders: &mut Vec<Box<dyn defmt_decoder::StreamDecoder + 'a>>,
) {
loop {
let mut has_data = false;
let mut buf = [0; 256];
for (ch, decoder) in rtt.up_channels.iter_mut().zip(&mut *decoders) {
let read_count = ch.read(core, &mut buf).unwrap();
if read_count > 0 {
dbg!(read_count);
decoder.received(&buf[..read_count]);
has_data = true;
}
}
if !has_data {
break;
}
}
}
/// Log defmt messages to rerun
pub fn log_defmt_messages<'a>(
rec: &rerun::RecordingStream,
locs: &'a Option<std::collections::BTreeMap<u64, defmt_decoder::Location>>,
decoders: &mut Vec<Box<dyn defmt_decoder::StreamDecoder + 'a>>,
) {
loop {
let mut has_decoded = false;
for decoder in &mut *decoders {
let frame = match decoder.decode() {
Ok(f) => f,
Err(DecodeError::UnexpectedEof) => continue,
Err(defmt_decoder::DecodeError::Malformed) => {
rec.log(
"log",
&rerun::TextLog::new("DecodeError::Malformed")
.with_level(TextLogLevel::WARN),
)
.unwrap();
continue;
}
};
has_decoded = true;
let level = match frame.level() {
Some(Level::Trace) => TextLogLevel::TRACE,
Some(Level::Debug) => TextLogLevel::DEBUG,
Some(Level::Info) => TextLogLevel::INFO,
Some(Level::Warn) => TextLogLevel::WARN,
Some(Level::Error) => TextLogLevel::ERROR,
None => TextLogLevel::INFO,
};
let loc = locs.as_ref().and_then(|locs| locs.get(&frame.index()));
let (file, line, module) = if let Some(loc) = loc {
(
loc.file.display().to_string(),
loc.line.to_string(),
loc.module.as_str(),
)
} else {
(
format!(
"└─ <invalid location: defmt frame-index: {}>",
frame.index()
),
"?".to_string(),
"?",
)
};
//let msg = formatter.format_frame(frame, Some(&file), line, module);
let msg = format!("{module} :: {} {file}:{line}", frame.display(false));
rec.log("log", &rerun::TextLog::new(msg).with_level(level))
.unwrap();
}
if !has_decoded {
break;
}
}
}