.\" Automatically generated from an mdoc input file. Do not edit.
.\"" SPDX-License-Identifier: MIT
.TH "LIBFEBUG" "3" "January 30, 2021" "OpenBSD 6.8" "Library Functions Manual"
.nh
.if n .ad l
.SH "NAME"
\fBfebug_start\fR(),
\fBfebug_end\fR(),
\fBfebug_register_type\fR(),
\fBfebug_wrap\fR(),
\fBfebug_unwrap\fR()
\- User-space debugfs ABI wrapper library
.SH "SYNOPSIS"
\fB#include <libfebug.h>\fR
.PP
library \(lqlibfebug\(rq
.PP
\fB#define FEBUG_DONT 0\fR
.br
\fB#define FEBUG_SOCKET \&"/var/run/febug.sock"\fR
.br
\fB#define FEBUG_SIGNUM SIGUSR2\fR
.PP
\fBgetenv("FEBUG_DONT")\fR
.br
\fBgetenv("FEBUG_SOCKET")\fR
.PP
\fIint febug_global_controlled_socket\fR
= -1;
.HP 4n
\fBvoid febug_start\fR();
.HP 4n
\fBvoid febug_start_path\fR(\fIconst\ char\ *\ path\fR);
.HP 4n
\fBvoid febug_debug_handler\fR(\fIint\fR);
.HP 4n
\fBvoid febug_register_type\fR(\fIuint64_t\ type\fR, \fIvoid\ (*formatter)(int,\ size_t)\fR);
.HP 4n
\fBvoid febug_wrap\fR(\fIuint64_t\ type\fR, \fIconst\ void\ *\ data\fR, \fIconst\ char\ *\ name\fR, \fI...\fR);
.HP 4n
\fBvoid febug_wrap_signal\fR(\fIuint64_t\ type\fR, \fIconst\ void\ *\ data\fR, \fIuint8_t\ signal\fR, \fIconst\ char\ *\ name\fR, \fI...\fR);
.HP 4n
\fBvoid febug_wrap_signalv\fR(\fIuint64_t\ type\fR, \fIconst\ void\ *\ data\fR, \fIuint8_t\ signal\fR, \fIconst\ char\ *\ name\fR, \fIva_list\ ap\fR);
.HP 4n
\fBvoid febug_unwrap\fR(\fIconst\ void\ *\ data\fR);
.HP 4n
\fBvoid febug_end\fR();
.SH "DESCRIPTION"
\fBlibfebug\fR
allows a programmer to simplify writing programs debuggable with
febug(8)
by presenting a high-level interface to
febug-abi(5).
.PP
There are three compile-time macros that allow a program to customise its behaviour:
.TP 8n
\fBFEBUG_DONT\fR
.br
If non-zero, all symbols become
\fBstatic\fR,
functions turn into no-ops, and therefore no symbols from libfebug.a/.so are imported at link-time;
this is intended as a way to easily disable
febug(8)
integration completely on release builds.
.TP 8n
\fBFEBUG_SIGNUM\fR
.br
The signal to request from
febug(8)
when using
\fBfebug_wrap\fR().
Defaults to
\fRSIGUSR2\fR.
.TP 8n
\fBFEBUG_SOCKET\fR
.br
The path to connect to
febug(8)
on. Defaults to
\fI/var/run/febug.sock\fR.
.PP
There are two environment variables that allow a user to customise its behaviour:
.TP 8n
\fIFEBUG_DONT\fR
If set, don't try to connect to
febug(8),
so all library functions become no-ops.
.TP 8n
\fIFEBUG_SOCKET\fR
If set, use its value instead of
\fBFEBUG_SOCKET\fR
to connect to
febug(8)
.PP
To be debugged, a program needs to, first, call
\fBfebug_start_path\fR()
(likely via
\fBfebug_start\fR(),
which simply passes
\fBFEBUG_SOCKET\fR
.br
thereto) to connect to
febug(8),
which, if successful, will set
\fIfebug_global_controlled_socket\fR
appropriately.
.PP
The program needs to install
\fBfebug_debug_handler\fR()
(or a wrapper around it) as the signal handler for
\fBFEBUG_SIGNUM\fR
(and any other signals, if different ones are explicitly requested; if
\fRSIGKILL\fR,
some event loop that answers on
\fIfebug_global_controlled_socket\fR
must be in place). It's a no-op if
\fIfebug_global_controlled_socket\fR
is -1. This finishes set-up.
.PP
The program should register handlers for types of variables it wishes to handle by calling
\fBfebug_register_type\fR()
\[u2014] those type numbers should be consistent across the program, lest the wrong handler is called.
If no handler was registered for a type,
\fBfebug_debug_handler\fR()
will write 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
\fBfebug_debug_handler\fR()
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
\fIfebug_global_controlled_socket\fR
is -1.
.PP
At any time, when the program wishes to expose a variable, it can call
\fBfebug_wrap_signalv\fR()
(likely via
\fBfebug_wrap_signal\fR()
(likely via
\fBfebug_wrap\fR(),
which passes
\fBFEBUG_SIGNUM\fR
thereto)), which will send a
\fBfebug_message\fR
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
\fIfebug_global_controlled_socket\fR
is -1.
.PP
When the variable goes out of scope, the program should call
\fBfebug_unwrap\fR()
to send a
\fBstop_febug_message\fR
with the same data pointer as it did
\fBfebug_wrap\fR(),
to prevent reading random data that might no longer be mapped, or make sense.
It's a no-op if
\fIfebug_global_controlled_socket\fR
is -1.
.PP
When it wishes to stop being debugged, the program may call
\fBfebug_end\fR()
which will shutter and reset
\fIfebug_global_controlled_socket\fR,
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 FDs and free all mappings anyway.
.SH "EXAMPLES"
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:
.nf
.sp
.RS 6n
// SPDX-License-Identifier: MIT
#define _POSIX_C_SOURCE 200809L
#include <libfebug.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.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() {
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[] = "JVLOkgsYmhCyEFxouKzDNajivGlpWqbdBwnfTAXQcreRHPIUSMtZQWERTYUIOPqwertyuiop1234567890";
febug_wrap(CSTRING_FEBUG_TP, data, "cool_data");
qsort(data, strlen(data), 1, char_comp);
}
sleep(2);
febug_end();
}
.RE
.fi
.SH "SEE ALSO"
febug-abi(5)
\[u2014] the ABI wrapped by this library.
.PP
libfebug++(3)
\[u2014] an equivalent C++ library.
.PP
libfebug++(3)
\[u2014] an equivalent Rust library.
.SH "AUTHORS"
Written by
\[u043D]\[u0430]\[u0431] <\fInabijaczleweli@nabijaczleweli.xyz\fR>
.SH "SPECIAL THANKS"
To all who support further development, in particular:
.PD 0
.TP 8n
\fB\(bu\fR
ThePhD
.TP 8n
\fB\(bu\fR
Embark Studios
.PD
.SH "REPORTING BUGS"
\fIfebug tracker\fR: \fBhttps://todo.sr.ht/~nabijaczleweli/febug\fR
.PP
febug mailing list:
<\fI~nabijaczleweli/febug@lists.sr.ht\fR>
archived at
\fBhttps://lists.sr.ht/~nabijaczleweli/febug\fR