libbpf_rs/print.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
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()
}