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 = "0.1.2-3-g82a6323"


env!("FEBUG_DONT")?
env!("FEBUG_SOCKET") = "/var/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();

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;

allows a programmer to simplify 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 a programmer to customise its behaviour:

FEBUG_DONT If set, all functions turn into no-ops; this is intended as a way to easily disable febug(8) integration completely on release builds.
FEBUG_SIGNUM The signal to request from febug(8) when using (). Defaults to SIGUSR2.
FEBUG_SOCKET The path to connect to febug(8) on. Defaults to /var/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 FEBUG_SOCKET to connect to febug(8)

To be debugged, a program needs to, first, call () (likely via (), which simply passes b"/var/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 (and any other signals, if different ones are explicitly requested; if 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. This finishes set-up. 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 corresponding to the debugged type; if your type is not not , lie here. 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 febug::Wrapper (likely via one of the convenience or traits), which will send a 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 . 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 shutter 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 FDs 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 febug::StaticWrappable;
use std::time::Duration;
use std::any::TypeId;
use std::io::Write;
use std::thread;


fn main() {
    febug::start();
    if febug::install_handler() {
        febug::FORMATTERS.lock().unwrap().insert(TypeId::of::<String>(), |of, vid| {
            let data = unsafe { &*(vid as *const String) };

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


    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));
                    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) — an equivalent C library.

libfebug++(3) — an equivalent C++ library.

Written by наб <nabijaczleweli@nabijaczleweli.xyz>

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

March 7, 2021 OpenBSD 6.8