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
use crate::*;
use lazy_static::lazy_static;
use std::io::{self, Write};
use std::os::raw::c_char;
use std::sync::Mutex;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[repr(u32)]
pub enum PrintLevel {
Warn = libbpf_sys::LIBBPF_WARN,
Info = libbpf_sys::LIBBPF_INFO,
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,
}
}
}
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.
lazy_static! {
static ref PRINT_CB: Mutex<Option<(PrintLevel, PrintCallback)>> =
Mutex::new(Some((PrintLevel::Info, default_callback)));
}
extern "C" fn outer_print_cb(
level: libbpf_sys::libbpf_print_level,
fmtstr: *const c_char,
va_list: *mut libbpf_sys::__va_list_tag,
) -> i32 {
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`]
///
/// # Examples
///
/// To pass all messages to the `log` crate:
///
/// ```
/// use log;
/// 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)> {
let real_cb: libbpf_sys::libbpf_print_fn_t = callback.as_ref().and(Some(outer_print_cb));
std::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 temporarliy 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()
}