1 // Copyright 2023 The Pigweed Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not 4 // use this file except in compliance with the License. You may obtain a copy of 5 // the License at 6 // 7 // https://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 // License for the specific language governing permissions and limitations under 13 // the License. 14 15 //! `pw_log` backend that calls `libc`'s `printf` to emit log messages. This 16 //! module is useful when you have a mixed C/C++ and Rust code base and want a 17 //! simple logging system that leverages an existing `printf` implementation. 18 //! 19 //! *Note*: This uses FFI to call `printf`. This has two implications: 20 //! 1. C/C++ macro processing is not done. If a system's `printf` relies on 21 //! macros, this backend will likely need to be forked to make work. 22 //! 2. FFI calls use `unsafe`. Attempts are made to use `printf` in sound ways 23 //! such as bounding the length of strings explicitly but this is still an 24 //! off-ramp from Rust's safety guarantees. 25 //! 26 //! Varargs marshaling for call to printf is handled through a type expansion 27 //! of a series of traits. It is documented in the [`varargs`] module. 28 //! 29 //! TODO: <pwbug.dev/311232605> - Document how to configure facade backends. 30 31 #![deny(missing_docs)] 32 33 pub mod varargs; 34 35 // Re-export dependences of backend proc macro to be accessed via `$crate::__private`. 36 #[doc(hidden)] 37 pub mod __private { 38 use core::ffi::{c_int, c_uchar}; 39 40 pub use pw_bytes::concat_static_strs; 41 pub use pw_format_core::{PrintfHexFormatter, PrintfUpperHexFormatter}; 42 pub use pw_log_backend_printf_macro::{_pw_log_backend, _pw_logf_backend}; 43 44 use pw_log_backend_api::LogLevel; 45 46 pub use crate::varargs::{Arguments, VarArgs}; 47 log_level_tag(level: LogLevel) -> &'static str48 pub const fn log_level_tag(level: LogLevel) -> &'static str { 49 match level { 50 LogLevel::Debug => "DBG\0", 51 LogLevel::Info => "INF\0", 52 LogLevel::Warn => "WRN\0", 53 LogLevel::Error => "ERR\0", 54 LogLevel::Critical => "CRT\0", 55 LogLevel::Fatal => "FTL\0", 56 } 57 } 58 59 macro_rules! extend_args { 60 ($head:ty; $next:ty $(,$rest:ty)* $(,)?) => { 61 extend_args!(<$head as VarArgs>::OneMore<$next>; $($rest,)*) 62 }; 63 ($head:ty;) => { 64 $head 65 }; 66 } 67 68 /// The printf uses its own formatter trait because it needs strings to 69 /// resolve to `%.*s` instead of `%s`. 70 /// 71 /// The default [`PrintfHexFormatter`] and [`PrintfUpperHexFormatter`] are 72 /// used since they are not supported by strings. 73 pub trait PrintfFormatter { 74 /// The format specifier for this type. 75 const FORMAT_ARG: &'static str; 76 } 77 78 /// A helper to declare a [`PrintfFormatter`] trait for a given type. 79 macro_rules! declare_formatter { 80 ($ty:ty, $specifier:literal) => { 81 impl PrintfFormatter for $ty { 82 const FORMAT_ARG: &'static str = $specifier; 83 } 84 }; 85 } 86 87 declare_formatter!(i32, "d"); 88 declare_formatter!(u32, "u"); 89 declare_formatter!(&str, ".*s"); 90 91 /// A helper to declare an [`Argument<T>`] trait for a given type. 92 /// 93 /// Useful for cases where `Argument::push_args()` appends a single 94 /// argument of type `T`. 95 macro_rules! declare_simple_argument { 96 ($ty:ty) => { 97 impl Arguments<$ty> for $ty { 98 type PushArg<Head: VarArgs> = Head::OneMore<$ty>; 99 fn push_arg<Head: VarArgs>(head: Head, arg: &$ty) -> Self::PushArg<Head> { 100 // Try expanding `CHECK` which should fail if we've exceeded 101 // 12 arguments in our args tuple. 102 let _ = Self::PushArg::<Head>::CHECK; 103 head.append(*arg) 104 } 105 } 106 }; 107 } 108 109 declare_simple_argument!(i32); 110 declare_simple_argument!(u32); 111 declare_simple_argument!(char); 112 113 // &str needs a more complex implementation of [`Argument<T>`] since it needs 114 // to append two arguments. 115 impl Arguments<&str> for &str { 116 type PushArg<Head: VarArgs> = extend_args!(Head; c_int, *const c_uchar); push_arg<Head: VarArgs>(head: Head, arg: &&str) -> Self::PushArg<Head>117 fn push_arg<Head: VarArgs>(head: Head, arg: &&str) -> Self::PushArg<Head> { 118 // Try expanding `CHECK` which should fail if we've exceeded 12 119 // arguments in our args tuple. 120 #[allow(clippy::let_unit_value)] 121 let _ = Self::PushArg::<Head>::CHECK; 122 let arg = *arg; 123 head.append(arg.len() as c_int).append(arg.as_ptr().cast()) 124 } 125 } 126 } 127 128 /// Implements the `pw_log` backend api. 129 #[macro_export] 130 macro_rules! pw_log_backend { 131 ($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{ 132 use $crate::__private as __pw_log_backend_crate; 133 $crate::__private::_pw_log_backend!($log_level, $format_string, $($args),*) 134 }}; 135 } 136 137 /// Implements the `pw_log` backend api. 138 #[macro_export] 139 macro_rules! pw_logf_backend { 140 ($log_level:expr, $format_string:literal $(, $args:expr)* $(,)?) => {{ 141 use $crate::__private as __pw_log_backend_crate; 142 $crate::__private::_pw_logf_backend!($log_level, $format_string, $($args),*) 143 }}; 144 } 145 146 #[cfg(test)] 147 mod tests { 148 use super::__private::*; 149 use core::ffi::c_int; 150 151 #[test] pushed_args_produce_correct_tuple()152 fn pushed_args_produce_correct_tuple() { 153 let string = "test"; 154 let args = (); 155 let args = <&str as Arguments<&str>>::push_arg(args, &(string as &str)); 156 let args = <u32 as Arguments<u32>>::push_arg(args, &2u32); 157 assert_eq!(args, (string.len() as c_int, string.as_ptr().cast(), 2u32)); 158 } 159 } 160