.\" 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::controlled_socket\fR,
\fBfebug::wrapper\fR,
\fIfebug::formatters\fR,
\fBfebug::debug_handler\fR()
\- User-space debugfs ABI wrapper library for C++
.SH "SYNOPSIS"
\fB#include <libfebug.hpp>\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
\fBstruct febug::controlled_socket;\fR
.br
\fIconst febug::controlled_socket febug::global_controlled_socket;\fR
.PP
\fBstruct febug::wrapper;\fR
.br
.PD 0
.HP 4n
\fBfebug::wrapper::wrapper\fR(\fIconst\ T\ &\ data\fR, \fIconst\ char\ *\ name\fR, \fI...\fR);
.PD
.HP 4n
\fBfebug::wrapper::wrapper\fR(\fIconst\ T\ &\ data\fR, \fIuint8_t\ signal\fR, \fIconst\ char\ *\ name\fR, \fI...\fR);
.HP 4n
\fBfebug::wrapper::wrapper\fR(\fIconst\ T\ &\ data\fR, \fIuint8_t\ signal\fR, \fIconst\ char\ *\ name\fR, \fIva_list\ ap\fR);
.PP
\fIstd::map<size_t, void (*)(int, size_t)> febug::formatters;\fR
.HP 4n
\fBvoid febug::debug_handler\fR(\fIint\fR);
.SH "DESCRIPTION"
\fBlibfebug++\fR
allows a programmer to simplify writing C++ 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
The
\fBfebug::controlled_socket\fR
structure is defined as follows:
.nf
.sp
.RS 6n
struct febug::controlled_socket {
int fd = -1;
inline operator int() const noexcept { return this->fd; }
controlled_socket(const char * path = FEBUG_SOCKET) noexcept;
~controlled_socket() noexcept;
};
.RE
.fi
.PP
There is a global instance at
\fIfebug::global_controlled_socket\fR
which, if
\fIpath\fR
isn't the null pointer, attempts connection to
febug(8)
and will set
\fIfd\fR,
if successful. Similarly, destroying it will hang up and reset
\fIfd\fR.
.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 adding entries to
\fIfebug::formatters\fR
\[u2014] the key is typeid(std::decay_t<T>).hash_code(), so if this yields different results for two types that should have the same handler,
multiple entries need to be registered.
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.
.PP
The
\fBfebug::wrapper\fR
structure is defined as follows:
.nf
.sp
.RS 6n
template <class T>
struct febug::wrapper {
const T * data;
wrapper(const T & data, const char * name, ...) noexcept;
wrapper(const T & data, uint8_t signal, const char * name, ...) noexcept;
wrapper(const T & data, uint8_t signal, const char * name, va_list ap) noexcept;
~wrapper() noexcept;
};
.RE
.fi
.PP
If the program wishes to debug a variable, it should construct a
\fBfebug::wrapper\fR
referencing it; the constructor will send a
\fBfebug_message\fR
with the type corresponding to
\fRtypeid(std::decay_t<T>).hash_code()\fR,
ID corresponding to the pointer to
\fIdata\fR,
signal being either specified or defaulting to
\fBFEBUG_SIGNUM,\fR
.br
and name formatted according to
printf(3).
The destructor will send a
\fBstop_febug_message\fR.
Both become no-ops if
\fIfebug::global_controlled_socket\fR
is -1.
.SH "EXAMPLES"
The following program sorts a std::vector<int> with
\fBstd::sort\fR()
but waits a second between each comparison; the vector and the amount of comparisons can be inspected via a
febug(8)
mount:
.nf
.sp
.RS 6n
// SPDX-License-Identifier: MIT
#include <libfebug.hpp>
#include <vector>
#include <algorithm>
#include <cstring>
#include <errno.h>
#include <unistd.h>
int main() {
if(febug::global_controlled_socket != -1) {
febug::formatters.emplace(typeid(std::vector<int>).hash_code(), [](int retpipe, std::size_t vid) {
const std::vector<int> & data = *(const std::vector<int> *)vid;
for(auto num : data)
dprintf(retpipe, "%d ", num);
write(retpipe, "\\n", 1);
});
febug::formatters.emplace(typeid(std::size_t).hash_code(), [](int retpipe, std::size_t vid) {
const std::size_t & data = *(const std::size_t *)vid;
dprintf(retpipe, "%zu\\n", data);
});
}
struct sigaction handler {};
handler.sa_handler = febug::debug_handler;
if(sigaction(FEBUG_SIGNUM, &handler, nullptr) == -1)
std::fprintf(stderr, "sigaction: %s\\n", std::strerror(errno));
{
std::vector<int> data{-1, -2, -3, 0, 1, 2, 3, -1, -2, -3, 0, 1, 2, 3, -1, -2, -3, 0, 1, 2, 3,
-1, -2, -3, 0, 1, 2, 3, -1, -2, -3, 0, 1, 2, 3, -1, -2, -3, 0, 1, 2, 3};
std::size_t comparisons_done{};
febug::wrapper data_w{data, "cool_data"};
febug::wrapper comparisons_done_w{comparisons_done, "comparisons"};
std::sort(data.begin(), data.end(), [&](auto lhs, auto rhs) {
sleep(1);
++comparisons_done;
return lhs < rhs;
});
}
sleep(2);
}
.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