Skip to content

Commit

Permalink
Using a Display argument for lazy evaluation
Browse files Browse the repository at this point in the history
  • Loading branch information
benfogle committed Nov 4, 2021
1 parent be69bd4 commit e32ccb7
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 22 deletions.
40 changes: 22 additions & 18 deletions libbpf-rs/src/print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::*;
use std::io::{self, Write};
use std::os::raw::c_char;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::fmt;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u32)]
Expand All @@ -23,16 +24,31 @@ impl From<libbpf_sys::libbpf_print_level> for PrintLevel {
}
}

pub type PrintCallback = fn(PrintLevel, &str);
pub type PrintCallback = fn(PrintLevel, &dyn fmt::Display);

struct Vsprintf {
fmtstr: *const c_char,
va_list: *mut libbpf_sys::__va_list_tag,
}

impl fmt::Display for Vsprintf {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let msg = match unsafe { vsprintf::vsprintf(self.fmtstr, self.va_list) } {
Ok(s) => s,
Err(e) => format!("Failed to parse libbpf output: {}", e),
};
f.write_str(&msg)
}
}

/// Mimic the default print functionality of libbpf. This way if the user calls `set_print` when no
/// previous callback had been set and then tries to restore it, it will appear to work correctly.
fn default_callback(lvl: PrintLevel, msg: &str) {
fn default_callback(lvl: PrintLevel, msg: &dyn fmt::Display) {
if lvl == PrintLevel::Debug {
return;
}

let _ = io::stderr().write(msg.as_bytes());
let _ = io::stderr().write(msg.to_string().as_bytes());
}

fn from_ptr(ptr: *mut ()) -> PrintCallback {
Expand All @@ -42,27 +58,15 @@ fn from_ptr(ptr: *mut ()) -> PrintCallback {
// There is no AtomicFnPtr. This is a workaround.
static PRINT_CB: AtomicPtr<()> = AtomicPtr::new(default_callback as *mut ());

/// libbpf's default cb uses vfprintf's return code...which is ignored everywhere. Mimic that for
/// completeness
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 cb = from_ptr(PRINT_CB.load(Ordering::Relaxed));
match unsafe { vsprintf::vsprintf(fmtstr, va_list) } {
Ok(s) => {
cb(level.into(), &s);
s.len() as i32
}
Err(e) => {
cb(
PrintLevel::Warn,
&format!("Failed to parse libbpf output: {}", e),
);
-1
}
}
let msg = Vsprintf { fmtstr, va_list };
cb(level.into(), &msg);
1 // return value is ignored by libbpf
}

/// Set a callback to receive log messages from libbpf, instead of printing them to stderr. Returns
Expand Down
9 changes: 5 additions & 4 deletions libbpf-rs/tests/test_print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,20 @@
use libbpf_rs::{set_print, ObjectBuilder, PrintLevel};
use serial_test::serial;
use std::sync::atomic::{AtomicBool, Ordering};
use std::fmt::Display;

#[test]
#[serial]
fn test_set_print() {
static CORRECT_LEVEL: AtomicBool = AtomicBool::new(false);
static CORRECT_MESSAGE: AtomicBool = AtomicBool::new(false);

fn callback(level: PrintLevel, msg: &str) {
fn callback(level: PrintLevel, msg: &dyn Display) {
if level == PrintLevel::Warn {
CORRECT_LEVEL.store(true, Ordering::Relaxed);
}

if msg.starts_with("libbpf: ") {
if msg.to_string().starts_with("libbpf: ") {
CORRECT_MESSAGE.store(true, Ordering::Relaxed);
}
}
Expand All @@ -39,10 +40,10 @@ fn test_set_print() {
#[test]
#[serial]
fn test_set_restore_print() {
fn callback1(_: PrintLevel, _: &str) {
fn callback1(_: PrintLevel, _: &dyn Display) {
println!("one");
}
fn callback2(_: PrintLevel, _: &str) {
fn callback2(_: PrintLevel, _: &dyn Display) {
println!("two");
}

Expand Down

0 comments on commit e32ccb7

Please sign in to comment.