Skip to content
This repository was archived by the owner on Jan 30, 2024. It is now read-only.

Commit 56d376d

Browse files
committed
Support multiple RTT channels
1 parent 4bb1991 commit 56d376d

File tree

2 files changed

+147
-52
lines changed

2 files changed

+147
-52
lines changed

src/cli.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ pub(crate) struct Opts {
4747
#[structopt(long)]
4848
pub(crate) connect_under_reset: bool,
4949

50+
/// Path to a trace file to write data to.
51+
#[structopt(long, short)]
52+
pub(crate) out_file: Option<String>,
53+
54+
/// The minimum log level of defmt messages that will be printed to stdout.
55+
#[structopt(long, default_value = "debug")]
56+
pub(crate) min_level: Level,
57+
5058
/// Enable more verbose logging.
5159
#[structopt(short, long, parse(from_occurrences))]
5260
verbose: u32,
@@ -80,9 +88,10 @@ pub(crate) fn handle_arguments() -> anyhow::Result<i32> {
8088
let opts: Opts = Opts::from_args();
8189
let verbose = opts.verbose;
8290

91+
let min_defmt_level = opts.min_level;
8392
defmt_decoder::log::init_logger(verbose >= 1, move |metadata| {
8493
if defmt_decoder::log::is_defmt_frame(metadata) {
85-
true // We want to display *all* defmt frames.
94+
metadata.level() <= min_defmt_level
8695
} else {
8796
// Log depending on how often the `--verbose` (`-v`) cli-param is supplied:
8897
// * 0: log everything from probe-run, with level "info" or higher

src/main.rs

Lines changed: 137 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use std::{
2121

2222
use anyhow::{anyhow, bail};
2323
use colored::Colorize as _;
24-
use defmt_decoder::{DecodeError, Frame, Locations, StreamDecoder};
24+
use defmt_decoder::{DecodeError, Encoding, Frame, Locations, StreamDecoder};
2525
use probe_rs::{
2626
flashing::{self, Format},
2727
Core,
@@ -161,7 +161,7 @@ fn start_program(sess: &mut Session, elf: &Elf) -> anyhow::Result<()> {
161161
Ok(())
162162
}
163163

164-
/// Set rtt to blocking mode
164+
/// Set rtt to blocking mode for channel 0
165165
fn set_rtt_to_blocking(
166166
core: &mut Core,
167167
main_fn_address: u32,
@@ -201,16 +201,16 @@ fn extract_and_print_logs(
201201
let exit = Arc::new(AtomicBool::new(false));
202202
let sig_id = signal_hook::flag::register(signal::SIGINT, exit.clone())?;
203203

204-
let mut logging_channel = if let Some(address) = elf.rtt_buffer_address() {
205-
Some(setup_logging_channel(address, sess.clone())?)
204+
let logging_channels = if let Some(address) = elf.rtt_buffer_address() {
205+
setup_logging_channels(address, sess.clone())?
206206
} else {
207207
eprintln!("RTT logs not available; blocking until the device halts..");
208-
None
208+
vec![]
209209
};
210210

211-
let use_defmt = logging_channel
212-
.as_ref()
213-
.map_or(false, |channel| channel.name() == Some("defmt"));
211+
let use_defmt = logging_channels
212+
.get(0)
213+
.map_or(false, |c| c.name() == Some("defmt"));
214214

215215
if use_defmt && opts.no_flash {
216216
bail!(
@@ -220,49 +220,40 @@ fn extract_and_print_logs(
220220
bail!("\"defmt\" RTT channel is in use, but the firmware binary contains no defmt data");
221221
}
222222

223-
let mut decoder_and_encoding = if use_defmt {
224-
elf.defmt_table
225-
.as_ref()
226-
.map(|table| (table.new_stream_decoder(), table.encoding()))
223+
let mut logging_channels = logging_channels
224+
.into_iter()
225+
.enumerate()
226+
.map(|(i, rtt)| LogChannel::new(i, rtt, if use_defmt { &elf.defmt_table } else { &None }))
227+
.collect::<Vec<_>>();
228+
229+
print_separator();
230+
231+
let mut out_file = if let Some(path) = &opts.out_file {
232+
let file = fs::OpenOptions::new()
233+
.append(true)
234+
.create(true)
235+
.write(true)
236+
.read(false)
237+
.open(path)?;
238+
239+
Some(io::BufWriter::new(file))
227240
} else {
228241
None
229242
};
230243

231-
print_separator();
232-
233244
let stdout = io::stdout();
234245
let mut stdout = stdout.lock();
235-
let mut read_buf = [0; 1024];
236246
let mut was_halted = false;
237247
while !exit.load(Ordering::Relaxed) {
238-
if let Some(logging_channel) = &mut logging_channel {
239-
let num_bytes_read = match logging_channel.read(&mut read_buf) {
240-
Ok(n) => n,
241-
Err(e) => {
242-
eprintln!("RTT error: {}", e);
243-
break;
244-
}
245-
};
246-
247-
if num_bytes_read != 0 {
248-
match decoder_and_encoding.as_mut() {
249-
Some((stream_decoder, encoding)) => {
250-
stream_decoder.received(&read_buf[..num_bytes_read]);
251-
252-
decode_and_print_defmt_logs(
253-
&mut **stream_decoder,
254-
elf.defmt_locations.as_ref(),
255-
current_dir,
256-
opts.shorten_paths,
257-
encoding.can_recover(),
258-
)?;
259-
}
260-
261-
_ => {
262-
stdout.write_all(&read_buf[..num_bytes_read])?;
263-
stdout.flush()?;
264-
}
265-
}
248+
for logging_channel in logging_channels.iter_mut().rev() {
249+
if !logging_channel.print_all(
250+
&mut stdout,
251+
&mut out_file,
252+
&elf.defmt_locations,
253+
current_dir,
254+
opts,
255+
)? {
256+
break;
266257
}
267258
}
268259

@@ -277,6 +268,7 @@ fn extract_and_print_logs(
277268
}
278269

279270
drop(stdout);
271+
drop(out_file);
280272

281273
signal_hook::low_level::unregister(sig_id);
282274
signal_hook::flag::register_conditional_default(signal::SIGINT, exit.clone())?;
@@ -296,15 +288,17 @@ fn extract_and_print_logs(
296288
}
297289

298290
fn decode_and_print_defmt_logs(
291+
number: usize,
299292
stream_decoder: &mut dyn StreamDecoder,
293+
out_file: &mut Option<io::BufWriter<fs::File>>,
300294
locations: Option<&Locations>,
301295
current_dir: &Path,
302296
shorten_paths: bool,
303297
encoding_can_recover: bool,
304298
) -> anyhow::Result<()> {
305299
loop {
306300
match stream_decoder.decode() {
307-
Ok(frame) => forward_to_logger(&frame, locations, current_dir, shorten_paths),
301+
Ok(frame) => forward_to_logger(number, &frame, locations, current_dir, shorten_paths, out_file),
308302
Err(DecodeError::UnexpectedEof) => break,
309303
Err(DecodeError::Malformed) => match encoding_can_recover {
310304
// if recovery is impossible, abort
@@ -319,13 +313,34 @@ fn decode_and_print_defmt_logs(
319313
}
320314

321315
fn forward_to_logger(
316+
number: usize,
322317
frame: &Frame,
323318
locations: Option<&Locations>,
324319
current_dir: &Path,
325320
shorten_paths: bool,
321+
out_file: &mut Option<io::BufWriter<fs::File>>,
326322
) {
327323
let (file, line, mod_path) = location_info(frame, locations, current_dir, shorten_paths);
328324
defmt_decoder::log::log_defmt(frame, file.as_deref(), line, mod_path.as_deref());
325+
326+
if let Some(out_file) = out_file {
327+
let timestamp = frame
328+
.display_timestamp()
329+
.map(|display| display.to_string())
330+
.unwrap_or_default();
331+
332+
writeln!(
333+
out_file,
334+
"{}|{}|{}|{}:{}|{}|{}",
335+
timestamp,
336+
number,
337+
frame.level().map_or("UNKOWN", |l| l.as_str()),
338+
file.as_deref().unwrap_or_default(),
339+
line.map(|l| l.to_string()).unwrap_or_default(),
340+
mod_path.unwrap_or_default(),
341+
frame.display_message()
342+
).unwrap();
343+
}
329344
}
330345

331346
fn location_info(
@@ -355,10 +370,10 @@ fn location_info(
355370
.unwrap_or((None, None, None))
356371
}
357372

358-
fn setup_logging_channel(
373+
fn setup_logging_channels(
359374
rtt_buffer_address: u32,
360375
sess: Arc<Mutex<Session>>,
361-
) -> anyhow::Result<UpChannel> {
376+
) -> anyhow::Result<Vec<UpChannel>> {
362377
const NUM_RETRIES: usize = 10; // picked at random, increase if necessary
363378

364379
let scan_region = ScanRegion::Exact(rtt_buffer_address);
@@ -367,12 +382,13 @@ fn setup_logging_channel(
367382
Ok(mut rtt) => {
368383
log::debug!("Successfully attached RTT");
369384

370-
let channel = rtt
371-
.up_channels()
372-
.take(0)
373-
.ok_or_else(|| anyhow!("RTT up channel 0 not found"))?;
385+
let channels = rtt.up_channels().drain().collect::<Vec<_>>();
386+
387+
if channels.len() == 0 {
388+
bail!("RTT up channel 0 not found");
389+
}
374390

375-
return Ok(channel);
391+
return Ok(channels);
376392
}
377393

378394
Err(probe_rs_rtt::Error::ControlBlockNotFound) => {
@@ -393,3 +409,73 @@ fn setup_logging_channel(
393409
fn print_separator() {
394410
println!("{}", "─".repeat(80).dimmed());
395411
}
412+
413+
struct LogChannel<'a> {
414+
number: usize,
415+
rtt: UpChannel,
416+
buf: [u8; 1024],
417+
decoder: Option<(Box<dyn StreamDecoder + 'a>, Encoding)>,
418+
}
419+
420+
impl<'a> LogChannel<'a> {
421+
pub fn new(number: usize, rtt: UpChannel, table: &'a Option<defmt_decoder::Table>) -> Self {
422+
Self {
423+
number,
424+
rtt,
425+
buf: [0u8; 1024],
426+
decoder: table.as_ref().map(|t| (t.new_stream_decoder(), t.encoding())),
427+
}
428+
}
429+
430+
pub fn print_all(
431+
&mut self,
432+
stdout: &mut io::StdoutLock,
433+
out_file: &mut Option<io::BufWriter<fs::File>>,
434+
locations: &Option<std::collections::BTreeMap<u64, defmt_decoder::Location>>,
435+
current_dir: &Path,
436+
opts: &cli::Opts,
437+
) -> anyhow::Result<bool> {
438+
let Self {
439+
number,
440+
rtt,
441+
buf,
442+
decoder,
443+
} = self;
444+
445+
let num_bytes_read = match rtt.read(buf) {
446+
Ok(n) => n,
447+
Err(e) => {
448+
eprintln!("RTT error: {}", e);
449+
return Ok(false);
450+
}
451+
};
452+
453+
if num_bytes_read != 0 {
454+
match decoder.as_mut() {
455+
Some((stream_decoder, encoding)) => {
456+
stream_decoder.received(&buf[..num_bytes_read]);
457+
458+
decode_and_print_defmt_logs(
459+
*number,
460+
&mut **stream_decoder,
461+
out_file,
462+
locations.as_ref(),
463+
current_dir,
464+
opts.shorten_paths,
465+
encoding.can_recover(),
466+
)?;
467+
}
468+
469+
_ => {
470+
stdout.write_all(&buf[..num_bytes_read])?;
471+
stdout.flush()?;
472+
if let Some(out_file) = out_file {
473+
out_file.write_all(&buf[..num_bytes_read])?;
474+
}
475+
}
476+
}
477+
}
478+
479+
Ok(true)
480+
}
481+
}

0 commit comments

Comments
 (0)