| // Copyright 2023 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| //! `pw_log` backend that calls `libc`'s `printf` to emit log messages. This |
| //! module is useful when you have a mixed C/C++ and Rust code base and want a |
| //! simple logging system that leverages an existing `printf` implementation. |
| //! |
| //! *Note*: This uses FFI to call `printf`. This has two implications: |
| //! 1. C/C++ macro processing is not done. If a system's `printf` relies on |
| //! macros, this backend will likely need to be forked to make work. |
| //! 2. FFI calls use `unsafe`. Attempts are made to use `printf` in sound ways |
| //! such as bounding the length of strings explicitly but this is still an |
| //! off-ramp from Rust's safety guarantees. |
| //! |
| //! Varargs marshaling for call to printf is handled through a type expansion |
| //! of a series of traits. It is documented in the [`varargs`] module. |
| //! |
| //! TODO: <pwbug.dev/311232605> - Document how to configure facade backends. |
| |
| #![deny(missing_docs)] |
| |
| pub mod varargs; |
| |
| // Re-export dependences of backend proc macro to be accessed via `$crate::__private`. |
| #[doc(hidden)] |
| pub mod __private { |
| use core::ffi::{c_int, c_uchar}; |
| |
| pub use pw_bytes::concat_static_strs; |
| pub use pw_log_backend_printf_macro::{_pw_log_backend, _pw_logf_backend}; |
| |
| use pw_log_backend_api::LogLevel; |
| |
| pub use crate::varargs::{Arguments, VarArgs}; |
| |
| pub const fn log_level_tag(level: LogLevel) -> &'static str { |
| match level { |
| LogLevel::Debug => "DBG\0", |
| LogLevel::Info => "INF\0", |
| LogLevel::Warn => "WRN\0", |
| LogLevel::Error => "ERR\0", |
| LogLevel::Critical => "CRT\0", |
| LogLevel::Fatal => "FTL\0", |
| } |
| } |
| |
| macro_rules! extend_args { |
| ($head:ty; $next:ty $(,$rest:ty)* $(,)?) => { |
| extend_args!(<$head as VarArgs>::OneMore<$next>; $($rest,)*) |
| }; |
| ($head:ty;) => { |
| $head |
| }; |
| } |
| |
| /// The printf uses its own formatter trait because it needs strings to |
| /// resolve to `%.*s` instead of `%s`. |
| pub trait PrintfFormatter { |
| /// The format specifier for this type. |
| const FORMAT_ARG: &'static str; |
| } |
| |
| /// A helper to declare a [`PrintfFormatter`] trait for a given type. |
| macro_rules! declare_formatter { |
| ($ty:ty, $specifier:literal) => { |
| impl PrintfFormatter for $ty { |
| const FORMAT_ARG: &'static str = $specifier; |
| } |
| }; |
| } |
| |
| declare_formatter!(i32, "d"); |
| declare_formatter!(u32, "u"); |
| declare_formatter!(&str, ".*s"); |
| |
| /// A helper to declare an [`Argument<T>`] trait for a given type. |
| /// |
| /// Useful for cases where `Argument::push_args()` appends a single |
| /// argument of type `T`. |
| macro_rules! declare_simple_argument { |
| ($ty:ty) => { |
| impl Arguments<$ty> for $ty { |
| type PushArg<Head: VarArgs> = Head::OneMore<$ty>; |
| fn push_arg<Head: VarArgs>(head: Head, arg: &$ty) -> Self::PushArg<Head> { |
| // Try expanding `CHECK` which should fail if we've exceeded |
| // 12 arguments in our args tuple. |
| let _ = Self::PushArg::<Head>::CHECK; |
| head.append(*arg) |
| } |
| } |
| }; |
| } |
| |
| declare_simple_argument!(i32); |
| declare_simple_argument!(u32); |
| declare_simple_argument!(char); |
| |
| // &str needs a more complex implementation of [`Argument<T>`] since it needs |
| // to append two arguments. |
| impl Arguments<&str> for &str { |
| type PushArg<Head: VarArgs> = extend_args!(Head; c_int, *const c_uchar); |
| fn push_arg<Head: VarArgs>(head: Head, arg: &&str) -> Self::PushArg<Head> { |
| // Try expanding `CHECK` which should fail if we've exceeded 12 |
| // arguments in our args tuple. |
| let _ = Self::PushArg::<Head>::CHECK; |
| let arg = *arg; |
| head.append(arg.len() as c_int).append(arg.as_ptr().cast()) |
| } |
| } |
| } |
| |
| /// Implements the `pw_log` backend api. |
| #[macro_export] |
| macro_rules! pw_log_backend { |
| ($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{ |
| use $crate::__private as __pw_log_backend_crate; |
| $crate::__private::_pw_log_backend!($log_level, $format_string, $($args),*) |
| }}; |
| } |
| |
| /// Implements the `pw_log` backend api. |
| #[macro_export] |
| macro_rules! pw_logf_backend { |
| ($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{ |
| use $crate::__private as __pw_log_backend_crate; |
| $crate::__private::_pw_logf_backend!($log_level, $format_string, $($args),*) |
| }}; |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::__private::*; |
| use core::ffi::c_int; |
| |
| #[test] |
| fn pushed_args_produce_correct_tuple() { |
| let string = "test"; |
| let args = (); |
| let args = <&str as Arguments<&str>>::push_arg(args, &(string as &str)); |
| let args = <u32 as Arguments<u32>>::push_arg(args, &(2 as u32)); |
| assert_eq!(args, (string.len() as c_int, string.as_ptr().cast(), 2u32)); |
| } |
| } |