xref: /aosp_15_r20/external/pigweed/pw_log/rust/pw_log_backend_printf/lib.rs (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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