1use crate::*;
2use anyhow::{anyhow, Result};
3use defmt_decoder::DecodeError;
4use num_traits::Zero;
5use probe_rs::config::MemoryRegion;
6pub use probe_rs::rtt::ChannelMode;
7use probe_rs::rtt::{DownChannel, Rtt, ScanRegion, UpChannel};
8use probe_rs::Core;
9use serde::Deserialize;
10use std::collections::HashMap;
11use std::fs::File;
12use std::{
13 fmt,
14 fmt::Write,
15 fs,
16 io::{Read, Seek},
17 str::FromStr,
18};
19use time::{OffsetDateTime, UtcOffset};
20
21pub fn attach_to_rtt(
22 core: &mut Core,
23 memory_map: &[MemoryRegion],
24 elf_file: &Path,
25 rtt_config: &RttConfig,
26 timestamp_offset: UtcOffset,
27) -> Result<crate::rtt::RttActiveTarget, anyhow::Error> {
28 log::info!("Initializing RTT");
29 let rtt_header_address = if let Ok(mut file) = File::open(elf_file) {
30 if let Some(address) = RttActiveTarget::get_rtt_symbol(&mut file) {
31 ScanRegion::Exact(address as u32)
32 } else {
33 ScanRegion::Ram
34 }
35 } else {
36 ScanRegion::Ram
37 };
38
39 match Rtt::attach_region(core, memory_map, &rtt_header_address) {
40 Ok(rtt) => {
41 log::info!("RTT initialized.");
42 let app = RttActiveTarget::new(rtt, elf_file, rtt_config, timestamp_offset)?;
43 Ok(app)
44 }
45 Err(err) => Err(anyhow!("Error attempting to attach to RTT: {}", err)),
46 }
47}
48
49fn default_channel_formats() -> Vec<RttChannelConfig> {
51 vec![]
52}
53
54fn default_include_location() -> bool {
56 true
58}
59
60#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, Default)]
61pub enum DataFormat {
62 #[default]
63 String,
64 BinaryLE,
65 Defmt,
66}
67impl FromStr for DataFormat {
68 type Err = String;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 let src = s.to_ascii_lowercase();
72 match &src.to_ascii_lowercase()[..] {
73 "string" => Ok(Self::String),
75 "binaryle" => Ok(Self::BinaryLE),
76 "defmt" => Ok(Self::Defmt),
77 _ => Err(format!("{src} is not a valid format")),
78 }
79 }
80}
81
82#[derive(clap::Parser, Debug, Clone, Deserialize, Default)]
84pub struct RttConfig {
85 #[structopt(skip)]
86 #[serde(default, rename = "rttEnabled")]
87 pub enabled: bool,
88 #[structopt(skip)]
90 #[serde(default = "default_channel_formats", rename = "rttChannelFormats")]
91 pub channels: Vec<RttChannelConfig>,
92}
93
94#[derive(clap::Parser, Debug, Clone, serde::Deserialize, Default)]
96#[serde(rename_all = "camelCase")]
97pub struct RttChannelConfig {
98 pub channel_number: Option<usize>,
99 pub channel_name: Option<String>,
100 #[serde(default)]
101 pub data_format: DataFormat,
102 #[structopt(skip)]
103 #[serde(default)]
104 pub show_timestamps: bool,
106 #[structopt(skip)]
107 #[serde(default = "default_include_location")]
108 pub show_location: bool,
110}
111
112#[derive(Debug)]
114pub struct RttActiveChannel {
115 pub up_channel: Option<UpChannel>,
116 pub down_channel: Option<DownChannel>,
117 pub channel_name: String,
118 pub data_format: DataFormat,
119 _input_data: String,
121 rtt_buffer: RttBuffer,
122 show_timestamps: bool,
123 show_location: bool,
124
125 timestamp_offset: UtcOffset,
130}
131
132impl RttActiveChannel {
134 pub fn new(
135 up_channel: Option<UpChannel>,
136 down_channel: Option<DownChannel>,
137 channel_config: Option<RttChannelConfig>,
138 timestamp_offset: UtcOffset,
139 ) -> Self {
140 let full_config = match &channel_config {
141 Some(channel_config) => channel_config.clone(),
142 None => RttChannelConfig {
143 ..Default::default() },
145 };
146 let buffer_size: usize = up_channel
147 .as_ref()
148 .map(|up| up.buffer_size())
149 .or_else(|| down_channel.as_ref().map(|down| down.buffer_size()))
150 .unwrap_or(1024); let defmt_enabled: bool = up_channel
152 .as_ref()
153 .map(|up| up.name() == Some("defmt"))
154 .or_else(|| {
155 down_channel
156 .as_ref()
157 .map(|down| down.name() == Some("defmt"))
158 })
159 .unwrap_or(false); let (data_format, show_location) = if defmt_enabled {
161 let show_location = if let Some(channel_config) = channel_config {
162 channel_config.show_location
163 } else {
164 true
165 };
166 (DataFormat::Defmt, show_location)
167 } else {
168 (full_config.data_format, false)
169 };
170 let name = up_channel
171 .as_ref()
172 .and_then(|up| up.name().map(Into::into))
173 .or_else(|| {
174 down_channel
175 .as_ref()
176 .and_then(|down| down.name().map(Into::into))
177 })
178 .or_else(|| full_config.clone().channel_name)
179 .unwrap_or_else(|| {
180 format!(
181 "Unnamed {:?} RTT channel - {}",
182 data_format,
183 full_config.channel_number.unwrap_or(0)
184 )
185 });
186 Self {
187 up_channel,
188 down_channel,
189 channel_name: name,
190 data_format,
191 _input_data: String::new(),
192 rtt_buffer: RttBuffer::new(buffer_size),
193 show_timestamps: full_config.show_timestamps,
194 show_location,
195 timestamp_offset,
196 }
197 }
198
199 pub fn number(&self) -> Option<usize> {
201 self.up_channel.as_ref().map(|uc| uc.number())
202 }
203
204 pub fn poll_rtt(&mut self, core: &mut Core) -> Option<usize> {
207 if let Some(channel) = self.up_channel.as_mut() {
208 for _loop_count in 0..10 {
210 match channel.read(core, self.rtt_buffer.0.as_mut()) {
211 Ok(count) => {
212 if count.is_zero() {
213 return None;
214 } else {
215 return Some(count);
216 }
217 }
218 Err(err) => {
219 if matches!(err, probe_rs::rtt::Error::Probe(_)) {
220 std::thread::sleep(std::time::Duration::from_millis(50));
221 } else {
222 log::error!("\nError reading from RTT: {}", err);
223 return None;
224 }
225 }
226 }
227 }
228 }
229 None
230 }
231
232 pub fn get_rtt_data(
236 &mut self,
237 core: &mut Core,
238 defmt_state: Option<&(defmt_decoder::Table, Option<defmt_decoder::Locations>)>,
239 ) -> Result<Option<(String, String)>, anyhow::Error> {
240 self
241 .poll_rtt(core)
242 .map(|bytes_read| {
243 Ok((
244 self.number().unwrap_or(0).to_string(), {
246 let mut formatted_data = String::new();
247 match self.data_format {
248 DataFormat::String => {
249 let incoming = String::from_utf8_lossy(&self.rtt_buffer.0[..bytes_read]).to_string();
250 for (_i, line) in incoming.split_terminator('\n').enumerate() {
251 if self.show_timestamps {
252 write!(formatted_data, "{} :", OffsetDateTime::now_utc().to_offset(self.timestamp_offset))
253 .map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
254 }
255 writeln!(formatted_data, "{line}").map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
256 }
257 }
258 DataFormat::BinaryLE => {
259 for element in &self.rtt_buffer.0[..bytes_read] {
260 write!(formatted_data, "{element:#04x}").map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
262 }
263 }
264 DataFormat::Defmt => {
265 match defmt_state {
266 Some((table, locs)) => {
267 let mut stream_decoder = table.new_stream_decoder();
268 stream_decoder.received(&self.rtt_buffer.0[..bytes_read]);
269 loop {
270 match stream_decoder.decode() {
271 Ok(frame) => {
272 let loc = locs.as_ref().and_then(|locs| locs.get(&frame.index()) );
273 writeln!(formatted_data, "{}", frame.display(false)).map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
274 if self.show_location {
275 if let Some(loc) = loc {
276 let relpath = if let Ok(relpath) =
277 loc.file.strip_prefix(&std::env::current_dir().unwrap())
278 {
279 relpath
280 } else {
281 &loc.file
283 };
284 writeln!(formatted_data,
285 "└─ {}:{}",
286 relpath.display(),
287 loc.line
288 ).map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
289 } else {
290 writeln!(formatted_data, "└─ <invalid location: defmt frame-index: {}>", frame.index()).map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
291 }
292 }
293 continue;
294 },
295 Err(DecodeError::UnexpectedEof) => break,
296 Err(DecodeError::Malformed) => match table.encoding().can_recover() {
297 false => {
299 return Err(anyhow!("Unrecoverable error while decoding Defmt data and some data may have been lost: {:?}", DecodeError::Malformed));
300 },
301 true => continue,
303 },
304
305 }
306 }
307 }
308 None => {
309 write!(formatted_data, "Running rtt in defmt mode but table or locations could not be loaded.")
310 .map_or_else(|err| log::error!("Failed to format RTT data - {:?}", err), |r|r);
311 }
312 }
313 }
314 };
315 formatted_data
316 }
317 ))
318 }).transpose()
319 }
320
321 pub fn _push_rtt(&mut self, core: &mut Core) {
322 if let Some(down_channel) = self.down_channel.as_mut() {
323 self._input_data += "\n";
324 down_channel
325 .write(core, self._input_data.as_bytes())
326 .unwrap();
327 self._input_data.clear();
328 }
329 }
330}
331
332#[derive(Debug)]
334pub struct RttActiveTarget {
335 pub active_channels: Vec<RttActiveChannel>,
336 pub defmt_state: Option<(defmt_decoder::Table, Option<defmt_decoder::Locations>)>,
337}
338
339impl RttActiveTarget {
340 pub fn new(
342 mut rtt: probe_rs::rtt::Rtt,
343 elf_file: &Path,
344 rtt_config: &RttConfig,
345 timestamp_offset: UtcOffset,
346 ) -> Result<Self> {
347 let mut active_channels = Vec::new();
348 let up_channels = rtt.up_channels().drain();
350 let down_channels = rtt.down_channels().drain();
351 for channel in up_channels {
352 let number = channel.number();
353 let channel_config = rtt_config
354 .channels
355 .clone()
356 .into_iter()
357 .find(|channel| channel.channel_number == Some(number));
358 active_channels.push(RttActiveChannel::new(
359 Some(channel),
360 None,
361 channel_config,
362 timestamp_offset,
363 ));
364 }
365
366 for channel in down_channels {
367 let number = channel.number();
368 let channel_config = rtt_config
369 .channels
370 .clone()
371 .into_iter()
372 .find(|channel| channel.channel_number == Some(number));
373 active_channels.push(RttActiveChannel::new(
374 None,
375 Some(channel),
376 channel_config,
377 timestamp_offset,
378 ));
379 }
380
381 if active_channels.is_empty() {
383 return Err(anyhow!(
384 "RTT Initialized correctly, but there were no active channels configured"
385 ));
386 }
387
388 let defmt_enabled = active_channels
389 .iter()
390 .any(|elem| elem.data_format == DataFormat::Defmt);
391 let defmt_state = if defmt_enabled {
392 let elf = fs::read(elf_file).map_err(|err| {
393 anyhow!(
394 "Error reading program binary while initalizing RTT: {}",
395 err
396 )
397 })?;
398 if let Some(table) = defmt_decoder::Table::parse(&elf)? {
399 let locs = {
400 let locs = table.get_locations(&elf)?;
401
402 if !table.is_empty() && locs.is_empty() {
403 log::warn!("Insufficient DWARF info; compile your program with `debug = 2` to enable location info.");
404 None
405 } else if table.indices().all(|idx| locs.contains_key(&(idx as u64))) {
406 Some(locs)
407 } else {
408 log::warn!(
409 "Location info is incomplete; it will be omitted from the output."
410 );
411 None
412 }
413 };
414 Some((table, locs))
415 } else {
416 log::warn!("No `Table` definition in DWARF info; compile your program with `debug = 2` to enable location info.");
417 None
418 }
419 } else {
420 None
421 };
422
423 Ok(Self {
424 active_channels,
425 defmt_state,
426 })
427 }
428
429 pub fn get_rtt_symbol<T: Read + Seek>(file: &mut T) -> Option<u64> {
430 let mut buffer = Vec::new();
431 if file.read_to_end(&mut buffer).is_ok() {
432 if let Ok(binary) = goblin::elf::Elf::parse(buffer.as_slice()) {
433 for sym in &binary.syms {
434 if let Some(name) = binary.strtab.get_at(sym.st_name) {
435 if name == "_SEGGER_RTT" {
436 return Some(sym.st_value);
437 }
438 }
439 }
440 }
441 }
442
443 log::warn!("No RTT header info was present in the ELF file. Does your firmware run RTT?");
444 None
445 }
446
447 #[deprecated(
450 since = "0.14.0",
451 note = "This function is deprecated and will be removed in a future version. Please use `poll_rtt_fallible` instead."
452 )]
453 pub fn poll_rtt(&mut self, core: &mut Core) -> HashMap<String, String> {
454 let defmt_state = self.defmt_state.as_ref();
455 self.active_channels
456 .iter_mut()
457 .filter_map(|active_channel| {
458 active_channel
459 .get_rtt_data(core, defmt_state)
460 .unwrap_or_default()
461 })
462 .collect::<HashMap<_, _>>()
463 }
464
465 pub fn poll_rtt_fallible(
468 &mut self,
469 core: &mut Core,
470 ) -> Result<HashMap<String, String>, anyhow::Error> {
471 let defmt_state = self.defmt_state.as_ref();
472 let mut data = HashMap::new();
473 for channel in self.active_channels.iter_mut() {
474 if let Some((channel, formatted_data)) = channel.get_rtt_data(core, defmt_state)? {
475 data.insert(channel, formatted_data);
476 }
477 }
478 Ok(data)
479 }
480
481 }
485
486struct RttBuffer(Vec<u8>);
487impl RttBuffer {
488 pub fn new(mut buffer_size: usize) -> RttBuffer {
490 let mut rtt_buffer = vec![0u8; 1];
491 while buffer_size > 0 {
492 buffer_size -= 1;
493 rtt_buffer.push(0u8);
494 }
495 RttBuffer(rtt_buffer)
496 }
497}
498impl fmt::Debug for RttBuffer {
499 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
500 self.0.fmt(f)
501 }
502}