LIBFEBUG(3) | Library Functions Manual | LIBFEBUG(3) |
febug_start()
,
febug_end()
,
febug_register_type()
,
febug_wrap()
, febug_unwrap()
— User-space debugfs ABI wrapper library
#include
<libfebug.h>
cc
-l
febug
…
#define FEBUG_DONT 0
#define FEBUG_SOCKET "/run/febug.sock"
#define FEBUG_SIGNUM SIGUSR2
getenv
("FEBUG_DONT"
);
getenv
("FEBUG_SOCKET"
);
int febug_global_controlled_socket
=
-1;
void febug_start
();
void
febug_start_path
(const
char * path);
void
febug_debug_handler
(int);
void
febug_register_type
(uint64_t
type, void
(*formatter)(int, size_t));
void
febug_wrap
(uint64_t
type, const void *
data, const char *
name, ...);
void
febug_wrap_signal
(uint64_t
type, const void *
data, uint8_t
signal, const char *
name, ...);
void
febug_wrap_signalv
(uint64_t
type, const void *
data, uint8_t
signal, const char *
name, va_list
ap);
void
febug_unwrap
(const
void * data);
void
febug_end
();
Simplifies writing programs debuggable with febug(8) by presenting a high-level interface to febug-abi(5).
There are three compile-time macros that allow customising
libfebug
behaviour:
FEBUG_DONT
FEBUG_SIGNUM
febug_wrap
().
Defaults to SIGUSR2
.FEBUG_SOCKET
There are two environment variables that allow a user to customise its behaviour:
FEBUG_DONT
FEBUG_SOCKET
FEBUG_SOCKET
to connect to
febug(8).To be debugged, a program needs to, first,
call
febug_start_path
()
(likely via
febug_start
(),
which simply passes FEBUG_SOCKET
thereto) to connect to
febug(8), which, if successful, will
set febug_global_controlled_socket appropriately.
The program needs to install
febug_debug_handler
()
(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.
The program should register handlers for
types of variables it wishes to handle by calling
febug_register_type
()
— those type numbers should be consistent across the program, lest
the wrong handler is called. If no handler was registered for a type,
febug_debug_handler
() will instead return a generic
"not found" message. The handler takes the write end of the pipe
as the first argument, and the variable ID as the second; it shouldn't close
the pipe, as that is done by febug_debug_handler
()
regardless, and the program would then run the risk of closing another file
with the same descriptor simultaneously opened by another thread. 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 call
febug_wrap_signalv
()
(likely via
febug_wrap_signal
()
(likely via febug_wrap
(), which passes
FEBUG_SIGNUM
thereto)),
which will send a
febug_message
with the specified type and signal numbers, ID equal to the data pointer,
and name formatted according to
printf(3).
It's a no-op if febug_global_controlled_socket is
-1.
When the variable goes out of scope, the program
should call
febug_unwrap
()
to send a stop_febug_message
with the same data
pointer as it did febug_wrap
(), to prevent reading
random data that might no longer be mapped, or make sense. It's a no-op if
febug_global_controlled_socket is
-1.
When it wishes to stop being debugged, the program
may call
febug_end
()
which will shut and reset
febug_global_controlled_socket, if any, and deallocate
the type→handler map. 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 sorts a string with qsort(3) but waits half a second between each comparison; the string can be inspected via a febug(8) mount:
// SPDX-License-Identifier: MIT #define _POSIX_C_SOURCE 200809L #include <libfebug.h> #include <errno.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #define CSTRING_FEBUG_TP 420 static void cstring_febug_formatter(int fd, size_t data) { const char * str = (const char *)data; dprintf(fd, "%s\n", str); } static int char_comp(const void * lhs, const void * rhs) { const struct timespec half_second = {0, 500 * 1000 * 1000}; nanosleep(&half_second, 0); return *(const char *)lhs - *(const char *)rhs; } int main(void) { febug_start(); febug_register_type(CSTRING_FEBUG_TP, cstring_febug_formatter); struct sigaction handler; memset(&handler, 0, sizeof(handler)); handler.sa_handler = febug_debug_handler; if(sigaction(FEBUG_SIGNUM, &handler, 0) == -1) { fprintf(stderr, "sigaction: %s\n", strerror(errno)); return 1; } { __attribute__((__cleanup__(febug_unwrap))) char data[] = "JVLOkgsYmhCyEFxouKzDNajivGlpWqbdBwnfTAXQcreRHPIUSMtZQWERTYUIOPqwertyuiop" "1234567890"; febug_wrap(CSTRING_FEBUG_TP, data, "cool_data"); qsort(data, strlen(data), 1, char_comp); } sleep(2); febug_end(); }
febug-abi(5) —
the ABI wrapped by this library.
libfebug++(3),
libfebug.rs(3), and
libfebug.py(3), —
equivalent C++, Rust, and Python libraries.
To all who support further development, in particular:
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 |