Anteater is a utility program that wraps execution of another program, monitors commands it executes, captures output written to STDOUT/STDERR, and prints the summary in human-friendly html logs.
The project is provided on AUR as anteater. It can be installed by using yay -S anteater or any similar package installer.
By default anteater produces logs in html format, however, when run with -L flag it will only output the logs in text format to standard output without creating any files.
The html logs are located in $HOME/.local/share/anteater/logs/html directory. Each execution creates a separate directory, however all executions are available in the index.html file.
The html is both browser-friendly and lynx-friendly, although some information (e.g. preview of children exit codes) is unavailable in lynx due to lack of javascript support.
There are four kinds of events we monitor:
- fork
- write to
STDOUTorSTDERR - exec
- exit
Rather than monitoring processes, we are interested in monitoring programs.
A program is created upon exec, whereas fork creates a new process belonging to the same program.
Similarily to the process tree, we keep track of a program tree where each node represents a group of processes (a subtree in the process tree). When a process emits an event it is logged to the program it belongs to.
The fork event is transparent in this model. Its only purpose is to trace the newly created process, which belongs to the same program as the parent.
The write event is pretty straightforward - the written data is logged to the current group of the process.
The exec event creates a new program and hence a new node in our tree.
This new node represents the source process and all descendant processes which are still in the same group.
The interaction between forks and execs may be not trivial, so let's consider some examples.
Let's consider the following sequence of events:
- process A execs
- process A writes "a"
- process A forks, creating process B
- process B writes "b"
- process B forks, creating process C
- process C writes "c"
Process tree:
A
|
B
|
C
Program tree:
[A, B, C]
Logs contain a single page:
WRITE a
WRITE b
WRITE c
Let's consider the following sequence of events:
- process A execs
- process A writes "a"
- process A forks, creating process B
- process B forks, creating process C
- process B execs
- process B writes "b"
- process C writes "c"
Process tree:
A
|
B
|
C
Program tree:
A
|
[B, C]
Logs contain two pages:
WRITE a
and
WRITE b
WRITE c
Let's consider the following sequence of events:
- process A execs
- process A writes "a"
- process A forks, creating process B
- process B forks, creating process C
- process C execs
- process C writes "c"
- process B execs
- process B writes "b"
Process tree:
A
|
B
|
C
Program tree:
A
/ \
C B
Logs contain three pages:
WRITE a
and
WRITE b
and
WRITE c
The exit event is logged to all groups that logged all execs of the process.
The backend of Anteater consists of:
event.handtracer.bpf.cwhich collect and sends the events on the kernel sidebpf_providerwhich receives the events on the anteater side, and exposes them to other parts of the program
The frontend consists of:
-
structure_provider- which organizes the events from thebpf_providerinto program tree described in the Event model section. The structure is then displayed in a concrete format by astructure_consumer -
structure_consumerwhich displays the events in a concrete format. It also acts like an abstract factory by creating children consumers upon consuming anexecexec. -
structure/html-structure_consumerthat outputs logs in html format. Thehtml_event_consumer_rootis used as an entrypoint to the structure. In some sense it represents the anteater itself since the only meaningul event for this class is the very firstexeccreated when starting the Anteater program. Thehtml_event_consumerrepresents a consumer for an actual program. -
structure/plain-structure_consumerthat outputs logs in plain text format -
console_logger-event_consumerthat simply prints the logs on theSTDOUT
Events are captured using eBPF. Specifically, we used libbpf, a C API for eBPF.
The main idea of eBPF is that it allows users to hook their own programs (called BPF porgrams) into a running kernel. These programs are subject to a high number of restrictions to ensure safety.
References and useful links regarding eBPF and libbpf:
On archlinux
sudo pacman -Sy bpf clang gtest boost
To compile simply run
make
To run tests run
sudo make test
The executable file is bin/main.
To debug a program run
bin/main <command> [arg]...