Skip to content

Commit 41f96ec

Browse files
committed
zephyr: Implement basic printk-based logging support
Allow clients to use the Rust `log` crate for logging, and provide a `zephyr::set_logger()` that will send those message to printk. This is used when Zephyr's logging is either disable, or enabled, and configured to use minimal logging. Signed-off-by: David Brown <[email protected]>
1 parent 41cf479 commit 41f96ec

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

zephyr/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ zephyr-sys = { version = "3.7.0", path = "../zephyr-sys" }
1515
# Although paste is brought in, it is a compile-time macro, and is not linked into the application.
1616
paste = "1.0"
1717

18+
# cfg-if is a compile-time macro as well, to make conditional compilation a bit easier to write.
19+
cfg-if = "1.0"
20+
21+
# The log create is used if the user desires logging, and calls `set_logger()`.
22+
log = "0.4.22"
23+
1824
[dependencies.fugit]
1925
version = "0.3.7"
2026

zephyr/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212

1313
pub mod align;
1414
pub mod error;
15+
pub mod logging;
1516
pub mod object;
1617
pub mod sync;
1718
pub mod sys;
1819
pub mod time;
1920

2021
pub use error::{Error, Result};
2122

23+
pub use logging::set_logger;
24+
2225
/// Re-exported for local macro use.
2326
pub use paste::paste;
2427

zephyr/src/logging.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//! Rust logging in Zephyr
2+
//!
3+
//! There are a few config settings in Zephyr that affect how logging is done. Zephyr has multiple
4+
//! choices of front ends and backends for the logging. We try to work with a few of these.
5+
//!
6+
//! There are various tradeoffs in terms of code size, and processing time that affect how logging
7+
//! is done. In addition to the tradeoffs in Zephyr, there are also tradeoffs in the Rust world,
8+
//! especially when it comes to formatting.
9+
//!
10+
//! For now, any use of logging in Rust will go through the `log` crate. This brings in the
11+
//! overhead of string formatting. For the most part, due to a semantic mismatch between how Rust
12+
//! does string formatting, and how C does it, we will not defer logging, but generate log strings
13+
//! that will be sent as full strings to the Zephyr logging system.
14+
//!
15+
//! - `CONFIG_LOG`: Global enable of logging in Zephyr.
16+
//! - `CONFIG_LOG_MODE_MINIMAL`: Indicates a minimal printk type of logging.
17+
//!
18+
//! At this time, we will provide two loggers. One is based on using the underlying printk
19+
//! mechanism, and if enabled, will send log messages directly to printk. This roughly corresponds
20+
//! to the minimal logging mode of Zephyr, but also works if logging is entirely disabled.
21+
//!
22+
//! The other log backend uses the logging infrastructure to log simple messages to the C logger.
23+
//! At this time, these require allocation for the string formatting, although the allocation will
24+
//! generally be short lived. A good future task will be to make the string formatter format
25+
//! directly into the log buffer used by Zephyr.
26+
27+
use log::{LevelFilter, Log, SetLoggerError};
28+
29+
cfg_if::cfg_if! {
30+
if #[cfg(all(CONFIG_PRINTK,
31+
any(not(CONFIG_LOG),
32+
all(CONFIG_LOG, CONFIG_LOG_MODE_MINIMAL))))]
33+
{
34+
// If we either have no logging, or it is minimal, and we have printk, we can do the printk
35+
// logging handler.
36+
mod impl_printk;
37+
pub use impl_printk::set_logger;
38+
} else if #[cfg(all(CONFIG_LOG,
39+
not(CONFIG_LOG_MODE_MINIMAL),
40+
CONFIG_RUST_ALLOC))]
41+
{
42+
// Otherwise, if we have logging and allocation, and not minimal, we can use the Zephyr
43+
// logging backend. Doing this without allocation is currently a TODO:
44+
todo!("Zephyr logging not yet supported");
45+
} else {
46+
/// No lagging is possible, provide an empty handler that does nothing.
47+
///
48+
/// It could be nice to print a message somewhere, but this is only available when there is
49+
/// no where we can send messages.
50+
pub unsafe fn set_logger() -> Result<(), SetLoggerError> {
51+
Ok(())
52+
}
53+
}
54+
}
55+
56+
// The Rust logging system has different entry points based on whether or not we are on a target
57+
// with atomic pointers. We will provide a single function for this, which will be safe or unsafe
58+
// depending on this. The safety has to do with initialization order, and as long as this is called
59+
// before any other threads run, it should be safe.
60+
//
61+
// TODO: Allow the default level to be set through Kconfig.
62+
cfg_if::cfg_if! {
63+
if #[cfg(target_has_atomic = "ptr")] {
64+
unsafe fn set_logger_internal(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
65+
log::set_logger(logger)?;
66+
log::set_max_level(LevelFilter::Info);
67+
Ok(())
68+
}
69+
} else {
70+
unsafe fn set_logger_internal(logger: &'static dyn Log) -> Result<(), SetLoggerError> {
71+
log::set_logger_racy(logger)?;
72+
log::set_max_level_racy(LevelFilter::Info);
73+
Ok(())
74+
}
75+
}
76+
}

zephyr/src/logging/impl_printk.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//! Logging through printk
2+
//!
3+
//! This module implements a log handler (for the [`log`] crate) that logs messages through Zephyr's
4+
//! printk mechanism.
5+
//!
6+
//! Currently, filtering is global, and set to Info.
7+
8+
use log::{Log, Metadata, Record, SetLoggerError};
9+
10+
use crate::printkln;
11+
12+
/// A simple log handler, built around printk.
13+
struct PrintkLogger;
14+
15+
impl Log for PrintkLogger {
16+
// For now, everything is just available.
17+
fn enabled(&self, _metadata: &Metadata<'_>) -> bool {
18+
true
19+
}
20+
21+
// Print out the log message, using printkln.
22+
//
23+
// Normal caveats behind printkln apply, if `RUST_PRINTK_SYNC` is not defined, then all message
24+
// printing will be racy. Otherwise, the message will be broken into small chunks that are each
25+
// printed atomically.
26+
fn log(&self, record: &Record<'_>) {
27+
printkln!("{}:{}: {}",
28+
record.level(),
29+
record.target(),
30+
record.args());
31+
}
32+
33+
// Flush is not needed.
34+
fn flush(&self) {
35+
}
36+
}
37+
38+
static PRINTK_LOGGER: PrintkLogger = PrintkLogger;
39+
40+
/// Set the log handler to log messages through printk in Zephyr.
41+
///
42+
/// This is unsafe due to racy issues in the log framework on targets that do not support atomic
43+
/// pointers.
44+
pub unsafe fn set_logger() -> Result<(), SetLoggerError> {
45+
super::set_logger_internal(&PRINTK_LOGGER)
46+
}

0 commit comments

Comments
 (0)