QUARK(7) | Miscellaneous Information Manual | QUARK(7) |
quark
— unified
system process telemetry library
quark
is a library that provides a way to
retrieve and listen to process events in linux systems. Its main purpose is
to abstract different backends and to provide a common API for listening to
system-wide events like fork(2),
exec(3),
exit(3) and others.
quark
not only provides an API for
listening to events, but also handles ordering, buffering and aggregation of
said events. In its most basic form, a short lived process consisting of
fork(2) +
exec(3) +
exit(3) will be aggregated into one
quark_event. An internal process cache is also kept
that can be looked up via
quark_process_lookup(3).
Clone the repository, compile and run quark's test utility quark-mon(8):
$ git clone --recursive https://github.com/elastic/quark $ cd quark $ make $ sudo ./quark-mon On another shell, create any process like: $ ls -1 /tmp | wc -l
See BUILDING for a list of dependencies if you're having trouble building. Also see INCLUDED BINARIES and quark-mon(8).
quark
tries to guarantee event ordering as much as
possible. Ordering must be done in userland for some backends, notably
anything that uses perf-rings. quark
uses two
Rank Balanced
Trees for ordering and aggregation.
The first tree is basically a priority queue, ordered by the time of the event. The second tree is ordered by time of the event + pid and it's used for event aggregation.
quark
buffers and aggregates related events that
happened close enough. The common case is generating a single event for
the triple: fork(2),
exec(3),
exit(3). There are rules on what can
be aggregated, and only events of the same pid are aggregated. For
example: quark
won't aggregate two
exec(3) events, otherwise we would
lose the effects of the first one. These rules will be exposed and
configurable in the future.quark
needs
to be able to buffer events, this means holding them before presenting
them to the user. quark
employs an ageing timeout
that is a stepped function of the number of currently buffered events, the
more events you have, the shorter the timeout will be, so memory can be
bound. A quark_event is only given to the user when
it has a certain age. From quark.c:
/* * Target age is the duration in ns of how long should we hold the event in the * tree before processing it. It's a function of the number of items in the tree * and its maximum capacity: * from [0; 10%] -> 1000ms * from [90%; 100%] -> 0ms * from (10%; 90%) -> linear from 1000ms -> 100ms */
quark
maintains an internal
process table with what has been learned about the process so far, this
context is then included in each event given to the user. The process
table can also be queried, see below.quark
tries to be as transparent as possible about
what it knows, there are counters for lost events, and each piece of
information of a quark_event is guarded by a flag,
meaning the user might get incomplete events in the case of lost events,
it's the user responsability to decide what to do with it.
Depending on load, the user might see an event as the aggregation of multiple events, or as independent events. The content remains the same.
quark
is written in C, but Go bindings are also
provided. Ideally we will be able to provide bindings for other languages
in the future.quark
will try to use
the EBPF, falling back to KPROBE if it failed.quark
can be built natively or via a
container, native is preferred and depends on:
Make sure to clone the repository recursively: git clone --recursive.
make builds the repository, including quark-mon, libquark_big.a and a libquark.a.
libquark_big.a includes all needed dependencies in one big archive. This includes a libbpf.a, libelf_pic.a (from the elftoolchain project, BSD license), and a libz.a (see zlib/LICENSE). See LINKING to learn how to link either.
While quark
doesn't build
elastic/ebpf, it does use the EBPF programs from that
repository, only the files needed are included in
quark
, as elastic/ebpf is quite
big.
Other useful build targets include:
quark
.quark
inside a docker container, so you
don't have to worry about having build dependencies.quark
for arm64 inside a docker
container.quark
inside a centos7 docker container,
useful for linking against ancient glibc-2.17.$ make btfhub BTFHUB_ARCHIVE_PATH=/my/path/to/btfhub-archive
quark
.
Usage:
$ make eebpf-sync EEBPF_PATH=/my/path/to/elastic/ebpf
All the targets above can generate debug output by specifying V=1, as in:
$ make V=1
$ cc -o myprogram myprogram.c libquark_big.a OR $ cc -o myprogram myprogram.c libquark.a libbpf/src/libbpf.a elftoolchain/libelf/libelf_pic.a zlib/libz.a
quark-test(8) is the main test utility ran by the CI, can be invoked via make test. All tests are self-contained in this binary.
Some included kernels can be tested in qemu via make
test-kernel. Any quark
utility can be run on a
custom kernel via the krun.sh script, as in:
$ make initramfs.gz $ ./krun.sh initramfs.gz kernel-images/amd64/linux-4.18.0-553.el8_10.x86_64 quark-test -vvv
Note that you can pass arguments to the utility and you have to make initramfs.gz first.
quark-mon(8) is a program that dumps quark_events to stdout and can be used for demo and debugging. It has a neat feature: can be run without priviledges, while useless in this small program, it aims to demonstrate how a user could implement the same.
quark-btf(8) is a
program for dumping BTF information used by
quark
.
quark-test(8) is a program for running tests during development.
quark
through
quark_queue_get_events(3).The ball starts with quark_queue_open(3).
quark_queue_open(3) initializes a quark_queue which holds the majority of runtime state used by library, this includes perf-rings, file descriptors, EBPF programs buffering data-structures and the like. It must be paired with a quark_queue_close(3) on exit.
quark_queue_get_events(3) is the main driver of the library, it does the buffering, per-ring scanning, aggregation and event cache garbage collecting. In case there are no events it returns zero and the user is expected to call quark_queue_block(3) or equivalent.
#include <err.h> #include <quark.h> #include <stdio.h> int main(void) { struct quark_queue qq; struct quark_event qevs[32], *qev; int n, i; if (quark_queue_open(&qq, NULL) == -1) err(1, "quark_queue_open"); for (; ;) { n = quark_queue_get_events(&qq, qevs, 32); if (n == -1) { warn("quark_queue_get_events"); break; } /* Scan each event */ for (i = 0, qev = qevs; i < n; i++, qev++) quark_event_dump(qev, stdout); if (n == 0) quark_queue_block(&qq); } quark_queue_close(&qq); return (1); }
quark_queue_get_events(3) is the meat of the library and contains further useful documentation.
quark-mon(8) is the
easiest way to get started with quark
.
quark_queue_open(3) describes initialization options that can be useful.
quark_event_dump(3), quark_process_lookup(3), quark_queue_block(3), quark_queue_close(3), quark_queue_get_epollfd(3), quark_queue_get_events(3), quark_queue_get_stats(3), quark_queue_open(3), quark-btf(8), quark-mon(8), quark-test(8)
quark
is released under the Apache-2.0
license and contains code under BSD-2, BSD-3, ISC, and zlib Licenses.
quark
started in April 2024.
November 12, 2024 | Linux |