use std::{
env, fs,
io::{self, Read},
path::PathBuf,
};
use anyhow::anyhow;
use defmt_decoder::Table;
use structopt::StructOpt;
#[derive(StructOpt)]
#[structopt(name = "defmt-print")]
struct Opts {
#[structopt(short, parse(from_os_str), required_unless_one(&["version"]))]
elf: Option<PathBuf>,
#[structopt(short = "V", long)]
version: bool,
}
const READ_BUFFER_SIZE: usize = 1024;
fn main() -> anyhow::Result<()> {
let opts: Opts = Opts::from_args();
if opts.version {
return print_version();
}
let verbose = false;
defmt_decoder::log::init_logger(verbose, |metadata| {
defmt_decoder::log::is_defmt_frame(metadata)
});
let bytes = fs::read(&opts.elf.unwrap())?;
let table = Table::parse(&bytes)?.ok_or_else(|| anyhow!(".defmt data not found"))?;
let locs = table.get_locations(&bytes)?;
let locs = if table.indices().all(|idx| locs.contains_key(&(idx as u64))) {
Some(locs)
} else {
log::warn!("(BUG) location info is incomplete; it will be omitted from the output");
None
};
let mut buf = [0; READ_BUFFER_SIZE];
let mut frames = vec![];
let current_dir = env::current_dir()?;
let stdin = io::stdin();
let mut stdin = stdin.lock();
loop {
let n = stdin.read(&mut buf)?;
frames.extend_from_slice(&buf[..n]);
loop {
match table.decode(&frames) {
Ok((frame, consumed)) => {
let loc = locs.as_ref().map(|locs| &locs[&frame.index()]);
let (mut file, mut line, mut mod_path) = (None, None, None);
if let Some(loc) = loc {
let relpath = if let Ok(relpath) = loc.file.strip_prefix(¤t_dir) {
relpath
} else {
&loc.file
};
file = Some(relpath.display().to_string());
line = Some(loc.line as u32);
mod_path = Some(loc.module.clone());
}
defmt_decoder::log::log_defmt(
&frame,
file.as_deref(),
line,
mod_path.as_deref(),
);
let num_frames = frames.len();
frames.rotate_left(consumed);
frames.truncate(num_frames - consumed);
}
Err(defmt_decoder::DecodeError::UnexpectedEof) => break,
Err(defmt_decoder::DecodeError::Malformed) => {
log::error!("failed to decode defmt data: {:x?}", frames);
return Err(defmt_decoder::DecodeError::Malformed.into());
}
}
}
}
}
#[allow(clippy::unnecessary_wraps)]
fn print_version() -> anyhow::Result<()> {
println!("{} {}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"));
println!("supported defmt version: {}", defmt_decoder::DEFMT_VERSION);
Ok(())
}