@@ -21,7 +21,7 @@ use std::{
2121
2222use anyhow:: { anyhow, bail} ;
2323use colored:: Colorize as _;
24- use defmt_decoder:: { DecodeError , Frame , Locations , StreamDecoder } ;
24+ use defmt_decoder:: { DecodeError , Encoding , Frame , Locations , StreamDecoder } ;
2525use 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
204204fn 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
333326fn 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
356358fn 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
366390fn 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(
429454fn 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