use std::ffi::c_char; use std::ffi::c_int; use std::ffi::c_void; use std::io; use std::io::Write; use std::mem; use std::sync::Mutex; use crate::util::LazyLock; /// An enum representing the different supported print levels. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] #[repr(u32)] pub enum PrintLevel { /// Print warnings and more severe messages. Warn = libbpf_sys::LIBBPF_WARN, /// Print general information and more severe messages. Info = libbpf_sys::LIBBPF_INFO, /// Print debug information and more severe messages. Debug = libbpf_sys::LIBBPF_DEBUG, } impl From for PrintLevel { fn from(level: libbpf_sys::libbpf_print_level) -> Self { match level { libbpf_sys::LIBBPF_WARN => Self::Warn, libbpf_sys::LIBBPF_INFO => Self::Info, libbpf_sys::LIBBPF_DEBUG => Self::Debug, // shouldn't happen, but anything unknown becomes the highest level _ => Self::Warn, } } } /// The type of callback functions suitable for being provided to [`set_print`]. pub type PrintCallback = fn(PrintLevel, String); /// Mimic the default print functionality of libbpf. This way if the user calls `get_print` when no /// previous callback had been set, with the intention of restoring it, everything will behave as /// expected. fn default_callback(_lvl: PrintLevel, msg: String) { let _ = io::stderr().write(msg.as_bytes()); } // While we can't say that set_print is thread-safe, because we shouldn't assume that of // libbpf_set_print, we should still make sure that things are sane on the rust side of things. // Therefore we are using a lock to keep the log level and the callback in sync. // // We don't do anything that can panic with the lock held, so we'll unconditionally unwrap() when // locking the mutex. // // Note that default print behavior ignores debug messages. static PRINT_CB: LazyLock>> = LazyLock::new(|| Mutex::new(Some((PrintLevel::Info, default_callback)))); extern "C" fn outer_print_cb( level: libbpf_sys::libbpf_print_level, fmtstr: *const c_char, // bindgen generated va_list type varies on different platforms, so just use void pointer // instead. It's safe because this argument is always a pointer. // The pointer of this function would be transmuted and passing to libbpf_set_print below. // See va_list: *mut c_void, ) -> c_int { let level = level.into(); if let Some((min_level, func)) = { *PRINT_CB.lock().unwrap() } { if level <= min_level { let msg = match unsafe { vsprintf::vsprintf(fmtstr, va_list) } { Ok(s) => s, Err(e) => format!("Failed to parse libbpf output: {e}"), }; func(level, msg); } } 0 // return value is ignored by libbpf } /// Set a callback to receive log messages from libbpf, instead of printing them to stderr. /// /// # Arguments /// /// * `callback` - Either a tuple `(min_level, function)` where `min_level` is the lowest priority /// log message to handle, or `None` to disable all printing. /// /// This overrides (and is overridden by) [`ObjectBuilder::debug`][crate::ObjectBuilder::debug] /// /// # Examples /// /// To pass all messages to the `log` crate: /// /// ``` /// use libbpf_rs::{PrintLevel, set_print}; /// /// fn print_to_log(level: PrintLevel, msg: String) { /// match level { /// PrintLevel::Debug => log::debug!("{}", msg), /// PrintLevel::Info => log::info!("{}", msg), /// PrintLevel::Warn => log::warn!("{}", msg), /// } /// } /// /// set_print(Some((PrintLevel::Debug, print_to_log))); /// ``` /// /// To disable printing completely: /// /// ``` /// use libbpf_rs::set_print; /// set_print(None); /// ``` /// /// To temporarliy suppress output: /// /// ``` /// use libbpf_rs::set_print; /// /// let prev = set_print(None); /// // do things quietly /// set_print(prev); /// ``` pub fn set_print( mut callback: Option<(PrintLevel, PrintCallback)>, ) -> Option<(PrintLevel, PrintCallback)> { // # Safety // outer_print_cb has the same function signature as libbpf_print_fn_t #[allow(clippy::missing_transmute_annotations)] let real_cb: libbpf_sys::libbpf_print_fn_t = unsafe { Some(mem::transmute(outer_print_cb as *const ())) }; let real_cb: libbpf_sys::libbpf_print_fn_t = callback.as_ref().and(real_cb); mem::swap(&mut callback, &mut *PRINT_CB.lock().unwrap()); unsafe { libbpf_sys::libbpf_set_print(real_cb) }; callback } /// Return the current print callback and level. /// /// # Examples /// /// To temporarily suppress output: /// /// ``` /// use libbpf_rs::{get_print, set_print}; /// /// let prev = get_print(); /// set_print(None); /// // do things quietly /// set_print(prev); /// ``` pub fn get_print() -> Option<(PrintLevel, PrintCallback)> { *PRINT_CB.lock().unwrap() }