LIBFEBUG.RS(3) Library Functions Manual LIBFEBUG.RS(3)

febug::start(), febug::Wrapper, febug::Wrappable, febug::StaticWrappable, febug::GLOBAL_CONTROLLED_SOCKET, febug::FORMATTERSUser-space debugfs ABI wrapper library for Rust

[dependencies]
febug = "1.0.0-2-g9ab5a53e"


env!("FEBUG_DONT")?
env!("FEBUG_SOCKET") = "/run/febug.sock"
env!("FEBUG_SIGNUM") = SIGUSR2

env::var("FEBUG_DONT");
env::var("FEBUG_SOCKET");


static GLOBAL_CONTROLLED_SOCKET:
AtomicI32 = -1
;


fn febug::start();

fn febug::start_raw(path: &[u8]);

extern "C" fn debug_handler(_: c_int);

bool fn febug::install_handler();

bool fn febug::install_handler_signal(signal: u8);

fn febug::end();

struct Type(); impl From<TypeId> for Type;
impl From<u64> for Type;
static febug::FORMATTERS:
Lazy<Mutex<BTreeMap<TypeId, fn(&mut File, usize)>>>
;


trait febug::StaticWrappable: 'static;

Wrapper<Self> fn febug::StaticWrappable::wrap(&self, name: Arguments<'_>);

Wrapper<Self> fn febug::StaticWrappable::wrap_signal(&self, signal: u8, name: Arguments<'_>);


trait febug::Wrappable;

Wrapper<Self> fn febug::Wrappable::wrap(&self, tp: u64, name: Arguments<'_>);

Wrapper<Self> fn febug::Wrappable::wrap_signal(&self, tp: u64, signal: u8, name: Arguments<'_>);


struct febug::Wrapper<T> { /* … */ };

Wrapper<T> fn febug::Wrapper::new(&self, tp: u64, data:argument, name: fmt::Arguments, name: Arguments<'_>);

Wrapper<T> fn febug::Wrapper::new_signal(&self, tp: u64, data:argument, signal: u8, name: fmt::Arguments, name: Arguments<'_>);


struct febug::abi::FebugMessage;
struct febug::abi::StopFebugMessage;
struct febug::abi::AttnFebugMessage;

Simplifies writing Rust programs debuggable with febug(8) by presenting a high-level interface to febug-abi(5).

There are three compile-time environment variables that allow customising libfebug.rs behaviour:

If set, all functions turn into no-ops; this is intended as a way to easily disable febug(8) integration completely on release builds.
The signal to request from febug(8) when using (). Defaults to SIGUSR2.
The path to connect to febug(8) on. Defaults to /run/febug.sock.

There are two environment variables that allow a user to customise its behaviour:

If set, don't try to connect to febug(8), so all library functions become no-ops.
If set, use its value instead of the built-in FEBUG_SOCKET to connect to febug(8).

To be debugged, a program needs to, first, call () (likely via (), which simply passes b"/run/febug.sock" thereto) to connect to febug(8), which, if successful, will set febug::GLOBAL_CONTROLLED_SOCKET to the connection's file descriptor.

The program needs to install () (or a wrapper around it) as the signal handler for FEBUG_SIGNUM (and any other signals, if different ones are explicitly requested); if notifications are disabled (by requesting SIGKILL), some event loop that answers on febug::GLOBAL_CONTROLLED_SOCKET must be in place. It's a no-op if febug::GLOBAL_CONTROLLED_SOCKET is -1. A convenience () function is provided, doing just that, and returning if the handler was installed.

The program should register handlers for types of variables it wishes to handle by adding entries to febug::FORMATTERS. If no handler was registered for a type, or the lock was poisoned, () will write a generic "not found" message. The key is febug::Type, which can be constructed from std::any::TypeId corresponding to the debugged type or just an integer. The handler takes the write end of the pipe as the first argument, and the variable ID as the second. It's a no-op if febug::GLOBAL_CONTROLLED_SOCKET is -1.

At any time, when the program wishes to expose a variable, it can construct a febug::Wrapper (likely via one of the convenience febug::StaticWrappable or febug::Wrappable traits), which will send a febug_message with the specified type and signal numbers (defaulting to SIGUSR2), ID equal to the address of the data argument, and name formatted. It's a no-op if febug::GLOBAL_CONTROLLED_SOCKET is -1.

When dropped, febug::Wrapper will send a stop_febug_message. It's a no-op if febug::GLOBAL_CONTROLLED_SOCKET is -1.

When it wishes to stop being debugged, the program may call () which will shut and reset febug::GLOBAL_CONTROLLED_SOCKET. The program may omit this if it'd be the last thing it did before exiting, since the kernel will close all file descriptors and free all mappings anyway.

The following program spawns 10 threads, each successive one sorting a longer subsection of a String, but waits a quarter-second between each comparison; the String for each thread and the amount of comparisons can be inspected via a febug(8) mount:

// SPDX-License-Identifier: MIT


extern crate febug;

use std::sync::atomic::{AtomicUsize, Ordering};
use febug::{StaticWrappable, Wrapper};
use std::time::Duration;
use std::any::TypeId;
use std::io::Write;
use std::fs::File;
use std::thread;


static COMPARISONS: AtomicUsize = AtomicUsize::new(0);


fn main() {
    febug::start();
    if febug::install_handler() {
        // Normal registration by TypeId, variables use .wrap(...)
        let mut fmt = febug::FORMATTERS.lock().unwrap();
        fmt.insert(TypeId::of::<String>().into(), |of, vid| {
            let data = unsafe { &*(vid as *const String) };

            let _ = of.write_all(data.as_bytes());
            let _ = of.write_all(b"\n");
        });

        // Custom registration with an explicit type number and Wrapper::new()
        fmt.insert(0.into(), |of: &mut File, _| {
            let _ = write!(of, "{}\n", COMPARISONS.load(Ordering::Relaxed));
        });
    }

    let _comparisons_wrapper = Wrapper::new(0, &COMPARISONS, format_args!("comparisons"));


    let threads = (0..10)
        .map(|i| {
            thread::spawn(move || {
                let mut sorteing = "The quick red fox jumps over the lazy brown \
                                    dog... tHE QUICK RED FOX JUMPS OVER THE \
                                    LAZY BROWN DOG!!"
                                       [0..(i + 1) * 10]
                    .to_string();
                let _sorteing_w = sorteing.wrap(format_args!("cool_data_{}", i));

                unsafe { sorteing.as_bytes_mut() }.sort_unstable_by(|a, b| {
                    thread::sleep(Duration::from_millis(250));
                    COMPARISONS.fetch_add(1, Ordering::Relaxed);
                    a.cmp(b)
                });

                thread::sleep(Duration::from_secs(2));
            })
        })
        .collect::<Vec<_>>();
    for t in threads {
        let _ = t.join();
    }
}

febug-abi(5) — the ABI wrapped by this library.
libfebug(3), libfebug++(3), and libfebug.py(3), — equivalent C, C++, and Python libraries.

To all who support further development, in particular:

febug tracker

febug mailing list: <~nabijaczleweli/febug@lists.sr.ht>, archived at https://lists.sr.ht/~nabijaczleweli/febug

September 2, 2024 febug 1.0.0-2-g9ab5a53e