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

Commit 21f62ec

Browse files
committed
Support multiple RTT channels
1 parent 7b20784 commit 21f62ec

File tree

2 files changed

+159
-52
lines changed

2 files changed

+159
-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 output.
5159
#[structopt(short, long, parse(from_occurrences))]
5260
pub(crate) 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: 149 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
config::MemoryRegion,
2727
flashing::{self, Format},
@@ -200,7 +200,7 @@ fn start_program(sess: &mut Session, elf: &Elf) -> anyhow::Result<()> {
200200
Ok(())
201201
}
202202

203-
/// Set rtt to blocking mode
203+
/// Set rtt to blocking mode for channel 0
204204
fn set_rtt_to_blocking(
205205
core: &mut Core,
206206
main_fn_address: u32,
@@ -241,16 +241,16 @@ fn extract_and_print_logs(
241241
let exit = Arc::new(AtomicBool::new(false));
242242
let sig_id = signal_hook::flag::register(signal::SIGINT, exit.clone())?;
243243

244-
let mut logging_channel = if let Some(address) = elf.rtt_buffer_address() {
245-
Some(setup_logging_channel(address, core, memory_map)?)
244+
let logging_channels = if let Some(address) = elf.rtt_buffer_address() {
245+
setup_logging_channels(address, core, memory_map)?
246246
} else {
247247
eprintln!("RTT logs not available; blocking until the device halts..");
248-
None
248+
vec![]
249249
};
250250

251-
let use_defmt = logging_channel
252-
.as_ref()
253-
.map_or(false, |channel| channel.name() == Some("defmt"));
251+
let use_defmt = logging_channels
252+
.get(0)
253+
.map_or(false, |c| c.name() == Some("defmt"));
254254

255255
if use_defmt && opts.no_flash {
256256
bail!(
@@ -260,49 +260,41 @@ fn extract_and_print_logs(
260260
bail!("\"defmt\" RTT channel is in use, but the firmware binary contains no defmt data");
261261
}
262262

263-
let mut decoder_and_encoding = if use_defmt {
264-
elf.defmt_table
265-
.as_ref()
266-
.map(|table| (table.new_stream_decoder(), table.encoding()))
263+
let mut logging_channels = logging_channels
264+
.into_iter()
265+
.enumerate()
266+
.map(|(i, rtt)| LogChannel::new(i, rtt, if use_defmt { &elf.defmt_table } else { &None }))
267+
.collect::<Vec<_>>();
268+
269+
print_separator();
270+
271+
let mut out_file = if let Some(path) = &opts.out_file {
272+
let file = fs::OpenOptions::new()
273+
.append(true)
274+
.create(true)
275+
.write(true)
276+
.read(false)
277+
.open(path)?;
278+
279+
Some(io::BufWriter::new(file))
267280
} else {
268281
None
269282
};
270283

271-
print_separator();
272-
273284
let stdout = io::stdout();
274285
let mut stdout = stdout.lock();
275-
let mut read_buf = [0; 1024];
276286
let mut was_halted = false;
277287
while !exit.load(Ordering::Relaxed) {
278-
if let Some(logging_channel) = &mut logging_channel {
279-
let num_bytes_read = match logging_channel.read(core, &mut read_buf) {
280-
Ok(n) => n,
281-
Err(e) => {
282-
eprintln!("RTT error: {}", e);
283-
break;
284-
}
285-
};
286-
287-
if num_bytes_read != 0 {
288-
match decoder_and_encoding.as_mut() {
289-
Some((stream_decoder, encoding)) => {
290-
stream_decoder.received(&read_buf[..num_bytes_read]);
291-
292-
decode_and_print_defmt_logs(
293-
&mut **stream_decoder,
294-
elf.defmt_locations.as_ref(),
295-
current_dir,
296-
opts.shorten_paths,
297-
encoding.can_recover(),
298-
)?;
299-
}
300-
301-
_ => {
302-
stdout.write_all(&read_buf[..num_bytes_read])?;
303-
stdout.flush()?;
304-
}
305-
}
288+
for logging_channel in logging_channels.iter_mut().rev() {
289+
if !logging_channel.print_all(
290+
core,
291+
&mut stdout,
292+
&mut out_file,
293+
&elf.defmt_locations,
294+
current_dir,
295+
opts,
296+
)? {
297+
break;
306298
}
307299
}
308300

@@ -315,6 +307,7 @@ fn extract_and_print_logs(
315307
}
316308

317309
drop(stdout);
310+
drop(out_file);
318311

319312
signal_hook::low_level::unregister(sig_id);
320313
signal_hook::flag::register_conditional_default(signal::SIGINT, exit.clone())?;
@@ -331,15 +324,24 @@ fn extract_and_print_logs(
331324
}
332325

333326
fn decode_and_print_defmt_logs(
327+
number: usize,
334328
stream_decoder: &mut dyn StreamDecoder,
329+
out_file: &mut Option<io::BufWriter<fs::File>>,
335330
locations: Option<&Locations>,
336331
current_dir: &Path,
337332
shorten_paths: bool,
338333
encoding_can_recover: bool,
339334
) -> anyhow::Result<()> {
340335
loop {
341336
match stream_decoder.decode() {
342-
Ok(frame) => forward_to_logger(&frame, locations, current_dir, shorten_paths),
337+
Ok(frame) => forward_to_logger(
338+
number,
339+
&frame,
340+
locations,
341+
current_dir,
342+
shorten_paths,
343+
out_file,
344+
),
343345
Err(DecodeError::UnexpectedEof) => break,
344346
Err(DecodeError::Malformed) => match encoding_can_recover {
345347
// if recovery is impossible, abort
@@ -354,13 +356,35 @@ fn decode_and_print_defmt_logs(
354356
}
355357

356358
fn forward_to_logger(
359+
number: usize,
357360
frame: &Frame,
358361
locations: Option<&Locations>,
359362
current_dir: &Path,
360363
shorten_paths: bool,
364+
out_file: &mut Option<io::BufWriter<fs::File>>,
361365
) {
362366
let (file, line, mod_path) = location_info(frame, locations, current_dir, shorten_paths);
363367
defmt_decoder::log::log_defmt(frame, file.as_deref(), line, mod_path.as_deref());
368+
369+
if let Some(out_file) = out_file {
370+
let timestamp = frame
371+
.display_timestamp()
372+
.map(|display| display.to_string())
373+
.unwrap_or_default();
374+
375+
writeln!(
376+
out_file,
377+
"{}|{}|{}|{}:{}|{}|{}",
378+
timestamp,
379+
number,
380+
frame.level().map_or("UNKOWN", |l| l.as_str()),
381+
file.as_deref().unwrap_or_default(),
382+
line.map(|l| l.to_string()).unwrap_or_default(),
383+
mod_path.unwrap_or_default(),
384+
frame.display_message()
385+
)
386+
.unwrap();
387+
}
364388
}
365389

366390
fn location_info(
@@ -390,11 +414,11 @@ fn location_info(
390414
.unwrap_or((None, None, None))
391415
}
392416

393-
fn setup_logging_channel(
417+
fn setup_logging_channels(
394418
rtt_buffer_address: u32,
395419
core: &mut probe_rs::Core,
396420
memory_map: &[MemoryRegion],
397-
) -> anyhow::Result<UpChannel> {
421+
) -> anyhow::Result<Vec<UpChannel>> {
398422
const NUM_RETRIES: usize = 10; // picked at random, increase if necessary
399423

400424
let scan_region = ScanRegion::Exact(rtt_buffer_address);
@@ -403,12 +427,13 @@ fn setup_logging_channel(
403427
Ok(mut rtt) => {
404428
log::debug!("Successfully attached RTT");
405429

406-
let channel = rtt
407-
.up_channels()
408-
.take(0)
409-
.ok_or_else(|| anyhow!("RTT up channel 0 not found"))?;
430+
let channels = rtt.up_channels().drain().collect::<Vec<_>>();
431+
432+
if channels.is_empty() {
433+
bail!("RTT up channel 0 not found");
434+
}
410435

411-
return Ok(channel);
436+
return Ok(channels);
412437
}
413438

414439
Err(probe_rs_rtt::Error::ControlBlockNotFound) => {
@@ -429,3 +454,76 @@ fn setup_logging_channel(
429454
fn print_separator() {
430455
println!("{}", "─".repeat(80).dimmed());
431456
}
457+
458+
struct LogChannel<'a> {
459+
number: usize,
460+
rtt: UpChannel,
461+
buf: [u8; 1024],
462+
decoder: Option<(Box<dyn StreamDecoder + 'a>, Encoding)>,
463+
}
464+
465+
impl<'a> LogChannel<'a> {
466+
pub fn new(number: usize, rtt: UpChannel, table: &'a Option<defmt_decoder::Table>) -> Self {
467+
Self {
468+
number,
469+
rtt,
470+
buf: [0u8; 1024],
471+
decoder: table
472+
.as_ref()
473+
.map(|t| (t.new_stream_decoder(), t.encoding())),
474+
}
475+
}
476+
477+
pub fn print_all(
478+
&mut self,
479+
core: &mut probe_rs::Core,
480+
stdout: &mut io::StdoutLock,
481+
out_file: &mut Option<io::BufWriter<fs::File>>,
482+
locations: &Option<std::collections::BTreeMap<u64, defmt_decoder::Location>>,
483+
current_dir: &Path,
484+
opts: &cli::Opts,
485+
) -> anyhow::Result<bool> {
486+
let Self {
487+
number,
488+
rtt,
489+
buf,
490+
decoder,
491+
} = self;
492+
493+
let num_bytes_read = match rtt.read(core, buf) {
494+
Ok(n) => n,
495+
Err(e) => {
496+
eprintln!("RTT error: {}", e);
497+
return Ok(false);
498+
}
499+
};
500+
501+
if num_bytes_read != 0 {
502+
match decoder.as_mut() {
503+
Some((stream_decoder, encoding)) => {
504+
stream_decoder.received(&buf[..num_bytes_read]);
505+
506+
decode_and_print_defmt_logs(
507+
*number,
508+
&mut **stream_decoder,
509+
out_file,
510+
locations.as_ref(),
511+
current_dir,
512+
opts.shorten_paths,
513+
encoding.can_recover(),
514+
)?;
515+
}
516+
517+
_ => {
518+
stdout.write_all(&buf[..num_bytes_read])?;
519+
stdout.flush()?;
520+
if let Some(out_file) = out_file {
521+
out_file.write_all(&buf[..num_bytes_read])?;
522+
}
523+
}
524+
}
525+
}
526+
527+
Ok(true)
528+
}
529+
}

0 commit comments

Comments
 (0)