diff --git a/Cargo.toml b/Cargo.toml index e43030e1..71389861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "decoder", "decoder/defmt-json-schema", "defmt", + "defmt-log", "macros", "parser", "print", diff --git a/book/src/setup.md b/book/src/setup.md index a355db2c..4980b793 100644 --- a/book/src/setup.md +++ b/book/src/setup.md @@ -63,4 +63,10 @@ To learn how to enable other logging levels and filters logs per modules read th ### Memory use -When in a tight memory situation and logging over RTT, the buffer size (default: 1024 bytes) can be configured with the `DEFMT_RTT_BUFFER_SIZE` environment variable. Use a power of 2 for best performance. \ No newline at end of file +When in a tight memory situation and logging over RTT, the buffer size (default: 1024 bytes) can be configured with the `DEFMT_RTT_BUFFER_SIZE` environment variable. Use a power of 2 for best performance. + +### Use on Windows/macOS/Linux + +You can use `defmt` without string-interning by enabling the `no-interning` feature. This will mean that format strings are included as literal strings and not hidden in the symbol name. + +You can optionally send the output to the `log` crate by enabling the `use-log-crate` feature. diff --git a/defmt-log/Cargo.toml b/defmt-log/Cargo.toml new file mode 100644 index 00000000..72bda360 --- /dev/null +++ b/defmt-log/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "defmt-log" +version = "0.1.0" +edition = "2021" + +[dependencies] +defmt = { version = "0.3", path = "../defmt", features = ["no-interning"] } +critical-section = "1.1" +log = "0.4.22" diff --git a/defmt-log/src/lib.rs b/defmt-log/src/lib.rs new file mode 100644 index 00000000..db85412d --- /dev/null +++ b/defmt-log/src/lib.rs @@ -0,0 +1,74 @@ +//! An implementation of the defmt logging traits, but using the `log` crate for output + +use std::sync::{Condvar, Mutex}; + +#[defmt::global_logger] +struct FakeLogger; + +#[derive(PartialEq, Eq)] +enum State { + Waiting, + AcquiredNeedAddr, + WantArgs, +} + +struct Context { + state: State +} + +impl Context { + const fn new() -> Context { + Context { + state: State::Waiting + } + } +} + +static CONTEXT: (Mutex, Condvar) = (Mutex::new(Context::new()), Condvar::new()); + +unsafe impl defmt::Logger for FakeLogger { + fn acquire() { + log::info!("Acquiring {:?}", std::thread::current().id()); + let mut ctx = CONTEXT.0.lock().unwrap(); + while ctx.state != State::Waiting { + // sit on the condvar because only one thread can grab the lock + ctx = CONTEXT.1.wait(ctx).unwrap(); + } + // cool, we can take it + ctx.state = State::AcquiredNeedAddr; + } + + unsafe fn flush() { + log::info!("Flushing {:?}", std::thread::current().id()); + } + + unsafe fn release() { + log::info!("Releasing {:?}", std::thread::current().id()); + let mut ctx = CONTEXT.0.lock().unwrap(); + ctx.state = State::Waiting; + CONTEXT.1.notify_one(); + } + + unsafe fn write(bytes: &[u8]) { + use std::convert::TryInto; + log::info!("Bytes {:?} {:02x?}", std::thread::current().id(), bytes); + let mut ctx = CONTEXT.0.lock().unwrap(); + match ctx.state { + State::Waiting => panic!("Unlocked write!!"), + State::AcquiredNeedAddr => { + let addr = &bytes[0..std::mem::size_of::()]; + let addr: usize = usize::from_le_bytes(addr.try_into().unwrap()); + let ptr = addr as *const &'static str; + let format_str: &'static str = unsafe { ptr.read() }; + log::info!("Format string: {}", format_str); + ctx.state = State::WantArgs; + }, + State::WantArgs => { + log::info!("Arg: {:02x?}", bytes); + }, + } + } +} + +#[export_name = "_defmt_timestamp"] +fn defmt_timestamp(_: defmt::Formatter<'_>) {} diff --git a/defmt/Cargo.toml b/defmt/Cargo.toml index d64a4a2e..54138d54 100644 --- a/defmt/Cargo.toml +++ b/defmt/Cargo.toml @@ -21,6 +21,8 @@ version = "0.3.10" alloc = [] avoid-default-panic = [] ip_in_core = [] +no-interning = [ "defmt-macros/no-interning" ] +use-log-crate = [] # Encoding feature flags. These should only be set by end-user crates, not by library crates. # diff --git a/defmt/src/export/mod.rs b/defmt/src/export/mod.rs index c0cd7be3..3c03b443 100644 --- a/defmt/src/export/mod.rs +++ b/defmt/src/export/mod.rs @@ -99,7 +99,7 @@ pub fn timestamp(fmt: crate::Formatter<'_>) { } /// Returns the interned string at `address`. -pub fn make_istr(address: u16) -> Str { +pub fn make_istr(address: usize) -> Str { Str { address } } diff --git a/defmt/src/formatter.rs b/defmt/src/formatter.rs index d153a7bc..f461b760 100644 --- a/defmt/src/formatter.rs +++ b/defmt/src/formatter.rs @@ -11,6 +11,6 @@ pub struct Formatter<'a> { /// [`intern!`]: macro.intern.html #[derive(Clone, Copy)] pub struct Str { - /// 16-bit address - pub(crate) address: u16, + /// The address of the interned string + pub(crate) address: usize, } diff --git a/macros/Cargo.toml b/macros/Cargo.toml index 6abf8179..b94f9e74 100644 --- a/macros/Cargo.toml +++ b/macros/Cargo.toml @@ -15,6 +15,8 @@ proc-macro = true [features] # WARNING: for internal use only, not covered by semver guarantees unstable-test = [] +# Turns off string interning +no-interning = [] [dependencies] defmt-parser = { version = "=0.4.1", path = "../parser" } diff --git a/macros/src/construct.rs b/macros/src/construct.rs index 6dbe9617..8bbe74b8 100644 --- a/macros/src/construct.rs +++ b/macros/src/construct.rs @@ -47,7 +47,7 @@ pub(crate) fn interned_string( let var_item = static_variable(&var_name, string, tag, prefix); quote!({ #var_item - &#var_name as *const u8 as u16 + &#var_name as *const _ as usize }) }; @@ -61,17 +61,24 @@ pub(crate) fn interned_string( /// under macos: ".defmt," + 16 character hex digest of symbol's hash /// otherwise: ".defmt." + prefix + symbol pub(crate) fn linker_section(for_macos: bool, prefix: Option<&str>, symbol: &str) -> String { - let mut sub_section = if let Some(prefix) = prefix { - format!(".{prefix}.{symbol}") + let symbol_hash = hash(symbol); + let suffix = if for_macos { + // "mach-o section specifier requires a section whose length is between 1 and 16 characters." + if let Some(prefix) = prefix { + let intermediate = format!("{prefix}_{symbol_hash:08x}"); + format!(",{intermediate:.16}") + } else { + format!(",{symbol_hash:08x}") + } } else { - format!(".{symbol}") + if let Some(prefix) = prefix { + format!(".{prefix}.{symbol_hash:x}") + } else { + format!(".{symbol_hash:x}") + } }; - if for_macos { - sub_section = format!(",{:x}", hash(&sub_section)); - } - - format!(".defmt{sub_section}") + format!(".defmt{suffix}") } pub(crate) fn static_variable( @@ -83,13 +90,20 @@ pub(crate) fn static_variable( let sym_name = mangled_symbol_name(tag, data); let section = linker_section(false, prefix, &sym_name); let section_for_macos = linker_section(true, prefix, &sym_name); - - quote!( - #[cfg_attr(target_os = "macos", link_section = #section_for_macos)] - #[cfg_attr(not(target_os = "macos"), link_section = #section)] - #[export_name = #sym_name] - static #name: u8 = 0; - ) + if cfg!(feature = "no-interning") { + quote!( + #[cfg_attr(target_os = "macos", link_section = #section_for_macos)] + #[cfg_attr(not(target_os = "macos"), link_section = #section)] + static #name: &'static str = #sym_name; + ) + } else { + quote!( + #[cfg_attr(target_os = "macos", link_section = #section_for_macos)] + #[cfg_attr(not(target_os = "macos"), link_section = #section)] + #[export_name = #sym_name] + static #name: u8 = 0; + ) + } } pub(crate) fn string_literal(content: &str) -> LitStr { diff --git a/macros/src/function_like/internp.rs b/macros/src/function_like/internp.rs index c75a3301..a0a3fb22 100644 --- a/macros/src/function_like/internp.rs +++ b/macros/src/function_like/internp.rs @@ -13,14 +13,21 @@ pub(crate) fn expand(args: TokenStream) -> TokenStream { let section_for_macos = construct::linker_section(true, prefix, &sym_name); let var_addr = if cfg!(feature = "unstable-test") { - quote!({ defmt::export::fetch_add_string_index() as u16 }) + quote!({ defmt::export::fetch_add_string_index() as usize }) + } else if cfg!(feature = "no-interning") { + quote!({ + #[cfg_attr(target_os = "macos", link_section = #section_for_macos)] + #[cfg_attr(not(target_os = "macos"), link_section = #section)] + static S: &'static str = #sym_name; + &S as *const _ as usize + }) } else { quote!({ #[cfg_attr(target_os = "macos", link_section = #section_for_macos)] #[cfg_attr(not(target_os = "macos"), link_section = #section)] #[export_name = #sym_name] static S: u8 = 0; - &S as *const u8 as u16 + &S as *const _ as usize }) };