Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,7 @@ Initial release

### [defmt-print-next]

* [#985] Add `--set-addr` option to actively set the `_SEGGER_RTT` address
* [#952] Support sending dtr on connection for serial port input
* [#965] Also support `--log-format=online` or `--log-format=default`
* [#986] Bump MSRV to 1.81
Expand Down
1 change: 1 addition & 0 deletions print/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ version = "1.0.0"
anyhow = "1"
clap = { version = "4.0", features = ["derive", "env"] }
defmt-decoder = { version = "=1.0.0", path = "../decoder" }
goblin = "0.9"
log = "0.4"
notify = "8"
tokio = { version = "1.38", features = ["full"] }
Expand Down
46 changes: 44 additions & 2 deletions print/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ use defmt_decoder::{
},
DecodeError, Frame, Locations, Table, DEFMT_VERSIONS,
};
use goblin::elf::Elf;
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
use tokio::{
fs,
io::{self, AsyncReadExt, Stdin},
io::{self, AsyncReadExt, AsyncWriteExt, Stdin},
net::TcpStream,
select,
sync::mpsc::Receiver,
Expand All @@ -27,30 +28,43 @@ use tokio_serial::{SerialPort, SerialPortBuilderExt, SerialStream};
#[derive(Parser, Clone)]
#[command(name = "defmt-print")]
struct Opts {
/// The firmware running on the device being logged
#[arg(short, required = true, conflicts_with("version"))]
elf: Option<PathBuf>,

/// Emit logs in JSON format
#[arg(long)]
json: bool,

/// A format string for target-generated logs
#[arg(long)]
log_format: Option<String>,

/// A format string for host-generated logs
#[arg(long)]
host_log_format: Option<String>,

/// Tell Segger J-Link what the RTT address is
#[arg(long)]
set_addr: bool,

/// Log any malformed defmt frames that are being skipped
#[arg(long)]
show_skipped_frames: bool,

/// Print extra detail
#[arg(short, long)]
verbose: bool,

/// Print the version number, and quit
#[arg(short = 'V', long)]
version: bool,

/// Reload the ELF file when it changes
#[arg(short, long)]
watch_elf: bool,

/// Which operation to perform
#[command(subcommand)]
command: Option<Command>,
}
Expand All @@ -59,14 +73,15 @@ struct Opts {
enum Command {
/// Read defmt frames from stdin (default)
Stdin,
/// Read defmt frames from tcp
/// Read defmt frames from a TCP server
Tcp {
#[arg(long, env = "RTT_HOST", default_value = "localhost")]
host: String,

#[arg(long, env = "RTT_PORT", default_value_t = 19021)]
port: u16,
},
/// Read defmt frames from a serial port
Serial {
#[arg(long, env = "SERIAL_PORT", default_value = "/dev/ttyUSB0")]
path: PathBuf,
Expand Down Expand Up @@ -106,6 +121,27 @@ impl Source {
Ok(Source::Serial(ser))
}

async fn set_rtt_addr(&mut self, elf_bytes: &[u8]) -> anyhow::Result<()> {
let Source::Tcp(tcpstream) = self else {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since passing --set-addr only makes sense with tcp we could warn if it is used with serial. Or we could move it into Command::Tcp so it is only available with tcp.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - fixed.

return Ok(());
};

let elf = Elf::parse(elf_bytes)?;
let rtt_symbol = elf
.syms
.iter()
.find(|sym| elf.strtab.get_at(sym.st_name) == Some("_SEGGER_RTT"))
.ok_or_else(|| anyhow!("Symbol '_SEGGER_RTT' not found in ELF file"))?;

let cmd = format!(
"$$SEGGER_TELNET_ConfigStr=SetRTTAddr;{:#x}$$",
rtt_symbol.st_value
);
tcpstream.write_all(cmd.as_bytes()).await?;

Ok(())
}

async fn read(&mut self, buf: &mut [u8]) -> anyhow::Result<(usize, bool)> {
match self {
Source::Stdin(stdin) => {
Expand Down Expand Up @@ -186,6 +222,7 @@ async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> {
json,
log_format,
host_log_format,
set_addr,
show_skipped_frames,
verbose,
..
Expand All @@ -196,6 +233,11 @@ async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> {
let table = Table::parse(&bytes)?.ok_or_else(|| anyhow!(".defmt data not found"))?;
let locs = table.get_locations(&bytes)?;

if set_addr {
// Using Segger RTT server, set the _SEGGER_RTT address actively.
source.set_rtt_addr(&bytes).await?;
}

// check if the locations info contains all the indicies
let locs = if table.indices().all(|idx| locs.contains_key(&(idx as u64))) {
Some(locs)
Expand Down
Loading