| 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<libbpf_sys::libbpf_print_level> 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<Mutex<Option<(PrintLevel, PrintCallback)>>> = |
| 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 <https://github.com/rust-lang/rust-bindgen/issues/2631> |
| 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() |
| } |