blob: 704003e8db70c568d982c0852560aa3349902596 [file] [log] [blame]
// 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));
}
}