Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds a watch flag to watch the elf file and reloads the file if it changed #807

Merged
merged 10 commits into from
Sep 3, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- [#843]: `defmt`: Sort IDs of log msgs by severity to allow runtime filtering by severity
- [#822]: `CI`: Run `cargo semver-checks` on every PR
- [#855]: `defmt-print`: Now uses tokio to make tcp and stdin reads async (in preparation for a `watch elf` flag)
- [#807]: `defmt-print`: Add watch_elf flag to allow elf reload without restarting `defmt-print`

[#852]: https://github.com/knurling-rs/defmt/pull/852
[#847]: https://github.com/knurling-rs/defmt/pull/847
[#845]: https://github.com/knurling-rs/defmt/pull/845
[#843]: https://github.com/knurling-rs/defmt/pull/843
[#822]: https://github.com/knurling-rs/defmt/pull/822
[#855]: https://github.com/knurling-rs/defmt/pull/855
[#807]: https://github.com/knurling-rs/defmt/pull/807

## [v0.3.8] - 2024-05-17

Expand Down
1 change: 1 addition & 0 deletions decoder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ object = { version = "0.35", default-features = false, features = [
serde = { version = "1", features = ["derive"] }
serde_json = { version = "1", features = ["arbitrary_precision"] }
regex = "1"
alterable_logger = "1.0.0"
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved

[features]
# WARNING: API and wire format subject to change.
Expand Down
4 changes: 2 additions & 2 deletions decoder/src/log/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ pub fn init_logger(
JsonLogger::new(formatter, host_formatter, should_log)
}
};
log::set_boxed_logger(logger).unwrap();
log::set_max_level(LevelFilter::Trace);
alterable_logger::set_boxed_logger(logger);
alterable_logger::set_max_level(LevelFilter::Trace);
}

fn timestamp_and_level_from_frame(frame: &Frame<'_>) -> (String, Option<Level>) {
Expand Down
2 changes: 2 additions & 0 deletions print/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ defmt-decoder = { version = "=0.3.11", path = "../decoder", features = [
] }
log = "0.4"
tokio = { version = "1.38", features = ["full"] }
notify = "6.1.1"
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
futures = "0.3"
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
83 changes: 69 additions & 14 deletions print/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@ use defmt_decoder::{
},
DecodeError, Frame, Locations, Table, DEFMT_VERSIONS,
};
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};

use tokio::{
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
fs,
io::{self, AsyncReadExt, Stdin},
net::TcpStream,
select,
sync::mpsc::Receiver,
};

/// Prints defmt-encoded logs to stdout
#[derive(Parser)]
#[derive(Parser, Clone)]
#[command(name = "defmt-print")]
struct Opts {
#[arg(short, required = true, conflicts_with("version"))]
Expand All @@ -44,11 +47,14 @@ struct Opts {
#[arg(short = 'V', long)]
version: bool,

#[arg[short, long]]
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
watch_elf: bool,

#[command(subcommand)]
command: Option<Command>,
}

#[derive(Subcommand)]
#[derive(Subcommand, Clone)]
enum Command {
/// Read defmt frames from stdin (default)
Stdin,
Expand Down Expand Up @@ -94,20 +100,74 @@ const READ_BUFFER_SIZE: usize = 1024;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let opts = Opts::parse();

if opts.version {
return print_version();
}

// We create the source outside of the run command since recreating the stdin looses us some frames
let mut source = match opts.command.clone() {
None | Some(Command::Stdin) => Source::stdin(),
Some(Command::Tcp { host, port }) => Source::tcp(host, port).await?,
};

if opts.watch_elf {
let (tx, mut rx) = tokio::sync::mpsc::channel(1);

let mut watcher = RecommendedWatcher::new(
move |res| {
futures::executor::block_on(async {
tx.send(res).await.unwrap();
})
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
},
Config::default(),
)?;
let mut directory_path = opts.elf.clone().unwrap();
directory_path.pop(); // We want the elf directory instead of the elf, since some editors remove and recreate the file on save which will remove the notifier
watcher.watch(directory_path.as_ref(), RecursiveMode::NonRecursive)?;
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved

let path = opts.elf.clone().unwrap();

loop {
select! {
r = run(opts.clone(), &mut source) => r?,
_ = has_file_changed(&mut rx, &path) => ()
}
}
Urhengulas marked this conversation as resolved.
Show resolved Hide resolved
} else {
run(opts, &mut source).await
}
}

async fn has_file_changed(rx: &mut Receiver<Result<Event, notify::Error>>, path: &PathBuf) -> bool {
loop {
if let Some(Ok(event)) = rx.recv().await {
if event.paths.contains(path) {
match event.kind {
notify::EventKind::Create(_) | notify::EventKind::Modify(_) => {
break;
}
_ => (),
}
}
}
}
true
}

async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> {
let Opts {
elf,
json,
log_format,
host_log_format,
show_skipped_frames,
verbose,
version,
command,
} = Opts::parse();

if version {
return print_version();
}
version: _,
watch_elf: _,
command: _,
} = opts;

// read and parse elf file
let bytes = fs::read(elf.unwrap()).await?;
Expand Down Expand Up @@ -162,11 +222,6 @@ async fn main() -> anyhow::Result<()> {
let mut stream_decoder = table.new_stream_decoder();
let current_dir = env::current_dir()?;

let mut source = match command {
None | Some(Command::Stdin) => Source::stdin(),
Some(Command::Tcp { host, port }) => Source::tcp(host, port).await?,
};

loop {
// read from stdin or tcpstream and push it to the decoder
let (n, eof) = source.read(&mut buf).await?;
Expand Down
Loading