Skip to content

Draft: Add log support #890

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

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"decoder",
"decoder/defmt-json-schema",
"defmt",
"defmt-log",
"macros",
"parser",
"print",
Expand Down
8 changes: 7 additions & 1 deletion book/src/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
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.
9 changes: 9 additions & 0 deletions defmt-log/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
74 changes: 74 additions & 0 deletions defmt-log/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Context>, 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::<usize>()];
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<'_>) {}
2 changes: 2 additions & 0 deletions defmt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
#
Expand Down
2 changes: 1 addition & 1 deletion defmt/src/export/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}

Expand Down
4 changes: 2 additions & 2 deletions defmt/src/formatter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
2 changes: 2 additions & 0 deletions macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
46 changes: 30 additions & 16 deletions macros/src/construct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
};

Expand All @@ -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(
Expand All @@ -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 {
Expand Down
11 changes: 9 additions & 2 deletions macros/src/function_like/internp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
})
};

Expand Down
Loading