From e658a6c780e27f188937cd42ced1bf7259e7f8e0 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Sun, 28 Jan 2024 01:02:43 +0100 Subject: [PATCH 1/8] Adds a watch flag to watch the elf file and reload if it changed --- decoder/Cargo.toml | 1 + decoder/src/log/mod.rs | 4 +- print/Cargo.toml | 2 + print/src/main.rs | 90 ++++++++++++++++++++++++++++++++++-------- 4 files changed, 79 insertions(+), 18 deletions(-) diff --git a/decoder/Cargo.toml b/decoder/Cargo.toml index c843ca5e..e8554fa6 100644 --- a/decoder/Cargo.toml +++ b/decoder/Cargo.toml @@ -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" [features] # WARNING: API and wire format subject to change. diff --git a/decoder/src/log/mod.rs b/decoder/src/log/mod.rs index 6f286513..b850676a 100644 --- a/decoder/src/log/mod.rs +++ b/decoder/src/log/mod.rs @@ -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) { diff --git a/print/Cargo.toml b/print/Cargo.toml index 3ac6d491..89f0cd4e 100644 --- a/print/Cargo.toml +++ b/print/Cargo.toml @@ -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" +futures = "0.3" diff --git a/print/src/main.rs b/print/src/main.rs index ae04f35c..50faab4b 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -1,8 +1,9 @@ use std::{ env, - path::{Path, PathBuf}, + path::{Path, PathBuf}, time::Duration, }; +use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; use anyhow::anyhow; use clap::{Parser, Subcommand}; use defmt_decoder::{ @@ -16,11 +17,11 @@ use defmt_decoder::{ use tokio::{ fs, io::{self, AsyncReadExt, Stdin}, - net::TcpStream, + 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"))] @@ -44,11 +45,14 @@ struct Opts { #[arg(short = 'V', long)] version: bool, + #[arg[short, long]] + watch_elf: bool, + #[command(subcommand)] command: Option, } -#[derive(Subcommand)] +#[derive(Subcommand, Clone)] enum Command { /// Read defmt frames from stdin (default) Stdin, @@ -94,6 +98,68 @@ 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(); + }) + }, + Config::default().with_poll_interval(Duration::from_secs(1)) + )?; + 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)?; + + let path = opts.elf.clone().unwrap(); + + loop { + select! { + r = run(opts.clone(), &mut source) => { + if r.is_err() { + return r; + } + }, + _ = has_file_changed(&mut rx, &path) => () + } + } + } else { + run(opts, &mut source).await + } + +} + +async fn has_file_changed(rx: &mut Receiver>, 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, @@ -101,13 +167,10 @@ async fn main() -> anyhow::Result<()> { 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?; @@ -162,11 +225,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?; From 053318dd3728362fd181d55514bfd506cc14df85 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Sun, 28 Jan 2024 01:30:44 +0100 Subject: [PATCH 2/8] Add changelog entry, fix rust format --- CHANGELOG.md | 2 ++ print/src/main.rs | 27 ++++++++++++--------------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21b364a4..5b01fada 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ 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 @@ -20,6 +21,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). [#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 diff --git a/print/src/main.rs b/print/src/main.rs index 50faab4b..12277567 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -1,9 +1,8 @@ use std::{ env, - path::{Path, PathBuf}, time::Duration, + path::{Path, PathBuf}, }; -use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; use anyhow::anyhow; use clap::{Parser, Subcommand}; use defmt_decoder::{ @@ -13,11 +12,14 @@ use defmt_decoder::{ }, DecodeError, Frame, Locations, Table, DEFMT_VERSIONS, }; +use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; use tokio::{ fs, io::{self, AsyncReadExt, Stdin}, - net::TcpStream, select, sync::mpsc::Receiver, + net::TcpStream, + select, + sync::mpsc::Receiver, }; /// Prints defmt-encoded logs to stdout @@ -98,7 +100,7 @@ const READ_BUFFER_SIZE: usize = 1024; #[tokio::main] async fn main() -> anyhow::Result<()> { - let opts = Opts::parse(); + let opts = Opts::parse(); if opts.version { return print_version(); @@ -119,7 +121,7 @@ async fn main() -> anyhow::Result<()> { tx.send(res).await.unwrap(); }) }, - Config::default().with_poll_interval(Duration::from_secs(1)) + 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 @@ -129,18 +131,13 @@ async fn main() -> anyhow::Result<()> { loop { select! { - r = run(opts.clone(), &mut source) => { - if r.is_err() { - return r; - } - }, + r = run(opts.clone(), &mut source) => r?, _ = has_file_changed(&mut rx, &path) => () } } } else { run(opts, &mut source).await } - } async fn has_file_changed(rx: &mut Receiver>, path: &PathBuf) -> bool { @@ -148,10 +145,10 @@ async fn has_file_changed(rx: &mut Receiver>, path: if let Some(Ok(event)) = rx.recv().await { if event.paths.contains(path) { match event.kind { - notify::EventKind::Create(_) | notify::EventKind::Modify(_) => { + notify::EventKind::Create(_) | notify::EventKind::Modify(_) => { break; - }, - _ => () + } + _ => (), } } } @@ -159,7 +156,7 @@ async fn has_file_changed(rx: &mut Receiver>, path: true } -async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> { +async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> { let Opts { elf, json, From 62dddc8ee2c4a90a796f5ae951052be51011ed08 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Tue, 16 Jul 2024 16:47:19 +0200 Subject: [PATCH 3/8] Address comments --- CHANGELOG.md | 2 +- print/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b01fada..78a43287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ 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` +- [#807]: `defmt-print`: Add `watch_elf` flag to allow ELF file reload without restarting `defmt-print` [#852]: https://github.com/knurling-rs/defmt/pull/852 [#847]: https://github.com/knurling-rs/defmt/pull/847 diff --git a/print/src/main.rs b/print/src/main.rs index 12277567..45736a5e 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -47,7 +47,7 @@ struct Opts { #[arg(short = 'V', long)] version: bool, - #[arg[short, long]] + #[arg(short, long)] watch_elf: bool, #[command(subcommand)] From ec2b6efbb51a22adce417b05791786256f0081b1 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Tue, 23 Jul 2024 12:25:59 +0200 Subject: [PATCH 4/8] Address comments, move code to run_and_watch function --- decoder/Cargo.toml | 2 +- print/Cargo.toml | 4 ++-- print/src/main.rs | 47 +++++++++++++++++++++++----------------------- 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/decoder/Cargo.toml b/decoder/Cargo.toml index e8554fa6..814064b0 100644 --- a/decoder/Cargo.toml +++ b/decoder/Cargo.toml @@ -43,7 +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" +alterable_logger = "1" [features] # WARNING: API and wire format subject to change. diff --git a/print/Cargo.toml b/print/Cargo.toml index 89f0cd4e..fd81d0d6 100644 --- a/print/Cargo.toml +++ b/print/Cargo.toml @@ -18,7 +18,7 @@ clap = { version = "4.0", features = ["derive", "env"] } defmt-decoder = { version = "=0.3.11", path = "../decoder", features = [ "unstable", ] } +futures = "0.3" log = "0.4" +notify = "6.1" tokio = { version = "1.38", features = ["full"] } -notify = "6.1.1" -futures = "0.3" diff --git a/print/src/main.rs b/print/src/main.rs index 45736a5e..88b0cfd1 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -13,7 +13,6 @@ use defmt_decoder::{ DecodeError, Frame, Locations, Table, DEFMT_VERSIONS, }; use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher}; - use tokio::{ fs, io::{self, AsyncReadExt, Stdin}, @@ -113,28 +112,7 @@ async fn main() -> anyhow::Result<()> { }; 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(); - }) - }, - 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)?; - - let path = opts.elf.clone().unwrap(); - - loop { - select! { - r = run(opts.clone(), &mut source) => r?, - _ = has_file_changed(&mut rx, &path) => () - } - } + run_and_watch(opts, &mut source).await } else { run(opts, &mut source).await } @@ -156,6 +134,29 @@ async fn has_file_changed(rx: &mut Receiver>, path: true } +async fn run_and_watch(opts: Opts, mut source: &mut Source) -> anyhow::Result<()> { + let (tx, mut rx) = tokio::sync::mpsc::channel(1); + + let mut watcher = RecommendedWatcher::new( + move |res| { + let _ = tx.blocking_send(res); + }, + 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)?; + + let path = opts.elf.clone().unwrap(); + + loop { + select! { + r = run(opts.clone(), &mut source) => r?, + _ = has_file_changed(&mut rx, &path) => () + } + } +} + async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> { let Opts { elf, From 8c4e5d968eaf896d03e902f3ed98b8eeb9f0c6a9 Mon Sep 17 00:00:00 2001 From: Johann Hemmann Date: Mon, 29 Jul 2024 12:49:34 +0200 Subject: [PATCH 5/8] Satisfy clippy --- print/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/print/src/main.rs b/print/src/main.rs index 88b0cfd1..0125a273 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -134,7 +134,7 @@ async fn has_file_changed(rx: &mut Receiver>, path: true } -async fn run_and_watch(opts: Opts, mut source: &mut Source) -> anyhow::Result<()> { +async fn run_and_watch(opts: Opts, source: &mut Source) -> anyhow::Result<()> { let (tx, mut rx) = tokio::sync::mpsc::channel(1); let mut watcher = RecommendedWatcher::new( @@ -151,7 +151,7 @@ async fn run_and_watch(opts: Opts, mut source: &mut Source) -> anyhow::Result<() loop { select! { - r = run(opts.clone(), &mut source) => r?, + r = run(opts.clone(), source) => r?, _ = has_file_changed(&mut rx, &path) => () } } From 2a0b9a1269b576082ffdfdce3864257386aeb19f Mon Sep 17 00:00:00 2001 From: Johann Hemmann Date: Mon, 29 Jul 2024 12:50:53 +0200 Subject: [PATCH 6/8] Simplify --- print/src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/print/src/main.rs b/print/src/main.rs index 0125a273..8e0fad5b 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -165,9 +165,7 @@ async fn run(opts: Opts, source: &mut Source) -> anyhow::Result<()> { host_log_format, show_skipped_frames, verbose, - version: _, - watch_elf: _, - command: _, + .. } = opts; // read and parse elf file From 3863c062ee83faadd4bffcf025355e1c66105418 Mon Sep 17 00:00:00 2001 From: Johann Hemmann Date: Tue, 30 Jul 2024 12:42:44 +0200 Subject: [PATCH 7/8] Canonicalize path --- print/src/main.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/print/src/main.rs b/print/src/main.rs index 8e0fad5b..56236c31 100644 --- a/print/src/main.rs +++ b/print/src/main.rs @@ -122,11 +122,8 @@ async fn has_file_changed(rx: &mut Receiver>, path: loop { if let Some(Ok(event)) = rx.recv().await { if event.paths.contains(path) { - match event.kind { - notify::EventKind::Create(_) | notify::EventKind::Modify(_) => { - break; - } - _ => (), + if let notify::EventKind::Create(_) | notify::EventKind::Modify(_) = event.kind { + break; } } } @@ -137,18 +134,20 @@ async fn has_file_changed(rx: &mut Receiver>, path: async fn run_and_watch(opts: Opts, source: &mut Source) -> anyhow::Result<()> { let (tx, mut rx) = tokio::sync::mpsc::channel(1); + let path = opts.elf.clone().unwrap().canonicalize().unwrap(); + + // We want the elf directory instead of the elf, since some editors remove + // and recreate the file on save which will remove the notifier + let directory_path = path.parent().unwrap(); + let mut watcher = RecommendedWatcher::new( move |res| { let _ = tx.blocking_send(res); }, 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)?; - let path = opts.elf.clone().unwrap(); - loop { select! { r = run(opts.clone(), source) => r?, From 4f1095e136c7766cc4f5f83dc41207f9f4f445e7 Mon Sep 17 00:00:00 2001 From: Johann Hemmann Date: Tue, 3 Sep 2024 14:19:45 +0200 Subject: [PATCH 8/8] Remove `futures` dependency --- print/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/print/Cargo.toml b/print/Cargo.toml index fd81d0d6..bdb023ee 100644 --- a/print/Cargo.toml +++ b/print/Cargo.toml @@ -18,7 +18,6 @@ clap = { version = "4.0", features = ["derive", "env"] } defmt-decoder = { version = "=0.3.11", path = "../decoder", features = [ "unstable", ] } -futures = "0.3" log = "0.4" notify = "6.1" tokio = { version = "1.38", features = ["full"] }