Jonathan Rajotte [Fri, 17 Jun 2022 21:34:46 +0000 (17:34 -0400)]
Consumer: strip ring buffer header when consuming ctf2 ring buffer packet
On the kernel tracer side, providing a header free packet would be quite
intrusive in term of ABI/API modification. Thus for now, the kernel
tracers keeps the notion of header. On the consumer side, we can drop
the header as necessary based on the format we expect or we could even
inspect the header if necessary. For now, we chose to trust the
configured format for the session since we have access to the
information.
On the userspace side, historically the usage of a "ring buffer" for the
metadata was somewhat driven by the ctf1 "requirement" of providing
header for a given metadata packet. Essentially in ctf1 the ring buffer
packet "metadata" leaks into the actual ctf1 spec. It also happened that
the same machinery could then be reused for both the kernelspace and
userspace metadata ingestions.
CTF2 drop all this so could lttng-tools! But, considering that ctf1
still need to be supported for a while and that we still need to have
all the machinery for consuming from the kernelspace metadata
ringbuffer, keeping the current data path for both userspace and
kernelspace for metadata is probably a safer bet.
The only "drawback" here is that headers for the userspace metadata
ringbuffer contain version fields that will be equivalent to 1.8 despite
the fact that it contains ctf2 metadata. This is because the version is
currently hard coded by lttng-ust into the metadata packet headers.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: I761c8a7d9742cbc9c875767233f2d30a43b9743f
Jonathan Rajotte [Thu, 18 Aug 2022 20:39:37 +0000 (16:39 -0400)]
Propagate trace format all the way to the consumer
TODO: currently we end up propagating and exposing the trace format as a
vulgar integer on lttng-consumerd side. This is one of the frontier we
discussed IRL.
TODO: use the trace format serialize and pass a complete trace format
object on the pipe
Note that this is necessary to later decide if the consumerd strips the
ringbuffer header or not based on the ctf version. As discussed IRL, ust
could be modified to expose an API allowing us to set the ctf version thus
propagating the ctf version inside the ringbuffer header and perofming
introspection at the consumer level and split based on the value. This
was simply easier to do since we have all the data available at hand.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: I4d12692fe2cab8ad9bf22db9b1d455ec397fa843
Jonathan Rajotte [Thu, 18 Aug 2022 20:29:25 +0000 (16:29 -0400)]
Convert ust_app_session to c++
This is a minimal patch to move the ust_app_session struct to c++ style.
This will allow us to add complex type in the struct later on (such
as unique_ptr/shared_ptr etc).
Use default member initialization when possible.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: I3536444e002f1311b851e2333ff5f8fc4a5904dd
Jonathan Rajotte [Thu, 18 Aug 2022 18:46:02 +0000 (14:46 -0400)]
Convert ltt_kernel_session to c++
This is a minimal patch to move the ltt_kernel_session struct to c++ style.
This will allow us to add complex type in the struct later on (such
as unique_ptr/shared_ptr etc).
Use default member initialization when possible.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: Ib5778db0c39666e30782931c5bf5d4ffb5beeff0
Jonathan Rajotte [Thu, 14 Apr 2022 20:01:25 +0000 (16:01 -0400)]
Convert ltt_ust_session to c++
This is a minimal patch to move the ltt_ust_session struct to c++ style.
This will allow us to add complex type in the struct later on (such
as unique_ptr/shared_ptr etc).
Use default member initialization when possible.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: I2578fa24fde8de0728d20a938aa55e2a6ad7a161
Jonathan Rajotte [Thu, 14 Apr 2022 20:01:25 +0000 (16:01 -0400)]
Convert ltt_session to c++
This is a minimal patch to move the ltt_ust_session struct to c++ style.
This will allow us to add complex type in the struct later on (such
as unique_ptr/shared_ptr etc).
Use default member initialization when possible.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com>
Change-Id: I76b2a0c9befc7f982599bcc9d8072c859a1731e5
sessiond: add a CTF 2-generating trace class visitor
The enumeration_type class is modified to guarantee that a mapping is
present. Implicit (or auto) mappings provided by the tracer are
converted to an explicit mapping.
This results in a slightly more verbose TSDL output which can be fixed
later.
In CTF 2, variant options are chosen using a set of ranges. This differs
from CTF 1.8 (and the current API) which assumed that a variant had
options that perfectly matched the selector enumeration's mappings.
Add integer mappings to variants and also carry a choice name that
becomes the name used by the TSDL producer.
There is no benefit to transfering the ownership of a new packet header
everytime it is serialized. Packet headers don't change after the
creation of a session; prefer allocating it once and provide a
raw (const) pointer to the session's instance.
While LTTng always produces one, a trace doesn't absolutely need a
packet header so a pointer is preferred over a reference.
sessiond: generate packet header, packet context and event header dynamically
Instead of hardcoding event header and packet header/context layouts,
their layout is expressed using the lttng::sessiond::trace::field/type
API and serialized to TSDL using the visitor.
This reduces the duplication of code in the CTF2 visitor.
Fix: sessiond: tsdl: don't prepend underscore for stream_id
Although the CTF v1.8 specification recommends ignoring any leading
underscore, Some readers, such as Babeltrace 1.x, expect special
identifiers without a prepended underscore.
This causes problems in a follow-up patch since packet header, context,
and event headers become dynamically generated.
Extend the field/field type to include the blob field types that
are used to express the trace class uuid in CTF2.
Roles defined by the CTF2 specification are also added to allow a trace
class to define the layout of some scopes (e.g. packet header and
context) instead of hard-coding the serialized version of the layout
description.
The TSDL trace class visitor takes the new types into account by
converting them into arrays of "bytes" (uint8_t).
Tests: size-based rotation: implement a trace size cutoff protection
Stop waiting for rotations when the trace exceeds a certain size cutoff.
This prevents those tests from filling a hard drive when they fail.
However, this check is racy since it is possible for an arbitrary number
of apps to run before the session daemon gets a chance to perform the
scheduled rotations.
A number of error codes were added to cmd_rotate_session since the
implementation of size-based rotations. The rotation thread doesn't
expect LTTNG_ERR_ROTATION_MULTIPLE_AFTER_STOP and
LTTNG_ERR_ROTATION_AFTER_STOP_CLEAR which are not fatal failures.
These rotations would simply result in an empty trace archive and are,
therefore, not produced. In both cases, it is safe to wait for the next
size cycle.
Fix: sessiond: size-based rotation threshold exceeded in per-pid tracing (2/2)
For a complete description of the original problem, refer to the previous
commit.
This change implements the second part of the fix.
Buffer statistic samples are augmented to include the channel's session
id. Since a session can outlive its channels (on the session daemon
side), the consumed size conditions are now bound to the session.
This means that the "total consumed" state is now part of the
session_info structure exclusively which, overall, is cleaner.
A side-effect of this change is that consumed size conditions are now
also evaluated when a trigger is registered or when a client subscribes
to it via a notification channel instead of waiting until the next
monitoring sample.
The buffer statistics sample also expresses a "consumed size" that is
relative to the last sample that was successfully sent.
Finally, the consumer daemon sends a final buffer statistics sample when
a channel is torn down. As explained in more detail in the previous
commit, this makes the accounting of per-pid sessions more reliable when
short-live applications are traced.
Fix: sessiond: size-based rotation threshold exceeded in per-pid tracing (1/2)
Issue observed
--------------
When tracing short-lived applications with buffers configured in per-pid
mode, the size-based rotation threshold is often greatly exceeded. In
the CI, this occasionally causes the size-based rotation tests to
timeout for the per-pid case.
Cause
-----
There is a scenario where a session's consumed size is miscalculated.
When an application exits during per-pid tracing, both the session and
consumer daemons notice it. The session daemon sees the application's
command pipe hanging-up, while the consumer daemon sees the
application's data-ready pipe hanging-up.
Upon handling these events, both daemons tear down their representation of
the channels.
In an ideal world, we'd want to sample the streams' "consumed_size" at
the last possible moment to get the size of all consumed data for this
stream. However, this is problematic in the following scenario:
- the sessiond destroys the channel before the consumer daemon,
- the consumer daemon sends a final buffer stats sample on tear down,
- the sessiond can do nothing with the sample as it doesn't know that
channel anymore.
(Note that the session daemon gracefully handles the case where it
doesn't know a channel.)
When applications have a short lifetime and are traced in per-PID
buffering mode, there is a high likelihood that the last buffer
statistics sample sent for a given channel will target a channel that
the session daemon has already torn down.
Solution
--------
Consumed-size conditions are somewhat special: they are bound to a
session, but they are evaluated through a per-channel event (buffer
statistics samples taken by the channels' monitoring timer).
To work around the problem of lifetime of channels, we can rely
on the fact that sessions outlive channels to perform the accounting
of the consumed size.
This patch is the first step to implement this fix: new
notification-thread commands are introduced to announce the creation and
destruction of an `ltt_session`. Currently, the notification thread
implies the existence of a session by tracking its channels' creation
and destruction.
With this change, it no longer needs to do so; session are explicitly
created and destroyed. Their unique ID is also kept stored.
The key of `sessions_ht` becomes the `id` of the session to allow
efficient look-ups on the reception of a buffer statistics sample.
The existing callsites that make use of the session's name to perform a
look-up are modified to look-up the id by name (see
sample_session_id_by_name()).
The add/remove channel commands and rotation ongoing/completed commands
are modified to refer to sessions by ID since they can assume the
notification thread knows about the session.
Note
----
In a follow-up patch, buffer statistics samples are modified to include
the session's ID and the consumed size is modified to become a "delta"
relative to the previous sample associated with a given channel.
This makes it possible to perform the accounting of a session's consumed
size beyond the lifetime of its channels.
The follow-up patch is the "core" of the fix, but it requires these
prior changes.
consumerd: send a buffer static sample on flush command
When application exits during per-pid tracing, both the session and
consumer daemons notice it. The session daemon sees the application's
command pipe hanging-up, while the consumer daemon sees the
application's data-ready pipe hanging-up.
Upon handling this event, both daemons tear down their representation of
the channels.
In an ideal world, we'd want to sample the streams' "consumed_size" at the
last possible moment to get the size of all consumed data for this
stream. However, this is problematic in the following scenario:
- the sessiond destroys the channel before the consumer daemon,
- the consumer daemon sends a final buffer stats sample on tear down,
- the sessiond can do nothing with the sample as it doesn't know that
channel anymore.
Note that the session daemon handles the case where it doesn't know a
channel gracefully.
When an application being traced in per-pid mode is torn down, the
session requests a flush of its buffers to the consumer daemon. We can
use this opportunity to emit a buffer stats sample.
This is still racy since the tear down of the channel could complete on
the session daemon's end before that last sample can be processed. In
practice, though, it markedly improves the precision of size-based
rotations in per-pid tracing mode.
On my work machine, I see the size-based rotation tests pass with
archive sizes within ~10% of the size threshold. Before this, we lost a
lot of samples from short-lived buffers and it would not be rare to see
archives end-up multiple times (5x-10x) larger than the size-threshold.
Another problem is that the consumed_size returned by the consumer
daemon will not include the packets that have yet to be consumed.
Whether or not this is a fix is debatable since it arguably just
improves the precision of size-based rotations.
Fix: ust-consumerd: set `hangup_flush_done` in a locked context
hangup_flush_done is updated after releasing the stream lock. This
doesn't appear to be a problem right now since this attribute is
apparently always accessed by the same thread, but it is conceptually
sus.
Since c08136a3f, the rotation thread's handle_condition() checks that
the notification received matches the trigger that was registered.
As part of the equality check, the triggers' credentials are compared.
This checks fails systematically since the group id of a trigger's
credentials is not transported by the serialize/create_from functions.
The trigger that is received through the notification thus has an unset
group id, while the rotation trigger of the `ltt_session` has a group id
set; it was not stripped by the communication layer.
The check also fails since the trigger registered for the size-based
rotation is "hidden". This internal attribute is not propagated through
the communication layer, which causes the comparison to fail.
Solution
========
Since triggers only use the 'uid' part of lttng_credentials, we ensure
that lttng_trigger_set_credentials only sets this part of the structure.
Also, the `is_hidden` attribute of a trigger is now propagated through
the communication layer. This has no effect for external applications
since this attribute is not exposed through the API. However, it is
useful for internal triggers which use the same communication
facilities.
This allows the equality check in rotation-thread.cpp to go through as
expected.
Michael Jeanson [Tue, 12 Apr 2022 20:55:50 +0000 (16:55 -0400)]
Bump URCU dependency to 0.14
Complete C++ support was introduced in Userspace-RCU 0.14, using earlier
versions results in a build failure, this should be reflected in the
configure check.
Change-Id: I1b708bd9b04784deb9f2c8768a331911c3ebb891 Signed-off-by: Michael Jeanson <mjeanson@efficios.com> Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Build fix: missing initializer for member 'rotation_positions'
gcc 5.4.0 complains that:
main.cpp: In function 'ssize_t relay_unpack_rotate_streams_header(const lttng_buffer_view*, lttcomm_relayd_rotate_streams*)':
main.cpp:2547:2: warning: missing initializer for member 'lttcomm_relayd_rotate_streams::rotation_positions' [-Wmissing-field-initializers]
The structure's members are initialized one by one.
At the same time, the use of the address of a packed
member (stream_count) is eliminated, which fixes another unrelated
warning emited by clang.
Jonathan Rajotte [Wed, 15 Jun 2022 19:09:03 +0000 (15:09 -0400)]
Build fix: specialization of template in different namespace
Observed issue
==============
On older g++, such as gcc (Ubuntu 5.3.1-14ubuntu2) 5.3.1 20160413:
make[3]: Entering directory '/tmp/virtenv/src/lttng-tools/src/bin/lttng-sessiond'
CXX utils.lo
In file included from ust-app.hpp:15:0,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
../../../src/common/format.hpp:17:24: warning: unknown option after '#pragma GCC diagnostic' kind [-Wpragmas]
DIAGNOSTIC_IGNORE_DUPLICATED_BRANCHES
^
In file included from ust-app.hpp:15:0,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
../../../src/common/format.hpp:23:13: error: specialization of 'template<class T, class Char, class Enable> struct fmt::v8::formatter' in different namespace [-fpermissive]
struct fmt::formatter<std::type_info> : fmt::formatter<std::string> {
^
In file included from ../../../src/common/format.hpp:19:0,
from ust-app.hpp:15,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
../../../src/vendor/fmt/core.h:707:8: error: from definition of 'template<class T, class Char, class Enable> struct fmt::v8::formatter' [-fpermissive]
struct formatter {
^
In file included from ust-registry.hpp:20:0,
from ust-app.hpp:19,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
ust-registry-event.hpp:66:13: error: specialization of 'template<class T, class Char, class Enable> struct fmt::v8::formatter' in different namespace [-fpermissive]
struct fmt::formatter<lttng::sessiond::ust::registry_event> : fmt::formatter<std::string> {
^
In file included from ../../../src/common/format.hpp:19:0,
from ust-app.hpp:15,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
../../../src/vendor/fmt/core.h:707:8: error: from definition of 'template<class T, class Char, class Enable> struct fmt::v8::formatter' [-fpermissive]
struct formatter {
^
In file included from ust-app.hpp:19:0,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
ust-registry.hpp: In constructor 'lttng::sessiond::ust::registry_typed_enum<MappingIntegerType>::registry_typed_enum(const char*, const lttng_ust_ctl_enum_entry*, size_t)':
ust-registry.hpp:111:45: error: 'lttng::sessiond::trace::integer_type::signedness' is not a class, namespace, or enumeration
lttng::sessiond::trace::integer_type::signedness::SIGNED :
^
ust-registry.hpp:112:51: error: 'lttng::sessiond::trace::integer_type::signedness' is not a class, namespace, or enumeration
lttng::sessiond::trace::integer_type::signedness::UNSIGNED),
^
In file included from lttng-sessiond.hpp:22:0,
from utils.cpp:17:
ust-app.hpp: At global scope:
ust-app.hpp:330:13: error: specialization of 'template<class T, class Char, class Enable> struct fmt::v8::formatter' in different namespace [-fpermissive]
struct fmt::formatter<ust_app> : fmt::formatter<std::string> {
^
In file included from ../../../src/common/format.hpp:19:0,
from ust-app.hpp:15,
from lttng-sessiond.hpp:22,
from utils.cpp:17:
../../../src/vendor/fmt/core.h:707:8: error: from definition of 'template<class T, class Char, class Enable> struct fmt::v8::formatter' [-fpermissive]
struct formatter {
^
cc1plus: warning: unrecognized command line option '-Wno-gnu-folding-constant'
cc1plus: warning: unrecognized command line option '-Wno-incomplete-setjmp-declaration'
Makefile:855: recipe for target 'utils.lo' failed
make[3]: *** [utils.lo] Error 1
This also applies to the following specializations:
void lst::signed_enumeration_type::accept(type_visitor& visitor) const
void lst::unsigned_enumeration_type::accept(type_visitor& visitor) const
Problem
=======
This is due to a now-fixed gcc bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=42018
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
Solution
========
Put the template specializations inside the proper namespace.
Signed-off-by: Jonathan Rajotte <jonathan.rajotte-julien@efficios.com> Signed-off-by: Jérémie Galarneau <jeremie.galarneau@efficios.com>
Change-Id: I6b931065b37e6e9ba97f87c754c15808506c2ba8
Build fix: old gcc does not recognize hidden/shadowed enumeration as valid
The build fails on GCC < 6 with:
ust-registry.hpp: In constructor 'lttng::sessiond::ust::registry_typed_enum<MappingIntegerType>::registry_typed_enum(const char*, const lttng_ust_ctl_enum_entry*, size_t)':
ust-registry.hpp:111:45: error: 'lttng::sessiond::trace::integer_type::signedness' is not a class, namespace, or enumeration
lttng::sessiond::trace::integer_type::signedness::SIGNED :
The same error occurs for stream_class::header_type.
This is due to a bug fixed in gcc 6:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60994
In both cases, the member is suffixed to disambiguate the reference to
the inner-enumeration.
Fix: sessiond: registry_channel: initialize _rcu_head and _node
1490020 Uninitialized pointer field
The pointer field will point to an arbitrary memory location, any
attempt to write may cause corruption.
In lttng::sessiond::ust::registry_channel::registry_channel(unsigned int, std::function<void (lttng::sessiond::ust::registry_channel const &)>, std::function<void (lttng::sessiond::ust::registry_channel const &, lttng::sessiond::ust::registry_event const &)>): A pointer field is not initialized in the constructor (CWE-457)
common: replace container_of with a C++ safe implementation
As more code moves to a more idiomatic C++ style, structures like
typically end up becoming classes that use different access controls,
virtual functions, etc. This, in turn, makes them adopt a non standard
layout and causes GCC and clang to emit the following warning when
container_of is used:
error: 'offsetof' within non-standard-layout type 'foo' is conditionally-supported [-Werror=invalid-offsetof]
This new implementation of container_of makes use of a pointer to a data
member to find the parent's address.
The use of ptr_to_member against the null dummy_parent makes me uneasy
as it seems equivalent to performing arithmetic on a null pointer, which
I understand is undefined behavior (C++11 Standard 5.7.5).
However, Boost.Instrusive uses an approach that seems roughly equivalent
to lttng::utils::container_of() [1].
It seems like a reasonable compromise that works on all mainstream
compilers.
Clean-up: sessiond: move registry_session free functions under class
Move a number of registry_session methods implemented as c-style
free functions under the `registry_session` class. This makes it
possible to make a large number of attributes private.
This leaves only the metadata storage and locking facilities
publicly accessible. Making them private requires a bit more
refactoring.
sessiond: transition from lttng-ust to tracer agnostic API
Refactor the session daemon's user space tracer management to use the
tracer-agnostic trace hierarchy description API. Since the API
introduced under lttng::sessiond::trace is closer to idiomatic C++,
some changes are needed to make use of it.
The biggest changes make the ust_registry* structures inherit from the
trace descriptions classes (trace_class, stream_class, event_class,
clock_class). This effectively isolates the members of the
`ust_registry` structures that describe their corresponding CTF class
to a base class and leaves only the implementation guts in the
`ust_registry` objects (moved under lttng::sessiond::ust).
Since the generation of TSDL metadata directly used the lttng_ust_ctl
API, it is replaced by a new implementation that is based around a trace
class visitor that serializes the tracer agnostic trace description
objects.
The TSDL environment visitor is moved under the TSDL implementation
of the trace class visitor.
Some little changes are also made to the existing code to make it
exception-safe, as needed.
The session daemon receives the fields present in events from the
instrumented applications in the form of an array of lttng_ust_ctl_field
structures and uses them directly in a number of code paths.
This makes it impossible to extend the structures which makes a number of
features harder to implement than they should be.
The session daemon also implements various CTF concepts (clock, trace,
event, stream classes) as part of various structures defined under
`ust_registry*`. This makes it hard to isolate which attributes are
"internal" and which are visible to the external world through the
traces.
Tracer-agnostic trace hierachy classes are introduced under the
lttng::sessiond::trace namespace. Those classes don't cover the full
functionality of CTF: they expose what the tracers can currently
express.
The top-level elements of the trace hierarchy -- trace, event, stream,
clock classes -- are visited using the trace_class_visitor interface.
Seperate field and type visitor interfaces are used to make it easier to
visit this subset of the trace hierarchy. This will be useful to
implement the listing of event fields through liblttng-ctl, for example.
In the short term, these classes will be used to implement the
serialization of the layout descriptions of CTF 1.8 and 2.
Tests fix: metadata event: print expected and actual event match count
The validate_metadata_event() function prints the actual event match
count twice when a test fails. Printing the expected vs actual counts
was probably the intention of the original author.
A locked reference is a wrapper that allows functions to return a
protected/synchronized version of an object. My immediate use-case for
this helper is making it easier to call functions that return an
rcu-protected object (require the caller to hold the RCU reader lock for
the duration of its use of that object) in an exception safe manner.
As such, these functions can now return
lttng::locked_reference<MyType, lttng::urcu::unique_read_lock> which
ensures the RCU reader lock is held for as long as the object is used.
The name() returned by std::type_info is implementation-dependant. In
practice, it is typically the type's mangled name. For GCC and clang, it
is possible to use abi::__cxa_demangle to demangle the name at runtime
while formatting a string.
If this poses any compatibility problem on other platforms, we can
fallback to using name() directly.
common: add macros to silence invalid offsetof warnings
Implementations are not forced to support offsetof for non-trivial types
in C++. gcc and clang both appear to support its use, but we should move
away from that. unfortunately that is not easy since offsetof is used
all over the place to use intrusive data structures.
I am unsure of the proper fix for this at the moment.
Add new exception types: communication, protocol, and invalid argument
These new exception types are useful to expess communication errors
and are used in later patches when an application doesn't honor the
liblttng-ust-ctl protocol.
Add `find_session_by_id` and `find_locked_session_by_id` which return
smart pointers to ltt_session. In both cases, the smart pointers make
use of ltt_session's underlying reference counting mechanism.
In the case of `find_locked_session_by_id`, the session that is returned
is locked; it is automatically unlocked (and a reference is released)
when the pointer goes out of scope. This makes it easier to write
exception-safe code that uses the ltt_session API.
Add C++ wrappers for pthread mutex and rcu read lock
Add two wrappers that are similar and provide the "Mutex" named
requirements[1] around pthread_mutex_t and liburcu's RCU reader lock.
In both cases, the intention is to either use the `mutex` or `read_lock`
interface with the standard concurrency support library (e.g. std::lock,
etc.) or, more likely, use the lock_guard wrappers.
The lock_guard[2] wrappers make it easier to convert existing code to be
exception-safe and generally makes the use of those locks less
error-prone.
Add fmt 8.1.1 headers (we will use it in header-only mode). fmt is made
available under the MIT license, which is already in the LICENSES
directory.
Note that an lttng-format.hpp header is added to disable a warning which
prevents us from building with -Werror.
../../../src/vendor/fmt/format-inl.h:2457:11: error: target of initialization might be a candidate for a format attribute [-Werror=suggest-attribute=format]
2457 | int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
| ^~~~~~~~~~~~
The header also ensures that FMT_HEADER_ONLY is defined for all uses of
libfmt.