11use std:: io:: Write ;
22
33use crossterm:: { style:: Print , QueueableCommand } ;
4- use defmt_decoder:: { Frame , Table } ;
5- use miette:: { bail, Context , Diagnostic , Result } ;
4+ use defmt_decoder:: {
5+ log:: format:: { Formatter , FormatterConfig , FormatterFormat } ,
6+ Frame ,
7+ Table ,
8+ } ;
9+ use log:: warn;
10+ use miette:: { bail, ensure, Context , Diagnostic , Result } ;
611use thiserror:: Error ;
712
813use crate :: cli:: monitor:: parser:: InputParser ;
@@ -22,9 +27,13 @@ pub enum DefmtError {
2227 NoDefmtData ,
2328
2429 #[ error( "Failed to parse defmt data" ) ]
25- #[ diagnostic( code( espflash:: monitor:: defmt:: parse_failed ) ) ]
30+ #[ diagnostic( code( espflash:: monitor:: defmt:: table_parse_failed ) ) ]
2631 TableParseFailed ,
2732
33+ #[ error( "Failed to parse defmt location data" ) ]
34+ #[ diagnostic( code( espflash:: monitor:: defmt:: location_parse_failed) ) ]
35+ LocationDataParseFailed ,
36+
2837 #[ error( "Unsupported defmt encoding: {0:?}. Only rzcobs is supported." ) ]
2938 #[ diagnostic( code( espflash:: monitor:: defmt:: unsupported_encoding) ) ]
3039 UnsupportedEncoding ( defmt_decoder:: Encoding ) ,
@@ -101,16 +110,16 @@ impl FrameDelimiter {
101110 }
102111}
103112
104- #[ derive( Debug ) ]
105- pub struct EspDefmt {
106- delimiter : FrameDelimiter ,
113+ struct DefmtData {
107114 table : Table ,
115+ locs : Option < defmt_decoder:: Locations > ,
116+ formatter : Formatter ,
108117}
109118
110- impl EspDefmt {
119+ impl DefmtData {
111120 /// Loads symbols from the ELF file (if provided) and initializes the
112121 /// context.
113- fn load_table ( elf : Option < & [ u8 ] > ) -> Result < Table > {
122+ fn load ( elf : Option < & [ u8 ] > , output_format : Option < String > ) -> Result < Self > {
114123 let Some ( elf) = elf else {
115124 bail ! ( DefmtError :: NoElf ) ;
116125 } ;
@@ -125,35 +134,93 @@ impl EspDefmt {
125134
126135 // We only support rzcobs encoding because it is the only way to multiplex
127136 // a defmt stream and an ASCII log stream over the same serial port.
128- if encoding == defmt_decoder:: Encoding :: Rzcobs {
129- Ok ( table)
137+ ensure ! (
138+ encoding == defmt_decoder:: Encoding :: Rzcobs ,
139+ DefmtError :: UnsupportedEncoding ( encoding)
140+ ) ;
141+
142+ let locs = table
143+ . get_locations ( elf)
144+ . map_err ( |_e| DefmtError :: LocationDataParseFailed ) ?;
145+
146+ let locs = if !table. is_empty ( ) && locs. is_empty ( ) {
147+ warn ! ( "Insufficient DWARF info; compile your program with `debug = 2` to enable location info." ) ;
148+ None
149+ } else if table. indices ( ) . all ( |idx| locs. contains_key ( & ( idx as u64 ) ) ) {
150+ Some ( locs)
130151 } else {
131- bail ! ( DefmtError :: UnsupportedEncoding ( encoding ) )
132- }
133- }
152+ warn ! ( "Location info is incomplete; it will be omitted from the output." ) ;
153+ None
154+ } ;
134155
135- pub fn new ( elf : Option < & [ u8 ] > ) -> Result < Self > {
136- Self :: load_table ( elf) . map ( |table| Self {
137- delimiter : FrameDelimiter :: new ( ) ,
156+ let show_location = locs. is_some ( ) ;
157+ let has_timestamp = table. has_timestamp ( ) ;
158+
159+ let format = match output_format. as_deref ( ) {
160+ None | Some ( "oneline" ) => FormatterFormat :: OneLine {
161+ with_location : show_location,
162+ } ,
163+ Some ( "full" ) => FormatterFormat :: Default {
164+ with_location : show_location,
165+ } ,
166+ Some ( format) => FormatterFormat :: Custom ( format) ,
167+ } ;
168+
169+ Ok ( Self {
138170 table,
171+ locs,
172+ formatter : Formatter :: new ( FormatterConfig {
173+ format,
174+ is_timestamp_available : has_timestamp,
175+ } ) ,
139176 } )
140177 }
141178
142- fn handle_raw ( bytes : & [ u8 ] , out : & mut dyn Write ) {
143- out. write_all ( bytes) . unwrap ( ) ;
144- }
179+ fn print ( & self , frame : Frame < ' _ > , out : & mut dyn Write ) {
180+ let loc = self . locs . as_ref ( ) . and_then ( |locs| locs. get ( & frame. index ( ) ) ) ;
181+ let ( file, line, module) = if let Some ( loc) = loc {
182+ (
183+ Some ( loc. file . display ( ) . to_string ( ) ) ,
184+ Some ( loc. line . try_into ( ) . unwrap ( ) ) ,
185+ Some ( loc. module . as_str ( ) ) ,
186+ )
187+ } else {
188+ ( None , None , None )
189+ } ;
190+ let s = self
191+ . formatter
192+ . format_frame ( frame, file. as_deref ( ) , line, module) ;
145193
146- fn handle_defmt ( frame : Frame < ' _ > , out : & mut dyn Write ) {
147- out. queue ( Print ( frame. display ( true ) . to_string ( ) ) ) . unwrap ( ) ;
194+ out. queue ( Print ( s) ) . unwrap ( ) ;
148195 out. queue ( Print ( "\r \n " ) ) . unwrap ( ) ;
149196
150197 out. flush ( ) . unwrap ( ) ;
151198 }
152199}
153200
201+ pub struct EspDefmt {
202+ delimiter : FrameDelimiter ,
203+ defmt_data : DefmtData ,
204+ }
205+
206+ impl std:: fmt:: Debug for EspDefmt {
207+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
208+ f. debug_struct ( "EspDefmt" ) . finish ( )
209+ }
210+ }
211+
212+ impl EspDefmt {
213+ pub fn new ( elf : Option < & [ u8 ] > , output_format : Option < String > ) -> Result < Self > {
214+ DefmtData :: load ( elf, output_format) . map ( |defmt_data| Self {
215+ delimiter : FrameDelimiter :: new ( ) ,
216+ defmt_data,
217+ } )
218+ }
219+ }
220+
154221impl InputParser for EspDefmt {
155222 fn feed ( & mut self , bytes : & [ u8 ] , out : & mut dyn Write ) {
156- let mut decoder = self . table . new_stream_decoder ( ) ;
223+ let mut decoder = self . defmt_data . table . new_stream_decoder ( ) ;
157224
158225 self . delimiter . feed ( bytes, |frame| match frame {
159226 FrameKind :: Defmt ( frame) => {
@@ -162,12 +229,12 @@ impl InputParser for EspDefmt {
162229 decoder. received ( FRAME_END ) ;
163230
164231 if let Ok ( frame) = decoder. decode ( ) {
165- Self :: handle_defmt ( frame, out) ;
232+ self . defmt_data . print ( frame, out) ;
166233 } else {
167234 log:: warn!( "Failed to decode defmt frame" ) ;
168235 }
169236 }
170- FrameKind :: Raw ( bytes) => Self :: handle_raw ( bytes, out ) ,
237+ FrameKind :: Raw ( bytes) => out . write_all ( bytes) . unwrap ( ) ,
171238 } ) ;
172239 }
173240}
0 commit comments