From cec8e8b0de8a106501afe1860dbf0379d66cf14d Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:17:05 +0200 Subject: [PATCH 1/8] feat(ebpf): Add Linux process event source Linux run can now load CO-RE programs, snapshot processes through iter/task, and emit enriched execve/exit/clone events instead of failing as an unwired capture stub. --- .github/workflows/linux-port.yml | 39 +++ internal/bootstrap/bootstrap_linux.go | 179 +++++++++++++- internal/bootstrap/source_linux.go | 32 ++- internal/ebpf/btf.go | 33 +++ internal/ebpf/c/clone.bpf.c | 166 +++++++++++++ internal/ebpf/c/common/events.h | 125 ++++++++++ internal/ebpf/c/execve.bpf.c | 85 +++++++ internal/ebpf/c/exit.bpf.c | 57 +++++ internal/ebpf/c/proc_iter.bpf.c | 60 +++++ internal/ebpf/clone_bpfeb.go | 171 +++++++++++++ internal/ebpf/clone_bpfeb.o | Bin 0 -> 30048 bytes internal/ebpf/clone_bpfel.go | 171 +++++++++++++ internal/ebpf/clone_bpfel.o | Bin 0 -> 30048 bytes internal/ebpf/consumer.go | 189 +++++++++++++++ internal/ebpf/consumer_test.go | 98 ++++++++ internal/ebpf/doc.go | 7 +- internal/ebpf/enrich.go | 52 ++++ internal/ebpf/events.go | 142 +++++++++++ internal/ebpf/events_test.go | 150 ++++++++++++ internal/ebpf/execve_bpfeb.go | 156 ++++++++++++ internal/ebpf/execve_bpfeb.o | Bin 0 -> 16352 bytes internal/ebpf/execve_bpfel.go | 156 ++++++++++++ internal/ebpf/execve_bpfel.o | Bin 0 -> 16352 bytes internal/ebpf/exit_bpfeb.go | 150 ++++++++++++ internal/ebpf/exit_bpfeb.o | Bin 0 -> 11336 bytes internal/ebpf/exit_bpfel.go | 150 ++++++++++++ internal/ebpf/exit_bpfel.o | Bin 0 -> 11336 bytes internal/ebpf/gen.go | 26 ++ internal/ebpf/generate.sh | 36 +++ internal/ebpf/loader.go | 220 +++++++++++++++++ internal/ebpf/maps.go | 27 +++ internal/ebpf/proc_iter.go | 64 +++++ internal/ebpf/prociter_bpfeb.go | 147 +++++++++++ internal/ebpf/prociter_bpfeb.o | Bin 0 -> 6264 bytes internal/ebpf/prociter_bpfel.go | 147 +++++++++++ internal/ebpf/prociter_bpfel.o | Bin 0 -> 6264 bytes internal/ebpf/ringbuf.go | 60 +++++ internal/ebpf/source.go | 297 +++++++++++++++++++++++ internal/ebpf/source_integration_test.go | 83 +++++++ internal/ebpf/source_test.go | 118 +++++++++ internal/ebpf/testdata/execve.json | 19 ++ pkg/api/server_linux.go | 62 +++++ pkg/event/event_linux.go | 22 +- pkg/event/params/names_linux.go | 6 + pkg/event/sequencer_linux.go | 55 +++++ pkg/util/signals/signals.go | 2 + pkg/util/signals/signals_linux.go | 47 ++++ 47 files changed, 3784 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/linux-port.yml create mode 100644 internal/ebpf/btf.go create mode 100644 internal/ebpf/c/clone.bpf.c create mode 100644 internal/ebpf/c/common/events.h create mode 100644 internal/ebpf/c/execve.bpf.c create mode 100644 internal/ebpf/c/exit.bpf.c create mode 100644 internal/ebpf/c/proc_iter.bpf.c create mode 100644 internal/ebpf/clone_bpfeb.go create mode 100644 internal/ebpf/clone_bpfeb.o create mode 100644 internal/ebpf/clone_bpfel.go create mode 100644 internal/ebpf/clone_bpfel.o create mode 100644 internal/ebpf/consumer.go create mode 100644 internal/ebpf/consumer_test.go create mode 100644 internal/ebpf/enrich.go create mode 100644 internal/ebpf/events.go create mode 100644 internal/ebpf/events_test.go create mode 100644 internal/ebpf/execve_bpfeb.go create mode 100644 internal/ebpf/execve_bpfeb.o create mode 100644 internal/ebpf/execve_bpfel.go create mode 100644 internal/ebpf/execve_bpfel.o create mode 100644 internal/ebpf/exit_bpfeb.go create mode 100644 internal/ebpf/exit_bpfeb.o create mode 100644 internal/ebpf/exit_bpfel.go create mode 100644 internal/ebpf/exit_bpfel.o create mode 100644 internal/ebpf/gen.go create mode 100755 internal/ebpf/generate.sh create mode 100644 internal/ebpf/loader.go create mode 100644 internal/ebpf/maps.go create mode 100644 internal/ebpf/proc_iter.go create mode 100644 internal/ebpf/prociter_bpfeb.go create mode 100644 internal/ebpf/prociter_bpfeb.o create mode 100644 internal/ebpf/prociter_bpfel.go create mode 100644 internal/ebpf/prociter_bpfel.o create mode 100644 internal/ebpf/ringbuf.go create mode 100644 internal/ebpf/source.go create mode 100644 internal/ebpf/source_integration_test.go create mode 100644 internal/ebpf/source_test.go create mode 100644 internal/ebpf/testdata/execve.json create mode 100644 pkg/api/server_linux.go create mode 100644 pkg/event/sequencer_linux.go create mode 100644 pkg/util/signals/signals_linux.go diff --git a/.github/workflows/linux-port.yml b/.github/workflows/linux-port.yml new file mode 100644 index 000000000..1c4c950c6 --- /dev/null +++ b/.github/workflows/linux-port.yml @@ -0,0 +1,39 @@ +name: linux-port + +on: + pull_request: + branches: + - linux-port + workflow_dispatch: + +concurrency: + group: linux-port-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +env: + GO_VERSION: 1.26.x + +jobs: + ebpf: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + - name: Install clang + run: | + sudo apt-get update + sudo apt-get install -y clang llvm + clang --version + - name: Check generation drift + run: | + chmod +x internal/ebpf/generate.sh + ./internal/ebpf/generate.sh + git diff --exit-code -- internal/ebpf + - name: Unit tests + run: go test ./internal/ebpf ./internal/bootstrap ./pkg/event ./pkg/ps ./pkg/api ./pkg/util/signals + - name: Privileged process source + run: sudo -E env "PATH=$PATH" go test -tags ebpf_integration -count=1 ./internal/ebpf diff --git a/internal/bootstrap/bootstrap_linux.go b/internal/bootstrap/bootstrap_linux.go index d9d3abc45..26ca98856 100644 --- a/internal/bootstrap/bootstrap_linux.go +++ b/internal/bootstrap/bootstrap_linux.go @@ -23,45 +23,202 @@ package bootstrap import ( "errors" + "fmt" + "os" + "syscall" + "github.com/rabbitstack/fibratus/pkg/aggregator" + "github.com/rabbitstack/fibratus/pkg/alertsender" + "github.com/rabbitstack/fibratus/pkg/api" "github.com/rabbitstack/fibratus/pkg/config" + "github.com/rabbitstack/fibratus/pkg/filter" + "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/rabbitstack/fibratus/pkg/rules" + "github.com/rabbitstack/fibratus/pkg/util/multierror" + "github.com/rabbitstack/fibratus/pkg/util/signals" + "github.com/rabbitstack/fibratus/pkg/util/version" + log "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) -var ErrCaptureNotWired = errors.New("linux event capture is not wired yet") +const instanceLockPath = "/tmp/fibratus.lock" +// ErrAlreadyRunning signals a Fibratus process is already running. +var ErrAlreadyRunning = errors.New("an instance of Fibratus process is already running in the system") + +// App centralizes the core building blocks responsible +// for event acquisition, rule engine initialization, +// and event routing to the output sinks. type App struct { - config *config.Config - evs *EventSourceControl + config *config.Config + evs *EventSourceControl + engine *rules.Engine + psnap ps.Snapshotter + agg *aggregator.BufferedAggregator + signals chan struct{} + lock *os.File } +// Option enables changing the behaviour of the bootstrap application. type Option func(*opts) -type opts struct{} +type opts struct { + installSignals bool +} +// WithSignals installs signal handlers. func WithSignals() Option { - return func(*opts) {} + return func(o *opts) { + o.installSignals = true + } } +// WithDebugPrivilege is a no-op on Linux. func WithDebugPrivilege() Option { return func(*opts) {} } +// NewApp constructs a new bootstrap application with the specified configuration +// and a list of options. func NewApp(cfg *config.Config, options ...Option) (*App, error) { if err := InitConfigAndLogger(cfg); err != nil { return nil, err } - return &App{config: cfg, evs: NewEventSourceControl(cfg)}, nil + var o opts + var sigs chan struct{} + for _, opt := range options { + opt(&o) + } + if o.installSignals { + sigs = signals.Install() + } + + psnap := ps.NewSnapshotter() + + var engine *rules.Engine + var rs *config.RulesCompileResult + if cfg.Filters != nil && cfg.Filters.Rules.Enabled && !cfg.ForwardMode && !cfg.IsCaptureSet() && !cfg.IsFilamentSet() { + engine = rules.NewEngine(psnap, cfg) + var err error + rs, err = engine.Compile() + if err != nil { + return nil, err + } + if rs != nil { + log.Infof("rules compile summary: %s", rs) + } + } else { + log.Info("rule engine is disabled") + } + + return &App{ + config: cfg, + evs: NewEventSourceControl(psnap, cfg, rs), + engine: engine, + psnap: psnap, + signals: sigs, + }, nil } -func (f *App) Run([]string) error { - if err := f.evs.Open(f.config); err != nil { +// Run configures and opens the event source to start consuming events. +func (f *App) Run(args []string) error { + if f.evs == nil { + panic("event source is nil") + } + cfg := f.config + + if cfg.IsFilamentSet() { + return fmt.Errorf("filaments are not supported on Linux") + } + + lock, err := acquireInstanceLock() + if err != nil { + if errors.Is(err, syscall.EWOULDBLOCK) || errors.Is(err, syscall.EAGAIN) { + return ErrAlreadyRunning + } + return fmt.Errorf("acquiring instance lock: %w", err) + } + f.lock = lock + + log.Infof("bootstrapping with pid %d. Version: %s", os.Getpid(), version.Get()) + log.Infof("configuration options: %s", cfg.Print()) + + fltr, err := filter.NewFromCLI(args, cfg) + if err != nil { + return err + } + if fltr != nil { + f.evs.SetFilter(fltr) + } + if f.engine != nil { + f.evs.RegisterEventListener(f.engine) + } + + if err := f.evs.Open(cfg); err != nil { + return multierror.Wrap(err, f.evs.Close()) + } + + f.agg, err = aggregator.NewBuffered( + f.evs.Events(), + f.evs.Errors(), + cfg.Aggregator, + cfg.Output, + cfg.Transformers, + cfg.Alertsenders, + ) + if err != nil { return err } - return ErrCaptureNotWired + return api.StartServer(cfg) } -func (*App) Wait() {} +// Wait waits for the app to receive the termination signal. +func (f *App) Wait() { + if f.signals != nil { + <-f.signals + } +} +// Shutdown is responsible for tearing down everything gracefully. func (f *App) Shutdown() error { - return f.evs.Close() + errs := make([]error, 0) + if f.evs != nil { + if err := f.evs.Close(); err != nil { + errs = append(errs, err) + } + } + if f.psnap != nil { + if err := f.psnap.Close(); err != nil { + errs = append(errs, err) + } + } + if f.agg != nil { + if err := f.agg.Stop(); err != nil { + errs = append(errs, err) + } + } + if err := api.CloseServer(); err != nil { + errs = append(errs, err) + } + if err := alertsender.ShutdownAll(); err != nil { + errs = append(errs, err) + } + if f.lock != nil { + _ = unix.Flock(int(f.lock.Fd()), unix.LOCK_UN) + _ = f.lock.Close() + _ = os.Remove(instanceLockPath) + } + return multierror.Wrap(errs...) +} + +func acquireInstanceLock() (*os.File, error) { + f, err := os.OpenFile(instanceLockPath, os.O_CREATE|os.O_RDWR, 0o600) + if err != nil { + return nil, err + } + if err := unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil { + _ = f.Close() + return nil, err + } + return f, nil } diff --git a/internal/bootstrap/source_linux.go b/internal/bootstrap/source_linux.go index b4df2a74e..8096eba53 100644 --- a/internal/bootstrap/source_linux.go +++ b/internal/bootstrap/source_linux.go @@ -22,15 +22,25 @@ package bootstrap import ( + "github.com/rabbitstack/fibratus/internal/ebpf" "github.com/rabbitstack/fibratus/pkg/config" + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/filter" + "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/rabbitstack/fibratus/pkg/source" ) +// EventSourceControl abstracts away the management of event sources. type EventSourceControl struct { - evs *stubEventSource + evs source.EventSource } -func NewEventSourceControl(*config.Config) *EventSourceControl { - return &EventSourceControl{evs: &stubEventSource{}} +func NewEventSourceControl( + psnap ps.Snapshotter, + cfg *config.Config, + compiler *config.RulesCompileResult, +) *EventSourceControl { + return &EventSourceControl{evs: ebpf.NewEventSource(psnap, cfg, compiler)} } func (s *EventSourceControl) Open(cfg *config.Config) error { @@ -41,10 +51,18 @@ func (s *EventSourceControl) Close() error { return s.evs.Close() } -type stubEventSource struct{} +func (s *EventSourceControl) Errors() <-chan error { + return s.evs.Errors() +} + +func (s *EventSourceControl) Events() <-chan *event.Event { + return s.evs.Events() +} -func (*stubEventSource) Open(*config.Config) error { - return nil +func (s *EventSourceControl) SetFilter(f filter.Filter) { + s.evs.SetFilter(f) } -func (*stubEventSource) Close() error { return nil } +func (s *EventSourceControl) RegisterEventListener(lis event.Listener) { + s.evs.RegisterEventListener(lis) +} diff --git a/internal/ebpf/btf.go b/internal/ebpf/btf.go new file mode 100644 index 000000000..0e4d96d78 --- /dev/null +++ b/internal/ebpf/btf.go @@ -0,0 +1,33 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "fmt" + "runtime" +) + +func checkRuntimeSupport() (*PrerequisiteReport, error) { + if runtime.GOARCH != "amd64" { + return nil, fmt.Errorf("linux eBPF capture requires amd64, got %s", runtime.GOARCH) + } + return ProbePrerequisites() +} diff --git a/internal/ebpf/c/clone.bpf.c b/internal/ebpf/c/clone.bpf.c new file mode 100644 index 000000000..d2e5bdfde --- /dev/null +++ b/internal/ebpf/c/clone.bpf.c @@ -0,0 +1,166 @@ +//go:build ignore + +#include "common/events.h" +#include "bpf_core_read.h" +#include "bpf_tracing.h" + +char LICENSE[] SEC("license") = "Dual MIT/GPL"; + +static __always_inline int handle_clone_enter(u64 flags, u32 syscall_id) +{ + u64 key = bpf_get_current_pid_tgid(); + struct scratch_value val = {}; + + val.arg0 = flags; + val.arg1 = syscall_id; + bpf_map_update_elem(&scratch, &key, &val, BPF_ANY); + return 0; +} + +static __always_inline int handle_clone_exit(long ret, u32 syscall_id) +{ + u64 key = bpf_get_current_pid_tgid(); + struct scratch_value *val; + struct fibratus_event *e; + struct task_struct *task; + struct task_struct *parent; + const struct cred *cred; + + /* Successful clones are emitted from sched_process_fork with child identity. + * Leave scratch in place so the fork handler can recover clone flags. + */ + if (ret >= 0) + return 0; + + e = reserve_event(); + if (!e) + return 0; + + e->kind = EVT_KIND_CLONE; + e->type = EVT_TYPE_CLONE; + e->syscall_id = syscall_id; + e->retval = ret; + fill_current_ids(e); + + task = (struct task_struct *)bpf_get_current_task(); + e->start_boottime = BPF_CORE_READ(task, start_boottime); + parent = BPF_CORE_READ(task, real_parent); + if (parent) + e->ppid = BPF_CORE_READ(parent, tgid); + cred = BPF_CORE_READ(task, real_cred); + if (cred) { + e->uid = BPF_CORE_READ(cred, uid.val); + e->gid = BPF_CORE_READ(cred, gid.val); + } + + val = bpf_map_lookup_elem(&scratch, &key); + if (val) { + e->clone_flags = val->arg0; + if (!e->syscall_id) + e->syscall_id = (u32)val->arg1; + } + bpf_map_delete_elem(&scratch, &key); + + bpf_ringbuf_submit(e, 0); + return 0; +} + +SEC("tp/syscalls/sys_enter_clone") +int handle_sys_enter_clone(struct trace_event_raw_sys_enter *ctx) +{ + return handle_clone_enter(ctx->args[0], (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_clone") +int handle_sys_exit_clone(struct trace_event_raw_sys_exit *ctx) +{ + return handle_clone_exit(ctx->ret, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_enter_clone3") +int handle_sys_enter_clone3(struct trace_event_raw_sys_enter *ctx) +{ + u64 flags = 0; + + bpf_probe_read_user(&flags, sizeof(flags), (const void *)ctx->args[0]); + return handle_clone_enter(flags, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_clone3") +int handle_sys_exit_clone3(struct trace_event_raw_sys_exit *ctx) +{ + return handle_clone_exit(ctx->ret, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_enter_fork") +int handle_sys_enter_fork(struct trace_event_raw_sys_enter *ctx) +{ + return handle_clone_enter(0, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_fork") +int handle_sys_exit_fork(struct trace_event_raw_sys_exit *ctx) +{ + return handle_clone_exit(ctx->ret, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_enter_vfork") +int handle_sys_enter_vfork(struct trace_event_raw_sys_enter *ctx) +{ + return handle_clone_enter(0, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_vfork") +int handle_sys_exit_vfork(struct trace_event_raw_sys_exit *ctx) +{ + return handle_clone_exit(ctx->ret, (u32)ctx->id); +} + +SEC("tp_btf/sched_process_fork") +int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_struct *child) +{ + struct fibratus_event *e; + struct scratch_value *val; + const struct cred *cred; + struct task_struct *real_parent; + u64 key; + + if (!child || !parent) + return 0; + + e = reserve_event(); + if (!e) + return 0; + + e->kind = EVT_KIND_CLONE; + e->type = EVT_TYPE_CLONE; + e->retval = child->tgid; + e->pid = child->tgid; + e->tid = child->pid; + e->tgid = child->tgid; + e->start_boottime = child->start_boottime; + __builtin_memcpy(&e->comm, child->comm, sizeof(e->comm)); + + real_parent = child->real_parent; + if (real_parent) + e->ppid = real_parent->tgid; + else + e->ppid = parent->tgid; + + cred = child->real_cred; + if (cred) { + e->uid = cred->uid.val; + e->gid = cred->gid.val; + } + + key = ((u64)parent->tgid << 32) | (u32)parent->pid; + val = bpf_map_lookup_elem(&scratch, &key); + if (val) { + e->clone_flags = val->arg0; + e->syscall_id = (u32)val->arg1; + bpf_map_delete_elem(&scratch, &key); + } + + bpf_ringbuf_submit(e, 0); + return 0; +} diff --git a/internal/ebpf/c/common/events.h b/internal/ebpf/c/common/events.h new file mode 100644 index 000000000..56400585a --- /dev/null +++ b/internal/ebpf/c/common/events.h @@ -0,0 +1,125 @@ +/* Shared event and map definitions for the Linux eBPF backend. */ + +#pragma once + +#include "vmlinux.h" +#include "bpf_helpers.h" + +#ifndef BPF_ANY +#define BPF_ANY 0 +#endif + +#define EVT_FILENAME_LEN 256 + +struct trace_event_raw_sys_enter { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + long id; + unsigned long args[6]; +}; + +struct trace_event_raw_sys_exit { + unsigned short common_type; + unsigned char common_flags; + unsigned char common_preempt_count; + int common_pid; + long id; + long ret; +}; + +enum fibratus_event_kind { + EVT_KIND_SNAPSHOT = 0, + EVT_KIND_EXECVE = 1, + EVT_KIND_EXIT = 2, + EVT_KIND_CLONE = 3, +}; + +/* Stable semantic type IDs. Keep in sync with pkg/event.Type on Linux. */ +#define EVT_TYPE_UNKNOWN 0 +#define EVT_TYPE_EXECVE 1 +#define EVT_TYPE_EXIT 2 +#define EVT_TYPE_CLONE 3 + +struct fibratus_event { + u32 kind; + u32 type; + u32 pid; + u32 tid; + u32 tgid; + u32 ppid; + u32 uid; + u32 gid; + u32 syscall_id; + u32 pad; + s64 retval; + u64 clone_flags; + u64 start_boottime; + u64 timestamp_ns; + u8 comm[TASK_COMM_LEN]; + u8 filename[EVT_FILENAME_LEN]; +}; + +struct scratch_value { + u64 arg0; + u64 arg1; + u8 filename[EVT_FILENAME_LEN]; +}; + +struct { + __uint(type, BPF_MAP_TYPE_RINGBUF); + __uint(max_entries, 1 << 24); +} events SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(max_entries, 1); + __type(key, u32); + __type(value, u64); +} drop_count SEC(".maps"); + +struct { + __uint(type, BPF_MAP_TYPE_PERCPU_HASH); + __uint(max_entries, 8192); + __type(key, u64); + __type(value, struct scratch_value); +} scratch SEC(".maps"); + +static __always_inline void account_drop(void) +{ + u32 key = 0; + u64 *count; + + count = bpf_map_lookup_elem(&drop_count, &key); + if (!count) + return; + __sync_fetch_and_add(count, 1); +} + +static __always_inline struct fibratus_event *reserve_event(void) +{ + struct fibratus_event *e; + + e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); + if (!e) { + account_drop(); + return NULL; + } + __builtin_memset(e, 0, sizeof(*e)); + e->timestamp_ns = bpf_ktime_get_ns(); + return e; +} + +static __always_inline void fill_current_ids(struct fibratus_event *e) +{ + u64 id = bpf_get_current_pid_tgid(); + u64 uidgid = bpf_get_current_uid_gid(); + + e->tgid = id >> 32; + e->tid = (u32)id; + e->pid = e->tgid; + e->uid = (u32)uidgid; + e->gid = uidgid >> 32; + bpf_get_current_comm(&e->comm, sizeof(e->comm)); +} diff --git a/internal/ebpf/c/execve.bpf.c b/internal/ebpf/c/execve.bpf.c new file mode 100644 index 000000000..55ec98d4c --- /dev/null +++ b/internal/ebpf/c/execve.bpf.c @@ -0,0 +1,85 @@ +//go:build ignore + +#include "common/events.h" +#include "bpf_core_read.h" +#include "bpf_tracing.h" + +char LICENSE[] SEC("license") = "Dual MIT/GPL"; + +static __always_inline int handle_exec_enter(const char *filename, u32 syscall_id) +{ + u64 key = bpf_get_current_pid_tgid(); + struct scratch_value val = {}; + + val.arg1 = syscall_id; + if (filename) + bpf_probe_read_user_str(&val.filename, sizeof(val.filename), filename); + bpf_map_update_elem(&scratch, &key, &val, BPF_ANY); + return 0; +} + +static __always_inline int handle_exec_exit(long ret, u32 syscall_id) +{ + u64 key = bpf_get_current_pid_tgid(); + struct scratch_value *val; + struct fibratus_event *e; + struct task_struct *task; + struct task_struct *parent; + const struct cred *cred; + + e = reserve_event(); + if (!e) + return 0; + + e->kind = EVT_KIND_EXECVE; + e->type = EVT_TYPE_EXECVE; + e->syscall_id = syscall_id; + e->retval = ret; + fill_current_ids(e); + + task = (struct task_struct *)bpf_get_current_task(); + e->start_boottime = BPF_CORE_READ(task, start_boottime); + parent = BPF_CORE_READ(task, real_parent); + if (parent) + e->ppid = BPF_CORE_READ(parent, tgid); + cred = BPF_CORE_READ(task, real_cred); + if (cred) { + e->uid = BPF_CORE_READ(cred, uid.val); + e->gid = BPF_CORE_READ(cred, gid.val); + } + + val = bpf_map_lookup_elem(&scratch, &key); + if (val) { + if (!e->syscall_id) + e->syscall_id = (u32)val->arg1; + __builtin_memcpy(&e->filename, val->filename, sizeof(e->filename)); + } + bpf_map_delete_elem(&scratch, &key); + + bpf_ringbuf_submit(e, 0); + return 0; +} + +SEC("tp/syscalls/sys_enter_execve") +int handle_sys_enter_execve(struct trace_event_raw_sys_enter *ctx) +{ + return handle_exec_enter((const char *)ctx->args[0], (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_execve") +int handle_sys_exit_execve(struct trace_event_raw_sys_exit *ctx) +{ + return handle_exec_exit(ctx->ret, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_enter_execveat") +int handle_sys_enter_execveat(struct trace_event_raw_sys_enter *ctx) +{ + return handle_exec_enter((const char *)ctx->args[1], (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_execveat") +int handle_sys_exit_execveat(struct trace_event_raw_sys_exit *ctx) +{ + return handle_exec_exit(ctx->ret, (u32)ctx->id); +} diff --git a/internal/ebpf/c/exit.bpf.c b/internal/ebpf/c/exit.bpf.c new file mode 100644 index 000000000..00e32465c --- /dev/null +++ b/internal/ebpf/c/exit.bpf.c @@ -0,0 +1,57 @@ +//go:build ignore + +#include "common/events.h" +#include "bpf_core_read.h" +#include "bpf_tracing.h" + +char LICENSE[] SEC("license") = "Dual MIT/GPL"; + +static __always_inline int handle_exit(long ret, u32 syscall_id) +{ + struct fibratus_event *e; + struct task_struct *task; + struct task_struct *parent; + const struct cred *cred; + u64 id; + + id = bpf_get_current_pid_tgid(); + /* Only emit process exits from the thread-group leader. */ + if ((u32)id != (id >> 32)) + return 0; + + e = reserve_event(); + if (!e) + return 0; + + e->kind = EVT_KIND_EXIT; + e->type = EVT_TYPE_EXIT; + e->syscall_id = syscall_id; + e->retval = ret; + fill_current_ids(e); + + task = (struct task_struct *)bpf_get_current_task(); + e->start_boottime = BPF_CORE_READ(task, start_boottime); + parent = BPF_CORE_READ(task, real_parent); + if (parent) + e->ppid = BPF_CORE_READ(parent, tgid); + cred = BPF_CORE_READ(task, real_cred); + if (cred) { + e->uid = BPF_CORE_READ(cred, uid.val); + e->gid = BPF_CORE_READ(cred, gid.val); + } + + bpf_ringbuf_submit(e, 0); + return 0; +} + +SEC("tp/syscalls/sys_exit_exit_group") +int handle_sys_exit_exit_group(struct trace_event_raw_sys_exit *ctx) +{ + return handle_exit(ctx->ret, (u32)ctx->id); +} + +SEC("tp/syscalls/sys_exit_exit") +int handle_sys_exit_exit(struct trace_event_raw_sys_exit *ctx) +{ + return handle_exit(ctx->ret, (u32)ctx->id); +} diff --git a/internal/ebpf/c/proc_iter.bpf.c b/internal/ebpf/c/proc_iter.bpf.c new file mode 100644 index 000000000..31c7b8be4 --- /dev/null +++ b/internal/ebpf/c/proc_iter.bpf.c @@ -0,0 +1,60 @@ +//go:build ignore + +#include "common/events.h" +#include "bpf_core_read.h" + +char LICENSE[] SEC("license") = "Dual MIT/GPL"; + +/* Task iterator baseline scanner. + * + * Intentionally omits bpf_d_path (available from Linux 5.10). Executable path + * and cmdline are left empty here and are filled best-effort from + * /proc//{exe,cmdline} in userspace. + * + * ctx->task is a trusted BTF pointer, so field access uses CO-RE direct reads + * rather than bpf_probe_read-based helpers. + */ +SEC("iter/task") +int dump_task(struct bpf_iter__task *ctx) +{ + struct task_struct *task; + struct task_struct *parent; + const struct cred *cred; + struct fibratus_event *e; + pid_t pid; + pid_t tgid; + + task = ctx->task; + if (!task) + return 0; + + pid = task->pid; + tgid = task->tgid; + if (pid != tgid) + return 0; + + e = reserve_event(); + if (!e) + return 0; + + e->kind = EVT_KIND_SNAPSHOT; + e->type = EVT_TYPE_UNKNOWN; + e->pid = tgid; + e->tid = pid; + e->tgid = tgid; + e->start_boottime = task->start_boottime; + __builtin_memcpy(&e->comm, task->comm, sizeof(e->comm)); + + parent = task->real_parent; + if (parent) + e->ppid = parent->tgid; + + cred = task->real_cred; + if (cred) { + e->uid = cred->uid.val; + e->gid = cred->gid.val; + } + + bpf_ringbuf_submit(e, 0); + return 0; +} diff --git a/internal/ebpf/clone_bpfeb.go b/internal/ebpf/clone_bpfeb.go new file mode 100644 index 000000000..0c1de89c0 --- /dev/null +++ b/internal/ebpf/clone_bpfeb.go @@ -0,0 +1,171 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build (mips || mips64 || ppc64 || s390x) && linux + +package ebpf + +import ( + "bytes" + _ "embed" + "fmt" + "io" + "structs" + + "github.com/cilium/ebpf" +) + +type cloneScratchValue struct { + _ structs.HostLayout + Arg0 uint64 + Arg1 uint64 + Filename [256]uint8 +} + +// loadClone returns the embedded CollectionSpec for clone. +func loadClone() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_CloneBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load clone: %w", err) + } + + return spec, err +} + +// loadCloneObjects loads clone and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *cloneObjects +// *clonePrograms +// *cloneMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadCloneObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadClone() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// cloneSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneSpecs struct { + cloneProgramSpecs + cloneMapSpecs + cloneVariableSpecs +} + +// cloneProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneProgramSpecs struct { + HandleSchedProcessFork *ebpf.ProgramSpec `ebpf:"handle_sched_process_fork"` + HandleSysEnterClone *ebpf.ProgramSpec `ebpf:"handle_sys_enter_clone"` + HandleSysEnterClone3 *ebpf.ProgramSpec `ebpf:"handle_sys_enter_clone3"` + HandleSysEnterFork *ebpf.ProgramSpec `ebpf:"handle_sys_enter_fork"` + HandleSysEnterVfork *ebpf.ProgramSpec `ebpf:"handle_sys_enter_vfork"` + HandleSysExitClone *ebpf.ProgramSpec `ebpf:"handle_sys_exit_clone"` + HandleSysExitClone3 *ebpf.ProgramSpec `ebpf:"handle_sys_exit_clone3"` + HandleSysExitFork *ebpf.ProgramSpec `ebpf:"handle_sys_exit_fork"` + HandleSysExitVfork *ebpf.ProgramSpec `ebpf:"handle_sys_exit_vfork"` +} + +// cloneMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneMapSpecs struct { + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` +} + +// cloneVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneVariableSpecs struct { +} + +// cloneObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type cloneObjects struct { + clonePrograms + cloneMaps + cloneVariables +} + +func (o *cloneObjects) Close() error { + return _CloneClose( + &o.clonePrograms, + &o.cloneMaps, + ) +} + +// cloneMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type cloneMaps struct { + DropCount *ebpf.Map `ebpf:"drop_count"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` +} + +func (m *cloneMaps) Close() error { + return _CloneClose( + m.DropCount, + m.Events, + m.Scratch, + ) +} + +// cloneVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type cloneVariables struct { +} + +// clonePrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type clonePrograms struct { + HandleSchedProcessFork *ebpf.Program `ebpf:"handle_sched_process_fork"` + HandleSysEnterClone *ebpf.Program `ebpf:"handle_sys_enter_clone"` + HandleSysEnterClone3 *ebpf.Program `ebpf:"handle_sys_enter_clone3"` + HandleSysEnterFork *ebpf.Program `ebpf:"handle_sys_enter_fork"` + HandleSysEnterVfork *ebpf.Program `ebpf:"handle_sys_enter_vfork"` + HandleSysExitClone *ebpf.Program `ebpf:"handle_sys_exit_clone"` + HandleSysExitClone3 *ebpf.Program `ebpf:"handle_sys_exit_clone3"` + HandleSysExitFork *ebpf.Program `ebpf:"handle_sys_exit_fork"` + HandleSysExitVfork *ebpf.Program `ebpf:"handle_sys_exit_vfork"` +} + +func (p *clonePrograms) Close() error { + return _CloneClose( + p.HandleSchedProcessFork, + p.HandleSysEnterClone, + p.HandleSysEnterClone3, + p.HandleSysEnterFork, + p.HandleSysEnterVfork, + p.HandleSysExitClone, + p.HandleSysExitClone3, + p.HandleSysExitFork, + p.HandleSysExitVfork, + ) +} + +func _CloneClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed clone_bpfeb.o +var _CloneBytes []byte diff --git a/internal/ebpf/clone_bpfeb.o b/internal/ebpf/clone_bpfeb.o new file mode 100644 index 0000000000000000000000000000000000000000..bd257bec4254700d3b62bd78dd04128ab236de7a GIT binary patch literal 30048 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 literal 0 HcmV?d00001 diff --git a/internal/ebpf/clone_bpfel.go b/internal/ebpf/clone_bpfel.go new file mode 100644 index 000000000..36266195f --- /dev/null +++ b/internal/ebpf/clone_bpfel.go @@ -0,0 +1,171 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux + +package ebpf + +import ( + "bytes" + _ "embed" + "fmt" + "io" + "structs" + + "github.com/cilium/ebpf" +) + +type cloneScratchValue struct { + _ structs.HostLayout + Arg0 uint64 + Arg1 uint64 + Filename [256]uint8 +} + +// loadClone returns the embedded CollectionSpec for clone. +func loadClone() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_CloneBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load clone: %w", err) + } + + return spec, err +} + +// loadCloneObjects loads clone and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *cloneObjects +// *clonePrograms +// *cloneMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadCloneObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadClone() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// cloneSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneSpecs struct { + cloneProgramSpecs + cloneMapSpecs + cloneVariableSpecs +} + +// cloneProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneProgramSpecs struct { + HandleSchedProcessFork *ebpf.ProgramSpec `ebpf:"handle_sched_process_fork"` + HandleSysEnterClone *ebpf.ProgramSpec `ebpf:"handle_sys_enter_clone"` + HandleSysEnterClone3 *ebpf.ProgramSpec `ebpf:"handle_sys_enter_clone3"` + HandleSysEnterFork *ebpf.ProgramSpec `ebpf:"handle_sys_enter_fork"` + HandleSysEnterVfork *ebpf.ProgramSpec `ebpf:"handle_sys_enter_vfork"` + HandleSysExitClone *ebpf.ProgramSpec `ebpf:"handle_sys_exit_clone"` + HandleSysExitClone3 *ebpf.ProgramSpec `ebpf:"handle_sys_exit_clone3"` + HandleSysExitFork *ebpf.ProgramSpec `ebpf:"handle_sys_exit_fork"` + HandleSysExitVfork *ebpf.ProgramSpec `ebpf:"handle_sys_exit_vfork"` +} + +// cloneMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneMapSpecs struct { + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` +} + +// cloneVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type cloneVariableSpecs struct { +} + +// cloneObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type cloneObjects struct { + clonePrograms + cloneMaps + cloneVariables +} + +func (o *cloneObjects) Close() error { + return _CloneClose( + &o.clonePrograms, + &o.cloneMaps, + ) +} + +// cloneMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type cloneMaps struct { + DropCount *ebpf.Map `ebpf:"drop_count"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` +} + +func (m *cloneMaps) Close() error { + return _CloneClose( + m.DropCount, + m.Events, + m.Scratch, + ) +} + +// cloneVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type cloneVariables struct { +} + +// clonePrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadCloneObjects or ebpf.CollectionSpec.LoadAndAssign. +type clonePrograms struct { + HandleSchedProcessFork *ebpf.Program `ebpf:"handle_sched_process_fork"` + HandleSysEnterClone *ebpf.Program `ebpf:"handle_sys_enter_clone"` + HandleSysEnterClone3 *ebpf.Program `ebpf:"handle_sys_enter_clone3"` + HandleSysEnterFork *ebpf.Program `ebpf:"handle_sys_enter_fork"` + HandleSysEnterVfork *ebpf.Program `ebpf:"handle_sys_enter_vfork"` + HandleSysExitClone *ebpf.Program `ebpf:"handle_sys_exit_clone"` + HandleSysExitClone3 *ebpf.Program `ebpf:"handle_sys_exit_clone3"` + HandleSysExitFork *ebpf.Program `ebpf:"handle_sys_exit_fork"` + HandleSysExitVfork *ebpf.Program `ebpf:"handle_sys_exit_vfork"` +} + +func (p *clonePrograms) Close() error { + return _CloneClose( + p.HandleSchedProcessFork, + p.HandleSysEnterClone, + p.HandleSysEnterClone3, + p.HandleSysEnterFork, + p.HandleSysEnterVfork, + p.HandleSysExitClone, + p.HandleSysExitClone3, + p.HandleSysExitFork, + p.HandleSysExitVfork, + ) +} + +func _CloneClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed clone_bpfel.o +var _CloneBytes []byte diff --git a/internal/ebpf/clone_bpfel.o b/internal/ebpf/clone_bpfel.o new file mode 100644 index 0000000000000000000000000000000000000000..145a50e697c14faecd5b5dd1b9a377b028abd6b8 GIT binary patch literal 30048 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 literal 0 HcmV?d00001 diff --git a/internal/ebpf/consumer.go b/internal/ebpf/consumer.go new file mode 100644 index 000000000..ca1d022cf --- /dev/null +++ b/internal/ebpf/consumer.go @@ -0,0 +1,189 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "expvar" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/rabbitstack/fibratus/pkg/ps" + pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" +) + +var ( + eventsProcessed = expvar.NewInt("ebpf.events.processed") + eventsExcluded = expvar.NewInt("ebpf.events.excluded") + eventsUnknown = expvar.NewInt("ebpf.events.unknown") + parseErrors = expvar.NewInt("ebpf.events.parse.errors") + pendingQueued = expvar.NewInt("ebpf.startup.pending.queued") + pendingDropped = expvar.NewInt("ebpf.startup.pending.dropped") + replayApplied = expvar.NewInt("ebpf.startup.replay.applied") + snapshotUpserts = expvar.NewInt("ebpf.startup.snapshot.upserts") + lateSnapshots = expvar.NewInt("ebpf.startup.snapshot.late") + enrichmentMiss = expvar.NewInt("ebpf.enrichment.miss") + ringbufDrops = expvar.NewInt("ebpf.ringbuf.drops") +) + +func applyProcessState(psnap ps.Snapshotter, evt *event.Event) { + if evt == nil || psnap == nil { + return + } + switch { + case evt.Type == event.Execve && succeeded(evt): + ps := buildPS(evt, psnap) + evt.PS = ps + _ = psnap.Write(evt) + case evt.Type == event.Clone && succeeded(evt) && evt.IsCreateProcess(): + ps := buildPS(evt, psnap) + evt.PS = ps + psnap.Put(ps) + case evt.Type == event.Clone && succeeded(evt) && evt.IsCreateThread(): + ok, existing := psnap.Find(evt.PID) + if ok && existing != nil { + evt.PS = existing + _ = psnap.AddThread(evt) + } + case evt.Type == event.Exit: + ok, existing := psnap.Find(evt.PID) + if ok { + evt.PS = existing + } + _ = psnap.Remove(evt) + default: + ok, existing := psnap.Find(evt.PID) + if ok { + evt.PS = existing + } + } + if evt.PS == nil { + ok, existing := psnap.Find(evt.PID) + if ok { + evt.PS = existing + } + } +} + +func upsertSnapshot(psnap ps.Snapshotter, rec *pstypes.PS) { + if psnap == nil || rec == nil { + return + } + ok, existing := psnap.Find(rec.PID) + if !ok || existing == nil { + psnap.Put(rec) + snapshotUpserts.Add(1) + return + } + if existing.StartBootTime != rec.StartBootTime { + psnap.Put(rec) + snapshotUpserts.Add(1) + return + } + mergePS(existing, rec) + snapshotUpserts.Add(1) +} + +func mergePS(dst, src *pstypes.PS) { + if src.Name != "" { + dst.Name = src.Name + } + if src.Exe != "" { + dst.Exe = src.Exe + } + if src.Cmdline != "" { + dst.Cmdline = src.Cmdline + } + if src.Ppid != 0 { + dst.Ppid = src.Ppid + } + if src.UID != 0 { + dst.UID = src.UID + } + if src.GID != 0 { + dst.GID = src.GID + } + if src.StartBootTime != 0 { + dst.StartBootTime = src.StartBootTime + } +} + +func buildPS(evt *event.Event, psnap ps.Snapshotter) *pstypes.PS { + ok, existing := psnap.Find(evt.PID) + exe := evt.GetParamAsString(params.Exe) + cmdline := evt.GetParamAsString(params.Cmdline) + name := evt.GetParamAsString(params.ProcessName) + if exe == "" && existing != nil { + exe = existing.Exe + } + if cmdline == "" && existing != nil { + cmdline = existing.Cmdline + } + if name == "" { + name = baseName(exe) + } + ps := &pstypes.PS{ + PID: evt.PID, + Ppid: evt.GetParamAsUint64(params.ProcessParentID), + Name: name, + Cmdline: cmdline, + Exe: exe, + Args: splitCmdline(cmdline), + StartBootTime: evt.GetParamAsUint64(params.StartBootTime), + UID: evt.GetParamAsUint32(params.UID), + GID: evt.GetParamAsUint32(params.GID), + Threads: make(map[uint64]pstypes.Thread), + } + if ok && existing != nil && existing.StartBootTime == ps.StartBootTime && existing.Threads != nil { + ps.Threads = existing.Threads + ps.Parent = existing.Parent + } + if ok, parent := psnap.Find(ps.Ppid); ok { + ps.Parent = parent + } + return ps +} + +func succeeded(evt *event.Event) bool { + ret, err := evt.Params.GetInt64(params.Retval) + if err != nil { + return true + } + return ret >= 0 +} + +func enrichEvent(evt *event.Event) { + if evt == nil { + return + } + exe, cmdline, err := enrichFromProc(evt.PID) + if err != nil { + enrichmentMiss.Add(1) + } + if exe != "" { + evt.Params.Append(params.Exe, params.Path, exe) + if evt.GetParamAsString(params.ProcessName) == "" { + evt.Params.Append(params.ProcessName, params.String, baseName(exe)) + } + } + if cmdline != "" { + evt.Params.Append(params.Cmdline, params.String, cmdline) + } +} diff --git a/internal/ebpf/consumer_test.go b/internal/ebpf/consumer_test.go new file mode 100644 index 000000000..3977c626f --- /dev/null +++ b/internal/ebpf/consumer_test.go @@ -0,0 +1,98 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "testing" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/rabbitstack/fibratus/pkg/ps" + pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestApplyProcessStateExecveAndPIDReuse(t *testing.T) { + snap := ps.NewSnapshotter() + first := sampleExecve().toEvent(1) + applyProcessState(snap, first) + ok, got := snap.Find(4242) + require.True(t, ok) + assert.Equal(t, "/bin/bash", got.Exe) + assert.Equal(t, uint64(123456789), got.StartBootTime) + assert.Equal(t, got.UUID(), first.PS.UUID()) + + reuse := sampleExecve().toEvent(2) + reuse.Params.Append(params.StartBootTime, params.Uint64, uint64(999)) + reuse.Params.Append(params.Exe, params.Path, "/bin/sh") + reuse.Params.Append(params.ProcessName, params.String, "sh") + applyProcessState(snap, reuse) + ok, got = snap.Find(4242) + require.True(t, ok) + assert.Equal(t, "/bin/sh", got.Exe) + assert.Equal(t, uint64(999), got.StartBootTime) + assert.NotEqual(t, first.PS.UUID(), got.UUID()) +} + +func TestApplyProcessStateExitRemoves(t *testing.T) { + snap := ps.NewSnapshotter() + evt := sampleExecve().toEvent(1) + applyProcessState(snap, evt) + exit := &event.Event{ + Type: event.Exit, + PID: 4242, + Params: event.Params{}, + } + exit.Params.Append(params.Retval, params.Int64, int64(0)) + applyProcessState(snap, exit) + ok, _ := snap.Find(4242) + assert.False(t, ok) +} + +func TestUpsertSnapshotIgnoresAfterLiveReplacement(t *testing.T) { + snap := ps.NewSnapshotter() + rec := &pstypes.PS{PID: 7, Name: "snap", StartBootTime: 9, Exe: "/bin/snap"} + upsertSnapshot(snap, rec) + ok, got := snap.Find(7) + require.True(t, ok) + assert.Equal(t, "snap", got.Name) + + live := &event.Event{Type: event.Execve, PID: 7, Params: event.Params{}} + live.Params.Append(params.ProcessName, params.String, "live") + live.Params.Append(params.Exe, params.Path, "/bin/live") + live.Params.Append(params.StartBootTime, params.Uint64, uint64(9)) + live.Params.Append(params.Retval, params.Int64, int64(0)) + applyProcessState(snap, live) + + upsertSnapshot(snap, &pstypes.PS{PID: 7, Name: "stale", StartBootTime: 9, Exe: "/bin/stale"}) + ok, got = snap.Find(7) + require.True(t, ok) + assert.Equal(t, "stale", got.Name) +} + +func TestPendingBackpressure(t *testing.T) { + es := &EventSource{pendingCap: 2} + require.True(t, es.enqueuePending(&event.Event{PID: 1})) + require.True(t, es.enqueuePending(&event.Event{PID: 2})) + require.False(t, es.enqueuePending(&event.Event{PID: 3})) + assert.Len(t, es.pending, 2) +} diff --git a/internal/ebpf/doc.go b/internal/ebpf/doc.go index ca37ad037..8d281edf7 100644 --- a/internal/ebpf/doc.go +++ b/internal/ebpf/doc.go @@ -16,7 +16,8 @@ * limitations under the License. */ -// Package ebpf hosts the Linux eBPF instrumentation backend and supporting -// feasibility prototypes used to validate CO-RE load, feature probes, and -// race-safe process-state reconciliation. +// Package ebpf hosts the Linux eBPF instrumentation backend. The process +// event source loads CO-RE programs, captures execve/exit/clone, and +// reconciles live events against an iter/task baseline. The spike +// subdirectory remains as a feasibility prototype. package ebpf diff --git a/internal/ebpf/enrich.go b/internal/ebpf/enrich.go new file mode 100644 index 000000000..19bbe0741 --- /dev/null +++ b/internal/ebpf/enrich.go @@ -0,0 +1,52 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "fmt" + "os" + "strings" +) + +// enrichFromProc best-effort reads /proc//{exe,cmdline}. Failures leave +// fields empty and never replace the iterator baseline scan. +func enrichFromProc(pid uint64) (exe string, cmdline string, err error) { + exe, err = os.Readlink(fmt.Sprintf("/proc/%d/exe", pid)) + if err != nil { + exe = "" + } + + raw, cerr := os.ReadFile(fmt.Sprintf("/proc/%d/cmdline", pid)) + if cerr != nil { + if err == nil { + err = cerr + } + return exe, "", err + } + parts := strings.Split(string(raw), "\x00") + cleaned := make([]string, 0, len(parts)) + for _, p := range parts { + if p != "" { + cleaned = append(cleaned, p) + } + } + return exe, strings.Join(cleaned, " "), err +} diff --git a/internal/ebpf/events.go b/internal/ebpf/events.go new file mode 100644 index 000000000..c4c237857 --- /dev/null +++ b/internal/ebpf/events.go @@ -0,0 +1,142 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "bytes" + "encoding/binary" + "fmt" + "time" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/rabbitstack/fibratus/pkg/util/hostname" +) + +const ( + eventKindSnapshot = 0 + eventKindExecve = 1 + eventKindExit = 2 + eventKindClone = 3 + + commLen = 16 + filenameLen = 256 +) + +// rawEvent mirrors struct fibratus_event in c/common/events.h. +type rawEvent struct { + Kind uint32 + Type uint32 + PID uint32 + TID uint32 + TGID uint32 + PPID uint32 + UID uint32 + GID uint32 + SyscallID uint32 + Pad uint32 + Retval int64 + CloneFlags uint64 + StartBootTime uint64 + TimestampNs uint64 + Comm [commLen]byte + Filename [filenameLen]byte +} + +func decodeRawEvent(raw []byte) (rawEvent, error) { + var ev rawEvent + if err := binary.Read(bytes.NewReader(raw), binary.LittleEndian, &ev); err != nil { + return ev, fmt.Errorf("decoding eBPF event: %w", err) + } + return ev, nil +} + +func (r rawEvent) comm() string { return cString(r.Comm[:]) } +func (r rawEvent) filename() string { return cString(r.Filename[:]) } + +func (r rawEvent) eventType() event.Type { + switch r.Type { + case uint32(event.Execve): + return event.Execve + case uint32(event.Exit): + return event.Exit + case uint32(event.Clone): + return event.Clone + default: + return event.UnknownType + } +} + +func (r rawEvent) processID() uint64 { + if r.PID != 0 { + return uint64(r.PID) + } + return uint64(r.TGID) +} + +func (r rawEvent) toEvent(seq uint64) *event.Event { + typ := r.eventType() + info := event.TypeToEventInfo(typ) + evt := &event.Event{ + Seq: seq, + PID: r.processID(), + Tid: uint64(r.TID), + Type: typ, + Name: info.Name, + Category: info.Category, + Description: info.Description, + Host: hostname.Get(), + Timestamp: time.Unix(0, int64(r.TimestampNs)), + Params: event.Params{}, + Metadata: make(event.Metadata), + } + evt.Params.Append(params.ProcessID, params.PID, evt.PID) + evt.Params.Append(params.ThreadID, params.TID, evt.Tid) + evt.Params.Append(params.ProcessParentID, params.PID, uint64(r.PPID)) + evt.Params.Append(params.ProcessName, params.String, r.comm()) + evt.Params.Append(params.UID, params.Uint32, r.UID) + evt.Params.Append(params.GID, params.Uint32, r.GID) + evt.Params.Append(params.SyscallID, params.Uint32, r.SyscallID) + evt.Params.Append(params.Retval, params.Int64, r.Retval) + evt.Params.Append(params.StartBootTime, params.Uint64, r.StartBootTime) + + switch typ { + case event.Execve: + name := r.filename() + if name == "" { + name = r.comm() + } + evt.Params.Append(params.Exe, params.Path, name) + evt.Params.Append(params.Cmdline, params.String, name) + case event.Exit: + evt.Params.Append(params.ExitStatus, params.Int64, r.Retval) + case event.Clone: + evt.Params.Append(params.CloneFlags, params.Uint64, r.CloneFlags) + } + return evt +} + +func cString(b []byte) string { + if i := bytes.IndexByte(b, 0); i >= 0 { + b = b[:i] + } + return string(b) +} diff --git a/internal/ebpf/events_test.go b/internal/ebpf/events_test.go new file mode 100644 index 000000000..8c089337e --- /dev/null +++ b/internal/ebpf/events_test.go @@ -0,0 +1,150 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "os" + "path/filepath" + "testing" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/event/params" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDecodeRawEvent(t *testing.T) { + raw := sampleExecve() + buf := encodeRaw(t, raw) + got, err := decodeRawEvent(buf) + require.NoError(t, err) + assert.Equal(t, raw.Kind, got.Kind) + assert.Equal(t, raw.Type, got.Type) + assert.Equal(t, raw.PID, got.PID) + assert.Equal(t, raw.StartBootTime, got.StartBootTime) + assert.Equal(t, "bash", got.comm()) + assert.Equal(t, "/bin/bash", got.filename()) +} + +func TestRawEventToEventGolden(t *testing.T) { + evt := sampleExecve().toEvent(7) + got := goldenEvent(evt) + path := filepath.Join("testdata", "execve.json") + if os.Getenv("UPDATE_GOLDEN") == "1" { + require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) + require.NoError(t, os.WriteFile(path, prettyJSON(t, got), 0o644)) + } + want, err := os.ReadFile(path) + require.NoError(t, err) + assert.JSONEq(t, string(want), string(prettyJSON(t, got))) +} + +func TestCloneThreadSemantics(t *testing.T) { + raw := sampleClone(0x00010000) + evt := raw.toEvent(1) + assert.True(t, evt.IsCreateThread()) + assert.False(t, evt.IsCreateProcess()) + + raw = sampleClone(0) + evt = raw.toEvent(2) + assert.True(t, evt.IsCreateProcess()) + assert.False(t, evt.IsCreateThread()) +} + +func sampleExecve() rawEvent { + var ev rawEvent + ev.Kind = eventKindExecve + ev.Type = uint32(event.Execve) + ev.PID = 4242 + ev.TID = 4242 + ev.TGID = 4242 + ev.PPID = 1 + ev.UID = 1000 + ev.GID = 1000 + ev.SyscallID = 59 + ev.Retval = 0 + ev.StartBootTime = 123456789 + ev.TimestampNs = 111 + copy(ev.Comm[:], "bash") + copy(ev.Filename[:], "/bin/bash") + return ev +} + +func sampleClone(flags uint64) rawEvent { + var ev rawEvent + ev.Kind = eventKindClone + ev.Type = uint32(event.Clone) + ev.PID = 99 + ev.TID = 100 + ev.TGID = 99 + ev.PPID = 1 + ev.Retval = 99 + ev.CloneFlags = flags + ev.StartBootTime = 55 + copy(ev.Comm[:], "worker") + return ev +} + +func encodeRaw(t *testing.T, ev rawEvent) []byte { + t.Helper() + var buf bytes.Buffer + require.NoError(t, binary.Write(&buf, binary.LittleEndian, ev)) + return buf.Bytes() +} + +type golden struct { + Name string `json:"name"` + PID uint64 `json:"pid"` + Tid uint64 `json:"tid"` + Seq uint64 `json:"seq"` + Params map[string]string `json:"params"` +} + +func goldenEvent(evt *event.Event) golden { + g := golden{ + Name: evt.Name, + PID: evt.PID, + Tid: evt.Tid, + Seq: evt.Seq, + Params: map[string]string{}, + } + for name := range evt.Params { + g.Params[name] = evt.GetParamAsString(name) + } + return g +} + +func prettyJSON(t *testing.T, v any) []byte { + t.Helper() + buf, err := json.MarshalIndent(v, "", " ") + require.NoError(t, err) + return append(buf, '\n') +} + +func TestSucceededHelper(t *testing.T) { + evt := sampleExecve().toEvent(1) + assert.True(t, succeeded(evt)) + evt.Params.Append(params.Retval, params.Int64, int64(-2)) + assert.False(t, succeeded(evt)) +} diff --git a/internal/ebpf/execve_bpfeb.go b/internal/ebpf/execve_bpfeb.go new file mode 100644 index 000000000..cbad50c9e --- /dev/null +++ b/internal/ebpf/execve_bpfeb.go @@ -0,0 +1,156 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build (mips || mips64 || ppc64 || s390x) && linux + +package ebpf + +import ( + "bytes" + _ "embed" + "fmt" + "io" + "structs" + + "github.com/cilium/ebpf" +) + +type execveScratchValue struct { + _ structs.HostLayout + Arg0 uint64 + Arg1 uint64 + Filename [256]uint8 +} + +// loadExecve returns the embedded CollectionSpec for execve. +func loadExecve() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_ExecveBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load execve: %w", err) + } + + return spec, err +} + +// loadExecveObjects loads execve and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *execveObjects +// *execvePrograms +// *execveMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadExecveObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadExecve() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// execveSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type execveSpecs struct { + execveProgramSpecs + execveMapSpecs + execveVariableSpecs +} + +// execveProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type execveProgramSpecs struct { + HandleSysEnterExecve *ebpf.ProgramSpec `ebpf:"handle_sys_enter_execve"` + HandleSysEnterExecveat *ebpf.ProgramSpec `ebpf:"handle_sys_enter_execveat"` + HandleSysExitExecve *ebpf.ProgramSpec `ebpf:"handle_sys_exit_execve"` + HandleSysExitExecveat *ebpf.ProgramSpec `ebpf:"handle_sys_exit_execveat"` +} + +// execveMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type execveMapSpecs struct { + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` +} + +// execveVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type execveVariableSpecs struct { +} + +// execveObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadExecveObjects or ebpf.CollectionSpec.LoadAndAssign. +type execveObjects struct { + execvePrograms + execveMaps + execveVariables +} + +func (o *execveObjects) Close() error { + return _ExecveClose( + &o.execvePrograms, + &o.execveMaps, + ) +} + +// execveMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadExecveObjects or ebpf.CollectionSpec.LoadAndAssign. +type execveMaps struct { + DropCount *ebpf.Map `ebpf:"drop_count"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` +} + +func (m *execveMaps) Close() error { + return _ExecveClose( + m.DropCount, + m.Events, + m.Scratch, + ) +} + +// execveVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadExecveObjects or ebpf.CollectionSpec.LoadAndAssign. +type execveVariables struct { +} + +// execvePrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadExecveObjects or ebpf.CollectionSpec.LoadAndAssign. +type execvePrograms struct { + HandleSysEnterExecve *ebpf.Program `ebpf:"handle_sys_enter_execve"` + HandleSysEnterExecveat *ebpf.Program `ebpf:"handle_sys_enter_execveat"` + HandleSysExitExecve *ebpf.Program `ebpf:"handle_sys_exit_execve"` + HandleSysExitExecveat *ebpf.Program `ebpf:"handle_sys_exit_execveat"` +} + +func (p *execvePrograms) Close() error { + return _ExecveClose( + p.HandleSysEnterExecve, + p.HandleSysEnterExecveat, + p.HandleSysExitExecve, + p.HandleSysExitExecveat, + ) +} + +func _ExecveClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed execve_bpfeb.o +var _ExecveBytes []byte diff --git a/internal/ebpf/execve_bpfeb.o b/internal/ebpf/execve_bpfeb.o new file mode 100644 index 0000000000000000000000000000000000000000..4b62ac978b7947976e078a44d32c3664763f74f8 GIT binary patch literal 16352 zcmeI2e{5veRmbnM$M!ft?B*9Hw8=}8F5|7&J6gq(d1k4H8eg zn$mo|S2+v5>L*%wRmDlZNf)K=UR*iYr^HRIRci4|2YbYjyDWEDZnNBCxyf>a8jkp{9I)zh!k!NEKGH(eq}-FlK_Yn&^$P8;rVsocT- zt@F%DU2k^)^~2$2+7QoE$4yp#mZhD?g-@G#Jv5(7rr@D=UCMVdH_kcd`8!^jT}pk6@)rxv6iYL21t-mH=B?mpi%`zY zTfuQzZ|1Gw^fQ}zE0_)z3(Cw}LG2m6(({J=9p-J8TP!zOZm?WuxyEvp#ET>pbuuQW|u}rc|uyk1tu(No$V;AdqV9fTkd~hkDi~FKXwTyv|I5c+Ie}3%ts_$NClK zUFK!x9p)uwZ5Qe{nYUQqVBTb|GjA}LnYCT0pJ!fUeU^EZIm5idJjJ}sJi)xgoMvt^ zrGUu5Gn6u3N%o&p6d9r`Zdg`?M3E%&AoxO4BIp~}H$UYBwJ)WFv zzH~steR8hJ>R~uJ*A#f)DyS725uA^fS?YZNtnYuw2IuSh9jxzjNPT}pW?8TAXYdsB z1j{tb6w4&b1WT9Y0Ly-sah4?P{eE(;`Ek!jC+8YkJ}2jzlXFd)76RTAnj~>pxBGeUkdo2Y019k#x-IT*V+=(orS{Di%ZhdBiST4rv$Bw$V*# zS%=F~Wom=Q@#he2_CrW_A*K;e-;E1G52DtyZTdF~*)GaxHJ8!vu}72%w?oa3=m(Ed zeMmpSX|m~QHi&$55v@=3o;Jbd?nQc%)ASyAnd?wi8j*ao^~uLx%0nO@e+n^;7$E)( zVgs>&_#VU!#5wjIuT~M?^_(5y7mb;doTUmJ+u;PH3_Y26sl^JCyYMx(UW(ujY9_yI%;e~(D^(R_ZN(-=R!tf{(-G_8|U zh-sv04I~g7NZ+SBF~f!W&ZEq6NGhAKjPl^fI{yVAOwu;diS2dv6h@P^8X9zCy;JH+Q!d@(&s3xZ)l~$`_D6| z``+sM725 zy8lI}U}tXvi_{k7?)Cb9fa|)D?{BAjhf$vVqmb5DZA#a7X;amqP^u}s-~{kj*FYL!AzRl#+ns%n1p9Lm#mzEmyDl!9rOnwWp`amvQE zm0g@G&A4UDEYI2nom)S zsdj_8ykD`^RITFY0#6&|Rs7ey>Oz%F4Js-(H#<95^0Wh-zFPEWs#a011i@^%#%6Gd za$#EGKP-TY*0E}Su2NIlg;t)XT&=G1erdWGwEOjfdXT#ws9O0fY{~h>VwDocD67n! zHKm5Hl&=owRBt7y%~wh;w?#TrK9R`Hm8vzDTy{?nqQ5g^WZSPMe{IN3VDwE^VS4as z)r(PgF~sgiT(s)V1aNJ>Qo&$&@XD*r6s8kYtXiwg=W1?b++B=5>Mec}dL)Nw2Egoq zSC~eA;i{XklQO7!d#RIhW$sGgRRVw7o392H4{aw-Q3JN%kXtRh9?V@$gfj<+T&pL8 zsG(WE?9G>_{Tk-77|bS4v3W!8DY%3y>JPaWr=ImLJpU3Z9XajwLQP>LBU6GrJLrdI zB&0iX2B1&qAxEQ!hC6rGESaxblFak+( zoyPQ$V9;H}?2Vq|*-Jv9?+tE_#-=zocWu7RW7EES%Qq7@D$~gJ_SY)R0S_>Yg6Q zy#bf*)DP)xFFK9eOAiAkP0@%2ln5ww4Jofi4<%(%*N}@0b4{MdKVuxUhq|OThiD}Z zqrNt7#1=>WV~Rt=#c88OA-nrU?(oR&XMLewXs6&^he&irW?>3oQBCUu(3i})N!?QIy=_JXzV@R0(wOI2Kqf%QW(zpq)l zUJ-u0!hUq(y`l4xa#o08m*(rrxj~#|{r6!JcP3enHNA@Q=u8WqV4hX#>zJ?3JoBe8Rd{FWYzS_KnA%%q z{+d#6!QakJ!CTCD2VbZ5+5R5<7V)Q;&se|Fw|F4L82?x*#8jT`@A;@w8^jsbTh4}< z>SJ7K{r6PyXEkx~67v_8+JZf?WoFoa8~(&rLX6kC82i`rXG(291YTo3G3GnA&Wye2 zJD7~_4a4!%O1+Kw?cQOY!p|ne`-V}}alv}ef*%yf&iD;%C4&Ii+Tsh~e#1X$`;oR6 ze(1ec>_<~#Kbkt(k52ZZq*=$^Skou_5%!NIGafxZ0sGMu?qk|#^w|P?W5)*gES_Zt zkO-aFlZdV6+az8E)Z-}xBY!C4{_aF(Lrtl)xknsN{+BoJ|~YjGWE`j*4` z7;&h4_hyve?e@B;eX=Kph@ihqkUf@nkS2R9?<4(So9Ww&ww?8g&IB}PmM4+Mxva&n zAWd^;^<|{VcgqdIOM+JfuL<4|yd`)?@LrVlG=%JN`i0&VoC1gZrT4%HX9VX3(|m{P zHw7;XUKPA9cvJAU;9bG{QKt8^us!rX7U6{8l;8=$8NqqMb-_)+%Ys)0uM6H3ye)WF z@P3r_GcPnBodZIj5S$V`AvhyAFSstaDR^1%s^E3On}W9m?+V_Jax5-*KyX5EO7Mi> zjNrWBy5OeZWx=b0*9C71-WI$YW&OAa?N42FzlYfsoD`fEJS8|QxGcCKcuDYz;5EVI zZ)iX5B7eh7{)Tx!%H46n1A-HRQ-UW1X9VX3*9A8PFAH83ye@cC@V4Mx!TV9x=Y!Dr z^b81nLU2m(gy4+eyx_Xvrr>44tAf`BZwlTPyeoJ=%JI110l^8uDZvwhGlKJi>w=qt zmj$m1UKhM6cw6wU;Qc7~#sv=uP6$p3o)DZ7oEKad{26fQdDDAC@LPiaLhxkCoRA4;rBoKnES{>YPc5EYihU>6o)Uq`0Ox2`%`P=yG7*N z09Cldw`F`=2!C{-4;K7I8k!^PMvL$tT$nEp{9#B97mL?t>9cMHe|!;XziryljfeEd zn00i$kWsDgAH(=!S@W-`;p)PymOOOIelD53cyZMGkb)R7V$^%WkQSrfs39%RdFP0d zMvQtR8b?@yM@(TO!Aa&MX7XJL&N(zgYxs!pRMuoV-xNxI-_g00wv68gA|V~2HrV$C zG5yo`4GQ`> zW0K}->l6Jo;&J^qO>vcmr2g&ut*uY(la0sp-#Y*uske-5U2T2Js39KL|0dU!J{%yHX{|w|nlr^DF)5pKnSejCTq1J+nbR)|lYq8{xDK zrp+VBS!r*xRCzRMQAB8|xP_t_7yU*wvV zmv^8~5&xRV>gz83K+AS!H#fd)h1r4rTYvxFz3WY2gl}YBJo~u-f8#5TcOZTJ-o1M_ z-V$y$W)0;!P$urKZ*#=*tn#`fenF%k$4ZAA*G=Qu>N0Ms7nKd6t?Ou)j^%z0{zePQ zm`}Fsv@-j=k-m@JB zqO7*tXj_i!{|4qY9mjDme0F*_u{Phc)2`j@v}-rp;`Dh9c>?%s%ZG7AJsB(PIv7BZ z>wqO$ua{3E?Azl!p{6&AxWMgw#L4)2;(TL0Y?<}^5`5*$ZeCW~UB9s&rvK3OyyfaS z(9F_q|KpN-kRD9;dSpb8Z2Nu|xBDl1X78gG>-L4X9bQjgPvxtk$Buorpx=jmz8_v6 zH$JS#+VcQ?nC{#0`fS)G4O4@C?m358ZxH zNys{Gcl$ymv2OEK+tW5*C2?2S?Gu&6ny}k1Dv8^|Zr`XRR)qs8e^c0LUP-J7yZxk+ zSQd8sN+q!*?DVQ6t_eH6Dv3p5r&lGhAnf$2B<6*0TUklW2|K+iiCJN%S0ync?DVQ6 z%EHT5RuXyPCE-!wYr@0Ai^4;~3&MlK^TGqdbHZuiS>Zn68Q~t`vT#Z`FB}Mu3U>$( z3%3amSsA~d@IKgRKJnJ%_Y-?R1#^CW4*ohkUF|;D?%MPBL+0B$*I<1e+&b56oog@; zu3oe2rFwNy%4l^t?%m9VTpI^8Rt6T0g-8ueIk2ArbGrJ zJ4CjLOp3I^uJ`Q-Zk=mfe>~_Mz@Fp2XU|7l=NfzN**e!O+lf0%;0(3zz2N! z(!V5(v&fkL#70YhQr;P$sV1Jm>;W2r&j~>d4U_CU3C4d2p8GVM&*unPE)**o!~rBOo7xXMJh-Bk*v0_=qaHkel2a3I^-zbx#~NzR5ksZq5_0hv&pzo5dvg z?E6s(Qzv5D0!NuDrp0o>dFWMF$OdEao(#BozY4`~KA@~{i z9Q?!Z%kY!%aC;8m64%*}8KUd@RK|d1nhx^nTwNX$_-NC_r@PnTkzpI3gk}+@ZFIj0 z&u|+4x8Vc$8Tc>4!zHQz@7bV99)f(!h3(tPqzm(E+x`dwoG0Fhcz?*FsU36nPa2;6 z!9JLQXMb>hEyMo-Jlvkgae=ztH9dvUpTJ|TZA(Lb8~(@P>HZ#``f)tplQ8>_igK<5 z@VgN{44H=Ci*O2Z27W)naP}#d<)0L$eQCtIr%h8g)=088{{#ZClR8<4q7SjXlL$Ns zc?uH$yE|PnuT5RJTtg<>K^=b-JHCN>XwwWlUA+CD1b++ZZ2u_2zmJIno3^D9{zHVR zC*2=QI6(O82=_py;r~j)GYH>8nCoX5{_hYz0m+L05n(%j5dJTOxvpjq7T0vlhtS;d zjquKs8{wws5dLqJh4b^u*I@hjX(X_ZIX1W1Y4~UO9}xZ+B;9ur{wc@+{$C|Njqra+ z{4m1bLl_lrV_okf+y_b5c;sBIu>!eWiP2q=Qw;< z(w~+3AnI)6z7V)gW6w_`jJiw;j7;3F?0YXt+AXj3On~qc@N@xuH}a#L&7TI-v0jcB zoU?G0MAHZr&D4qG$j|H4 zL@{@HA{q%;#nj2C8I5Z#dTesya=?qZ5>?D}W^5`lBZbL)n4O$LiDI^pDP>1Pmyju3 z?lHLZnoGH{Xd*KnnW;Y3*tbz(b~ID)#Z0M?$wr}7DlBAP3yYN^RgDTJJ2^f+IT2a| zBz$QsbGhgf@`WfG&zD3E$&t^E82m#6nCJ~Fj!qUz#+p!zvy?5B&1hy~WGvcfH;l?r zb~-Yp{4wZ~&5Vr|8E{JZ#OyJbtNUX9Qg_z07NXKrVIq*anC9}QQrXFgVkw}>!I4h1 zcl0!M`-Prg>HN59MXv|w;^5f;wT71?m^M`?pff^P6_zgNMpDdHEET4* zrJ%0g1N1)1RX>At1iLu`U@;)fjUYaEDMJW) zfzQ|#6mzdflb2HQ$j+|7r&A$TG@i+aQ~8lh31c}Hji(NadR@U`n1n0JcLiq$pAXNx z_zDU&9QJaqWYCj!LxMQp>6=C*mez0tke;$#j!HLmclMZ@GRNG4=pHq#k#v0_H*xvm z)Ft!_`eZsv9d=o2eQ_j05A@J<_UYqMXHdoHoj5L|*MkJRHl#ZC&DiARm8rb+&Bp3+ z-BYW{mcrPCn2rOcdf)_RuBqGz=5vFbNC^E*w+1kiT`6`FGs+c2>C>l!6UUuO zSRu$KsV)jN`J%2h7wt+*`@ROPFQ*2E{pe{1YW*tQ$<8fhidR^@tVSYN4_D@qPF)>S z+1IgLhC3Ba!Hmz0h4~B@y187K#4H)Zdc+236S9S9#7q>iP|FpAp)8cbi<6Tj4x-%% zjonRqPMx&lU7rE^am79N^B4NV3;k!#rPw~KsjCHQ^_)6xTAc>>DH79>#&g=c{GBM= zi_HXGlXqiK-=fRa70^=K($iQQFzG?ru$wGJ4VN^~*k+uh=Q-x8g3|1dg8GK-sxbt&g)a*DE}Njk@u6 zO@*1u?}Npui{sczxyK;sJ~zxv>4CRQBsbind8@zgU9xxcT{7PK#{S=^@w(LWFFe=( z;!FLe8=G;_jODV?L@~nQiO8^#6N}e)+qcfFrO7J;P8ci>-ao-$DO?`cjNko zI|EvN2G=HcUbB1}SDx)yhku@(MEJAtop`Oo`aPckGcWlsm>&-me2(zk0!|74GtwXJ z!C4p5r}F`1KzJu~+=YWMztA!L32+OP8xrmX2Wi+D<7V)za24rI=#XV$>e~eUXgh7B zW4%1cb_W}FkPm_D?D({@157)PBkwu*mW6~F5}qc%oiM|~ze*lUn7lBAb!A*x{F{3Cd1*uMmQW4sUC0j7PYkiPp(V*=sx z;5IbYC!7QCoW*mMlz#)f>t@0X3cmy1dkgjn-vYPZhJC{S#PshZ%xH{vg7d=PXZ|}0 zQx@KfGk@z^!psOC2Jd?>VP<383Z4`8_AUsYM*gO|=npXM%YzT0vu+Eco2*+CZpQOe z``h@vi(-y5%QJmH(o@2|e2?%Mr0-fZCaritcpT|1U&A~U{w(+*4AzX|*%yu{-Y*S_#lJ6Jq;od}- z#MqFZWg^_5@aw{J!e13$1@k@R0ql)*0rpDrIbrS@{~PVt#_w}!=EkHPjwx`>@%OB? z9L@daCx{4Con zuOs92e8%SAaUZ)d7|QW}gS+}ByM9CBwOtvB*LJ-m@!GC0O1!q~9Urgk`gdrvFE(B` zZ9d#H5&tmmqCHd>F0fZ){uFXwY`pmHneKPsncq9!lj!!tGv0H+NVr$w_r?0tx6AJ# z`)XgJus)>zPe8AS?N=$5cJGKO7erqQwv}Qe9GUN&d6uRl^LtKN?DzRsWc)pU(Dkjz zI%l)ghuJFJzTQ9P%3 zLGd-k%ZhI*zODGKVrvBAygh9+D#n3g9D{1l;;ERy?bCUh$&hCB-X>R~4@*URTWfU0mM| z#VN&oig}iammg9*s<^CpR`I;zMa4^sR}`-*UQ@iTILU#BtBt?nl;S?c1B!d#jA?f z6t64x_wSnhazFQ+QrxF_K=F{`QN?A&vx?^xFDhPAyrOtj@tWdw#YsM_!+Co<6sHvT zDIQQfqVIB)NV6?Z9qM)7&YA35~IlTQVYKW4g1QMqKg z3(;8j*_WU1CTx6at^00K_ica$9`J1$-xlH@9r(dQzDOgbVcGgD@`DTa2ZG6E#Su&b@gwk*0}Om`hC=DTrQ?m?fXYJzF3wr7fpAuGH#`Go$;Sb&YwMdBK(+v z=yl>m_)$kzoCr@ivf_AnoYdpQiLlpVuSjsO%j_h$N4Uq$hadHN9OpUy!*luX_HTMfpbk-@%t}aA|wxy9Z~B zZ{N>9{vKA_#3FA#&MTg~WgHxBZl7b?sQn>n-yV*Uhwal8-Tv1Q*L?ooB*YAS%?-)= j_xXK-)|J2Ml%?XcSb3K}^n>%yg)GP44g2~!meEWe`e0C z@1Dj9MHNq(X6`w^nKLtIKJV;xzdCs2p;*kLX7K23=#hkG4wTfKp^QAYkT?4{bui6- zjk;KVgC9g6JZFgLq?P`JP4`cV{IW;SuzGghCUlFvC8YbTc_U}jHbv_F`ngfdpC6_7 zv6lP!<#m?lcJY&_-6y(y`SQB1Ps)2+iJoM;v$j3&57N^l(J4+po%e=C4vEZ&91v+d zf?Z137uhY+)Qo&HE|8pzddc_+c}e)9$Tg7{M6QZFFLFiXvdAToXGAWFTo5@ga!zDJ zfF7y+?IYT&)sGv*Td^g>gkYr9-qCBeDei!KQU(kQL@*@wkNn1c^Su{diEAW2}qywI8g$d zB@jP?m~?i~eMDDB8|%7^*m7a6gU_K`5nb}fpeGSCh`TIDfym)ci0mKF=bNI# zrdiyF&hxMXF$0~OV7&qTMq_1%3+27-!X1#XHIv3kaKqy=cR=pKqT{rk=;kBviN_)X z{Y#LrNar8??OaK{zBFb#ya_uve_#(%NsUV8t1lqWt{I}`^Z{zzGFYNjiUh$ z{3e4RZAENwXDZd3b#b$?GJTRc1YxmO$%j=6Pe7|u{qDP9&(C_Xnx80!V?I|=-?x{O@oXj+ zO2rAEA2b!t(ur)L9@1E)R1R{bI!aV?m253H8Q6?$Wg<;@^wW605Eir3A=L-C#(|9r zbCcPMD@L_SHWvoQsi2ZQ8B}MhK{%7IQLZ#ST`C5~f6>Pa*@>#lC|AO8x?B@4#G;%Z zBmAp`=!xrAb+S~ck?BCLHbaxy;#eVUwqqI#CMuXo7|^>I!L_3u7_^j<%Ok?k>2M+kCrH68RZ1?uqx$B#x&QPkW>*GNstcH~nVQPmp z>GiAmQ(;3&(=b z9)9>xFgSAf7;><{{cBGNTh;Spm?3c}I(f(l+}s)W*+?c(dSnZ_iL{BBbJ%eK8XR)iYg`k|}rAubR1cfj=7FKLVt`d$>v0AQ_W@ZT!S*ZobOQjl5q1m!zwtag4 zJ~P{`2Jr9T@I%3aj~pEgjt)L>D8=ogH!Zcmt@Qr8sMC6|Ns)+;-1$5h-y&rlq~u8R*j0+TncHiqWyT%rIcmh(|1BC1h<1T2SNDldLv{R3zAE zdO!ZN&LQ_5O>&*Ryb}9R-Z|KZ zTgF{ImX0v)R59^4_Gh9Rt{1Tk^3lK8+eGCq$PvqNl$}KTu~Az--bdal?vSyv@GHnNPo-+5bKJ)FmO7K3szRxJp`0w}G8unT9orW_Ywg$!?OW-Ww5KrO3{1<~1FI!ID zA-p6!EW8ehb0hhR2V$h7yb#w)q^u`BOo!kK}`+sSeM??F5elPeWYFb_eOe4yKio!^V!-A0bYL2e@RTR zzen-WsH16Rl`e>zBI(Q=e8g?2$N9r)2&SIj!=8PO2E3o>`A9u!$@E1ILE;@@2N7}s zk;|Atg}g?-af8bz5D}8Bb9u+z(Ajs#+&=CQj;R+qwd#jA?f6klp(v%MqpAMaB6zT&jvjN)O% zql(Ll8;a)@FDhOJNA?@vRa^Li;){ycTRG98nD>9A{zOXgfZ`#=BZ?;#&nTW#yrB4u z;$_9>6<<(%QSo{!Cp#2(D^4jMP&}k~MDe8J8O3vo7ZjgSysY@V;tPr|Dqe5pwhqPJ zic^XQ6b~sLQ9P-5M)92D1;u9+FDpK;_=4h#iq~7Yy+iSx;K+HbV> zIPU(UU_T07>5GzlKEG-$Vk3V|puR%k#58|PuHcUo%`Z)T_;6Xv9;d$Q?6i^6iROo? zBL@%m2cIH{v=#lqfF&dPgMF5a=nwW<;u(Bz0?&oI54wtX7Olqr`(H~i7Ys>Z-7&9$ zXvjn%qPu$*Cw&F+8s*KsGE%(do_Y!y_?}@xMx=;sL!$VQ{O2s!Pu?FKx)28tH)%g{ ziH&d#T(YwHbmcjLXJWJVC#8fB$?d!QvMbN+zl`YGcm5%XL+7Fq_AV=`5ze0BF+|6@ zyz_h5l-hW>_Bmx0(Usq@Fd9C~m~g|2C{9G@kN56o{hyNp8A$HGvvcip|M6{Vv-Y>H zAP=GaEi0SPX#4zI!_}Kx_M(eZi} WHs-<^Eot@JW&FJbFqhHocmD@szlUA` literal 0 HcmV?d00001 diff --git a/internal/ebpf/exit_bpfel.go b/internal/ebpf/exit_bpfel.go new file mode 100644 index 000000000..673523e58 --- /dev/null +++ b/internal/ebpf/exit_bpfel.go @@ -0,0 +1,150 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build (386 || amd64 || arm || arm64 || loong64 || mips64le || mipsle || ppc64le || riscv64 || wasm) && linux + +package ebpf + +import ( + "bytes" + _ "embed" + "fmt" + "io" + "structs" + + "github.com/cilium/ebpf" +) + +type exitScratchValue struct { + _ structs.HostLayout + Arg0 uint64 + Arg1 uint64 + Filename [256]uint8 +} + +// loadExit returns the embedded CollectionSpec for exit. +func loadExit() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_ExitBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load exit: %w", err) + } + + return spec, err +} + +// loadExitObjects loads exit and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *exitObjects +// *exitPrograms +// *exitMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadExitObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadExit() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// exitSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type exitSpecs struct { + exitProgramSpecs + exitMapSpecs + exitVariableSpecs +} + +// exitProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type exitProgramSpecs struct { + HandleSysExitExit *ebpf.ProgramSpec `ebpf:"handle_sys_exit_exit"` + HandleSysExitExitGroup *ebpf.ProgramSpec `ebpf:"handle_sys_exit_exit_group"` +} + +// exitMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type exitMapSpecs struct { + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` +} + +// exitVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type exitVariableSpecs struct { +} + +// exitObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadExitObjects or ebpf.CollectionSpec.LoadAndAssign. +type exitObjects struct { + exitPrograms + exitMaps + exitVariables +} + +func (o *exitObjects) Close() error { + return _ExitClose( + &o.exitPrograms, + &o.exitMaps, + ) +} + +// exitMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadExitObjects or ebpf.CollectionSpec.LoadAndAssign. +type exitMaps struct { + DropCount *ebpf.Map `ebpf:"drop_count"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` +} + +func (m *exitMaps) Close() error { + return _ExitClose( + m.DropCount, + m.Events, + m.Scratch, + ) +} + +// exitVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadExitObjects or ebpf.CollectionSpec.LoadAndAssign. +type exitVariables struct { +} + +// exitPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadExitObjects or ebpf.CollectionSpec.LoadAndAssign. +type exitPrograms struct { + HandleSysExitExit *ebpf.Program `ebpf:"handle_sys_exit_exit"` + HandleSysExitExitGroup *ebpf.Program `ebpf:"handle_sys_exit_exit_group"` +} + +func (p *exitPrograms) Close() error { + return _ExitClose( + p.HandleSysExitExit, + p.HandleSysExitExitGroup, + ) +} + +func _ExitClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed exit_bpfel.o +var _ExitBytes []byte diff --git a/internal/ebpf/exit_bpfel.o b/internal/ebpf/exit_bpfel.o new file mode 100644 index 0000000000000000000000000000000000000000..f9b7dfc31e417f6665df399324e52e24a46c7122 GIT binary patch literal 11336 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 R_XsDY07bn1Z8q1K{{aRnlZ^lX literal 0 HcmV?d00001 diff --git a/internal/ebpf/gen.go b/internal/ebpf/gen.go new file mode 100644 index 000000000..a0f07fc5c --- /dev/null +++ b/internal/ebpf/gen.go @@ -0,0 +1,26 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +// Regenerate with ./generate.sh (preferred) or go generate -tags linux. +// bpf2go version is pinned to github.com/cilium/ebpf v0.20.0. +// +//go:generate ./generate.sh diff --git a/internal/ebpf/generate.sh b/internal/ebpf/generate.sh new file mode 100755 index 000000000..e9ef3a51a --- /dev/null +++ b/internal/ebpf/generate.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +# Regenerate bpf2go bindings for the Linux eBPF process source. +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$ROOT" + +if ! command -v clang >/dev/null 2>&1; then + echo "clang is required to regenerate eBPF objects" >&2 + exit 1 +fi + +# The process source is x86_64-only. Always compile CO-RE objects for that +# architecture so generation is host-independent. +export BPF_TARGET_ARCH=x86 + +CLANG_VERSION="$(clang --version | head -n1)" +echo "clang: ${CLANG_VERSION}" +echo "target arch: ${BPF_TARGET_ARCH}" + +INCLUDES=(-I./c -I./c/common -I./spike/c -O2 -g "-D__TARGET_ARCH_${BPF_TARGET_ARCH}") + +generate() { + local name="$1" + local src="$2" + go run github.com/cilium/ebpf/cmd/bpf2go@v0.20.0 \ + -go-package ebpf -output-dir . -cc clang -target bpfel,bpfeb -tags linux \ + "${name}" "${src}" -- "${INCLUDES[@]}" +} + +generate execve ./c/execve.bpf.c +generate exit ./c/exit.bpf.c +generate clone ./c/clone.bpf.c +generate prociter ./c/proc_iter.bpf.c + +echo "generated eBPF bindings" diff --git a/internal/ebpf/loader.go b/internal/ebpf/loader.go new file mode 100644 index 000000000..1e2145ea1 --- /dev/null +++ b/internal/ebpf/loader.go @@ -0,0 +1,220 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "fmt" + "io" + "sync" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" + "github.com/cilium/ebpf/rlimit" + log "github.com/sirupsen/logrus" +) + +type loader struct { + exec *execveObjects + exit *exitObjects + clone *cloneObjects + iter *prociterObjects + links []link.Link + iterLink *link.Iter + once sync.Once +} + +func loadCollections() (*loader, error) { + if err := rlimit.RemoveMemlock(); err != nil { + return nil, fmt.Errorf("removing memlock: %w", err) + } + + execSpec, err := loadExecve() + if err != nil { + return nil, fmt.Errorf("loading execve collection spec: %w", err) + } + exitSpec, err := loadExit() + if err != nil { + return nil, fmt.Errorf("loading exit collection spec: %w", err) + } + cloneSpec, err := loadClone() + if err != nil { + return nil, fmt.Errorf("loading clone collection spec: %w", err) + } + iterSpec, err := loadProciter() + if err != nil { + return nil, fmt.Errorf("loading prociter collection spec: %w", err) + } + + var execObjs execveObjects + if err := execSpec.LoadAndAssign(&execObjs, nil); err != nil { + return nil, fmt.Errorf("loading execve objects: %w", err) + } + + replacements := map[string]*ebpf.Map{ + eventsMapName: execObjs.Events, + dropCountMapName: execObjs.DropCount, + scratchMapName: execObjs.Scratch, + } + opts := &ebpf.CollectionOptions{MapReplacements: replacements} + + var exitObjs exitObjects + if err := exitSpec.LoadAndAssign(&exitObjs, opts); err != nil { + _ = execObjs.Close() + return nil, fmt.Errorf("loading exit objects: %w", err) + } + + var cloneObjs cloneObjects + if err := cloneSpec.LoadAndAssign(&cloneObjs, opts); err != nil { + _ = exitObjs.Close() + _ = execObjs.Close() + return nil, fmt.Errorf("loading clone objects: %w", err) + } + + var iterObjs prociterObjects + if err := iterSpec.LoadAndAssign(&iterObjs, opts); err != nil { + _ = cloneObjs.Close() + _ = exitObjs.Close() + _ = execObjs.Close() + return nil, fmt.Errorf("loading prociter objects: %w", err) + } + + return &loader{ + exec: &execObjs, + exit: &exitObjs, + clone: &cloneObjs, + iter: &iterObjs, + }, nil +} + +func (l *loader) eventsMap() *ebpf.Map { return l.exec.Events } + +func (l *loader) dropCount() uint64 { + var key uint32 + var drop uint64 + if l.exec == nil || l.exec.DropCount == nil { + return 0 + } + _ = l.exec.DropCount.Lookup(&key, &drop) + return drop +} + +func (l *loader) attachHotPath() error { + type tp struct { + group string + name string + prog *ebpf.Program + } + tracepoints := []tp{ + {"syscalls", "sys_enter_execve", l.exec.HandleSysEnterExecve}, + {"syscalls", "sys_exit_execve", l.exec.HandleSysExitExecve}, + {"syscalls", "sys_enter_execveat", l.exec.HandleSysEnterExecveat}, + {"syscalls", "sys_exit_execveat", l.exec.HandleSysExitExecveat}, + {"syscalls", "sys_exit_exit_group", l.exit.HandleSysExitExitGroup}, + {"syscalls", "sys_exit_exit", l.exit.HandleSysExitExit}, + {"syscalls", "sys_enter_clone", l.clone.HandleSysEnterClone}, + {"syscalls", "sys_exit_clone", l.clone.HandleSysExitClone}, + {"syscalls", "sys_enter_clone3", l.clone.HandleSysEnterClone3}, + {"syscalls", "sys_exit_clone3", l.clone.HandleSysExitClone3}, + {"syscalls", "sys_enter_fork", l.clone.HandleSysEnterFork}, + {"syscalls", "sys_exit_fork", l.clone.HandleSysExitFork}, + {"syscalls", "sys_enter_vfork", l.clone.HandleSysEnterVfork}, + {"syscalls", "sys_exit_vfork", l.clone.HandleSysExitVfork}, + } + for _, t := range tracepoints { + if t.prog == nil { + return fmt.Errorf("missing program for %s/%s", t.group, t.name) + } + lnk, err := link.Tracepoint(t.group, t.name, t.prog, nil) + if err != nil { + return fmt.Errorf("attaching %s/%s: %w", t.group, t.name, err) + } + l.links = append(l.links, lnk) + } + + if l.clone.HandleSchedProcessFork == nil { + return fmt.Errorf("missing sched_process_fork program") + } + fork, err := link.AttachTracing(link.TracingOptions{Program: l.clone.HandleSchedProcessFork}) + if err != nil { + return fmt.Errorf("attaching sched_process_fork: %w", err) + } + l.links = append(l.links, fork) + return nil +} + +func (l *loader) runTaskIterator() error { + if l.iter == nil || l.iter.DumpTask == nil { + return fmt.Errorf("missing iter/task program") + } + it, err := link.AttachIter(link.IterOptions{Program: l.iter.DumpTask}) + if err != nil { + return fmt.Errorf("attaching iter/task: %w", err) + } + l.iterLink = it + file, err := it.Open() + if err != nil { + return fmt.Errorf("opening task iterator: %w", err) + } + _, _ = io.Copy(io.Discard, file) + return file.Close() +} + +func (l *loader) Close() error { + var err error + l.once.Do(func() { + for _, lnk := range l.links { + if lnk != nil { + if e := lnk.Close(); e != nil { + err = e + } + } + } + if l.iterLink != nil { + if e := l.iterLink.Close(); e != nil { + err = e + } + } + // Replacement collections share maps owned by execve. Close only their + // programs so the canonical maps are released once. + if l.iter != nil { + if e := l.iter.prociterPrograms.Close(); e != nil { + err = e + } + } + if l.exit != nil { + if e := l.exit.exitPrograms.Close(); e != nil { + err = e + } + } + if l.clone != nil { + if e := l.clone.clonePrograms.Close(); e != nil { + err = e + } + } + if l.exec != nil { + if e := l.exec.Close(); e != nil { + err = e + } + } + log.Debug("eBPF loader closed") + }) + return err +} diff --git a/internal/ebpf/maps.go b/internal/ebpf/maps.go new file mode 100644 index 000000000..353876c85 --- /dev/null +++ b/internal/ebpf/maps.go @@ -0,0 +1,27 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +const ( + eventsMapName = "events" + dropCountMapName = "drop_count" + scratchMapName = "scratch" +) diff --git a/internal/ebpf/proc_iter.go b/internal/ebpf/proc_iter.go new file mode 100644 index 000000000..75bba914c --- /dev/null +++ b/internal/ebpf/proc_iter.go @@ -0,0 +1,64 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "strings" + + pstypes "github.com/rabbitstack/fibratus/pkg/ps/types" +) + +func snapshotFromRaw(r rawEvent, exe, cmdline string) *pstypes.PS { + name := r.comm() + if name == "" && exe != "" { + name = baseName(exe) + } + if cmdline == "" { + cmdline = exe + } + args := splitCmdline(cmdline) + return &pstypes.PS{ + PID: uint64(r.TGID), + Ppid: uint64(r.PPID), + Name: name, + Cmdline: cmdline, + Exe: exe, + Args: args, + StartBootTime: r.StartBootTime, + UID: r.UID, + GID: r.GID, + Threads: make(map[uint64]pstypes.Thread), + } +} + +func baseName(path string) string { + if i := strings.LastIndex(path, "/"); i >= 0 && i+1 < len(path) { + return path[i+1:] + } + return path +} + +func splitCmdline(cmdline string) []string { + if cmdline == "" { + return nil + } + return strings.Fields(cmdline) +} diff --git a/internal/ebpf/prociter_bpfeb.go b/internal/ebpf/prociter_bpfeb.go new file mode 100644 index 000000000..4fe1862cf --- /dev/null +++ b/internal/ebpf/prociter_bpfeb.go @@ -0,0 +1,147 @@ +// Code generated by bpf2go; DO NOT EDIT. +//go:build (mips || mips64 || ppc64 || s390x) && linux + +package ebpf + +import ( + "bytes" + _ "embed" + "fmt" + "io" + "structs" + + "github.com/cilium/ebpf" +) + +type prociterScratchValue struct { + _ structs.HostLayout + Arg0 uint64 + Arg1 uint64 + Filename [256]uint8 +} + +// loadProciter returns the embedded CollectionSpec for prociter. +func loadProciter() (*ebpf.CollectionSpec, error) { + reader := bytes.NewReader(_ProciterBytes) + spec, err := ebpf.LoadCollectionSpecFromReader(reader) + if err != nil { + return nil, fmt.Errorf("can't load prociter: %w", err) + } + + return spec, err +} + +// loadProciterObjects loads prociter and converts it into a struct. +// +// The following types are suitable as obj argument: +// +// *prociterObjects +// *prociterPrograms +// *prociterMaps +// +// See ebpf.CollectionSpec.LoadAndAssign documentation for details. +func loadProciterObjects(obj interface{}, opts *ebpf.CollectionOptions) error { + spec, err := loadProciter() + if err != nil { + return err + } + + return spec.LoadAndAssign(obj, opts) +} + +// prociterSpecs contains maps and programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type prociterSpecs struct { + prociterProgramSpecs + prociterMapSpecs + prociterVariableSpecs +} + +// prociterProgramSpecs contains programs before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type prociterProgramSpecs struct { + DumpTask *ebpf.ProgramSpec `ebpf:"dump_task"` +} + +// prociterMapSpecs contains maps before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type prociterMapSpecs struct { + DropCount *ebpf.MapSpec `ebpf:"drop_count"` + Events *ebpf.MapSpec `ebpf:"events"` + Scratch *ebpf.MapSpec `ebpf:"scratch"` +} + +// prociterVariableSpecs contains global variables before they are loaded into the kernel. +// +// It can be passed ebpf.CollectionSpec.Assign. +type prociterVariableSpecs struct { +} + +// prociterObjects contains all objects after they have been loaded into the kernel. +// +// It can be passed to loadProciterObjects or ebpf.CollectionSpec.LoadAndAssign. +type prociterObjects struct { + prociterPrograms + prociterMaps + prociterVariables +} + +func (o *prociterObjects) Close() error { + return _ProciterClose( + &o.prociterPrograms, + &o.prociterMaps, + ) +} + +// prociterMaps contains all maps after they have been loaded into the kernel. +// +// It can be passed to loadProciterObjects or ebpf.CollectionSpec.LoadAndAssign. +type prociterMaps struct { + DropCount *ebpf.Map `ebpf:"drop_count"` + Events *ebpf.Map `ebpf:"events"` + Scratch *ebpf.Map `ebpf:"scratch"` +} + +func (m *prociterMaps) Close() error { + return _ProciterClose( + m.DropCount, + m.Events, + m.Scratch, + ) +} + +// prociterVariables contains all global variables after they have been loaded into the kernel. +// +// It can be passed to loadProciterObjects or ebpf.CollectionSpec.LoadAndAssign. +type prociterVariables struct { +} + +// prociterPrograms contains all programs after they have been loaded into the kernel. +// +// It can be passed to loadProciterObjects or ebpf.CollectionSpec.LoadAndAssign. +type prociterPrograms struct { + DumpTask *ebpf.Program `ebpf:"dump_task"` +} + +func (p *prociterPrograms) Close() error { + return _ProciterClose( + p.DumpTask, + ) +} + +func _ProciterClose(closers ...io.Closer) error { + for _, closer := range closers { + if err := closer.Close(); err != nil { + return err + } + } + return nil +} + +// Do not access this directly. +// +//go:embed prociter_bpfeb.o +var _ProciterBytes []byte diff --git a/internal/ebpf/prociter_bpfeb.o b/internal/ebpf/prociter_bpfeb.o new file mode 100644 index 0000000000000000000000000000000000000000..565df7f816d49aeb569219e3c32824350e3e3f97 GIT binary patch literal 6264 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 DR*2= e.pendingCap { + pendingDropped.Add(1) + return false + } + e.pending = append(e.pending, evt) + pendingQueued.Add(1) + return true +} + +func (e *EventSource) finishBaseline() { + e.pendingMu.Lock() + pending := e.pending + e.pending = nil + e.live.Store(true) + e.pendingMu.Unlock() + + for _, evt := range pending { + e.dispatch(evt) + replayApplied.Add(1) + } +} + +func (e *EventSource) dispatch(evt *event.Event) { + applyProcessState(e.psnap, evt) + eventsProcessed.Add(1) + + if e.config != nil { + if e.config.EventSource.ExcludeEvent(evt.Type.ID()) { + eventsExcluded.Add(1) + return + } + if e.config.EventSource.ExcludeImage(evt.PS) { + eventsExcluded.Add(1) + return + } + if evt.IsDropped(e.config.IsCaptureSet()) { + eventsExcluded.Add(1) + return + } + } + if e.filter != nil && !e.filter.Eval(evt) { + return + } + if err := e.q.Push(evt); err != nil { + select { + case e.errs <- err: + default: + } + } +} + +func (e *EventSource) startupSummary() string { + return fmt.Sprintf("pending_queued=%d pending_dropped=%d replay_applied=%d snapshot_upserts=%d", + pendingQueued.Value(), pendingDropped.Value(), replayApplied.Value(), snapshotUpserts.Value()) +} diff --git a/internal/ebpf/source_integration_test.go b/internal/ebpf/source_integration_test.go new file mode 100644 index 000000000..a33c43f56 --- /dev/null +++ b/internal/ebpf/source_integration_test.go @@ -0,0 +1,83 @@ +//go:build linux && ebpf_integration + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "os" + "testing" + "time" + + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/stretchr/testify/require" +) + +func TestLiveProcessSource(t *testing.T) { + cfg := testConfig() + es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + if err := es.Open(cfg); err != nil { + t.Fatalf("opening process source: %v", err) + } + t.Cleanup(func() { _ = es.Close() }) + + cmd := execLookPath() + p, err := os.StartProcess(cmd, []string{cmd}, &os.ProcAttr{ + Files: []*os.File{nil, nil, nil}, + }) + require.NoError(t, err) + _, _ = p.Wait() + + deadline := time.Now().Add(5 * time.Second) + var sawExec, sawExit bool + for time.Now().Before(deadline) && (!sawExec || !sawExit) { + select { + case evt := <-es.Events(): + switch evt.Type { + case event.Execve: + if evt.PID == uint64(p.Pid) { + sawExec = true + } + case event.Exit: + if evt.PID == uint64(p.Pid) { + sawExit = true + } + } + case err := <-es.Errors(): + t.Fatalf("event source error: %v", err) + case <-time.After(50 * time.Millisecond): + } + } + if !sawExec { + t.Fatal("did not observe execve for spawned process") + } + if !sawExit { + t.Fatal("did not observe exit for spawned process") + } +} + +func execLookPath() string { + for _, p := range []string{"/bin/true", "/usr/bin/true"} { + if _, err := os.Stat(p); err == nil { + return p + } + } + return "/bin/sh" +} diff --git a/internal/ebpf/source_test.go b/internal/ebpf/source_test.go new file mode 100644 index 000000000..439640af6 --- /dev/null +++ b/internal/ebpf/source_test.go @@ -0,0 +1,118 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "testing" + + "github.com/rabbitstack/fibratus/pkg/config" + "github.com/rabbitstack/fibratus/pkg/event" + "github.com/rabbitstack/fibratus/pkg/ps" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func testConfig() *config.Config { + cfg := &config.Config{ForwardMode: true} + cfg.EventSource.Init() + return cfg +} + +func TestStartupReplayAndLateSnapshot(t *testing.T) { + cfg := testConfig() + es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + + hot := sampleExecve() + hot.PID = 42 + hot.TGID = 42 + hot.StartBootTime = 100 + hot.Comm = [16]byte{} + hot.Filename = [256]byte{} + copy(hot.Comm[:], "hot-a") + copy(hot.Filename[:], "/bin/hot-a") + es.handleRecord(encodeRaw(t, hot)) + require.Len(t, es.pending, 1) + ok, _ := es.psnap.Find(42) + assert.False(t, ok) + + snap := hot + snap.Kind = eventKindSnapshot + snap.Type = 0 + snap.Comm = [16]byte{} + copy(snap.Comm[:], "snap-a") + es.handleSnapshot(snap) + ok, got := es.psnap.Find(42) + require.True(t, ok) + assert.Equal(t, "snap-a", got.Name) + + es.finishBaseline() + assert.True(t, es.live.Load()) + assert.Nil(t, es.pending) + + select { + case evt := <-es.Events(): + assert.Equal(t, event.Execve, evt.Type) + assert.Equal(t, uint64(42), evt.PID) + require.NotNil(t, evt.PS) + assert.Equal(t, "hot-a", evt.PS.Name) + assert.Equal(t, uint64(100), evt.PS.StartBootTime) + default: + t.Fatal("expected replayed execve on the output queue") + } + + late := snap + late.Comm = [16]byte{} + copy(late.Comm[:], "stale") + es.handleSnapshot(late) + ok, got = es.psnap.Find(42) + require.True(t, ok) + assert.Equal(t, "hot-a", got.Name) +} + +func TestPIDReuseKeepsDistinctUUIDs(t *testing.T) { + cfg := testConfig() + es := NewEventSource(ps.NewSnapshotter(), cfg, nil).(*EventSource) + es.live.Store(true) + + first := sampleExecve() + first.PID = 8 + first.TGID = 8 + first.StartBootTime = 1 + first.Comm = [16]byte{} + copy(first.Comm[:], "one") + es.handleRecord(encodeRaw(t, first)) + + exit := rawEvent{Kind: eventKindExit, Type: uint32(event.Exit), PID: 8, TGID: 8, StartBootTime: 1} + es.handleRecord(encodeRaw(t, exit)) + + second := first + second.StartBootTime = 2 + second.Comm = [16]byte{} + second.Filename = [256]byte{} + copy(second.Comm[:], "two") + copy(second.Filename[:], "/bin/two") + es.handleRecord(encodeRaw(t, second)) + + ok, got := es.psnap.Find(8) + require.True(t, ok) + assert.Equal(t, "two", got.Name) + assert.Equal(t, uint64(2), got.StartBootTime) +} diff --git a/internal/ebpf/testdata/execve.json b/internal/ebpf/testdata/execve.json new file mode 100644 index 000000000..f676d77b6 --- /dev/null +++ b/internal/ebpf/testdata/execve.json @@ -0,0 +1,19 @@ +{ + "name": "execve", + "pid": 4242, + "tid": 4242, + "seq": 7, + "params": { + "cmdline": "/bin/bash", + "exe": "/bin/bash", + "gid": "1000", + "name": "bash", + "pid": "4242", + "ppid": "1", + "retval": "0", + "start_boot_time": "123456789", + "syscall_id": "59", + "tid": "4242", + "uid": "1000" + } +} diff --git a/pkg/api/server_linux.go b/pkg/api/server_linux.go new file mode 100644 index 000000000..72dcd0db1 --- /dev/null +++ b/pkg/api/server_linux.go @@ -0,0 +1,62 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package api + +import ( + "fmt" + "net" + "os" + "strings" + + "github.com/rabbitstack/fibratus/pkg/config" +) + +var listener net.Listener + +// StartServer starts the HTTP server with the specified configuration. +func StartServer(c *config.Config) error { + var err error + transport := c.API.Transport + if strings.HasPrefix(transport, "unix://") { + path := strings.TrimPrefix(transport, "unix://") + _ = os.Remove(path) + listener, err = net.Listen("unix", path) + if err != nil { + return fmt.Errorf("fail to listen on the %q socket: %w", path, err) + } + } else { + listener, err = net.Listen("tcp", transport) + if err != nil { + return fmt.Errorf("fail to listen on %q: %w", transport, err) + } + } + + setupServer(listener, c) + return nil +} + +// CloseServer shutdowns the server by stopping the listener. +func CloseServer() error { + if listener != nil { + return listener.Close() + } + return nil +} diff --git a/pkg/event/event_linux.go b/pkg/event/event_linux.go index 52d873418..a2113821e 100644 --- a/pkg/event/event_linux.go +++ b/pkg/event/event_linux.go @@ -20,7 +20,11 @@ package event -import "github.com/rabbitstack/fibratus/pkg/event/params" +import ( + "os" + + "github.com/rabbitstack/fibratus/pkg/event/params" +) const cloneThread = uint64(0x00010000) @@ -41,3 +45,19 @@ func (e *Event) IsTerminateProcess() bool { return e.Type == Exit } func (e *Event) PartialKey() uint64 { return uint64(e.Type)<<32 | uint64(e.PID) } + +// DropCurrentProc determines if events generated by the Fibratus process are dropped. +var DropCurrentProc = true + +var currentPid = uint64(os.Getpid()) + +// IsDropped reports whether the event should be omitted from the output queue. +func (e *Event) IsDropped(_ bool) bool { + return DropCurrentProc && e.PID == currentPid +} + +// IsStackWalk reports whether the event is a stack walk record. +func (e *Event) IsStackWalk() bool { return false } + +// IsState reports whether the event is used only for state management. +func (e *Event) IsState() bool { return false } diff --git a/pkg/event/params/names_linux.go b/pkg/event/params/names_linux.go index 4d5acd049..edf79f1f0 100644 --- a/pkg/event/params/names_linux.go +++ b/pkg/event/params/names_linux.go @@ -27,4 +27,10 @@ const ( UID = "uid" // GID is the effective Linux group identifier. GID = "gid" + // SyscallID is the raw architecture-specific syscall number. + SyscallID = "syscall_id" + // Retval is the syscall return value. + Retval = "retval" + // StartBootTime is the process start time measured from system boot, in nanoseconds. + StartBootTime = "start_boot_time" ) diff --git a/pkg/event/sequencer_linux.go b/pkg/event/sequencer_linux.go new file mode 100644 index 000000000..33ea66a38 --- /dev/null +++ b/pkg/event/sequencer_linux.go @@ -0,0 +1,55 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package event + +import "sync/atomic" + +// Sequencer increments the event sequence number. +type Sequencer struct { + seq uint64 +} + +// NewSequencer creates an in-memory event sequencer. +func NewSequencer() *Sequencer { + return &Sequencer{} +} + +// Increment increments the sequence number atomically. +func (s *Sequencer) Increment() { + atomic.AddUint64(&s.seq, 1) +} + +// Get returns the current sequence number. +func (s *Sequencer) Get() uint64 { + return atomic.LoadUint64(&s.seq) +} + +// Reset sets the sequence number to zero. +func (s *Sequencer) Reset() error { + atomic.StoreUint64(&s.seq, 0) + return nil +} + +// Close is a no-op on Linux. +func (s *Sequencer) Close() error { return nil } + +// Shutdown closes the sequencer. +func (s *Sequencer) Shutdown() error { return s.Close() } diff --git a/pkg/util/signals/signals.go b/pkg/util/signals/signals.go index d7670af07..d4d69a7f4 100644 --- a/pkg/util/signals/signals.go +++ b/pkg/util/signals/signals.go @@ -1,3 +1,5 @@ +//go:build windows + /* * Copyright 2021-2022 by Nedim Sabic Sabic * https://www.fibratus.io diff --git a/pkg/util/signals/signals_linux.go b/pkg/util/signals/signals_linux.go new file mode 100644 index 000000000..f202280ba --- /dev/null +++ b/pkg/util/signals/signals_linux.go @@ -0,0 +1,47 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package signals + +import ( + "os" + "os/signal" + "syscall" + + log "github.com/sirupsen/logrus" +) + +// Install setups the signal handler. Returns a blocking +// channel which receives an input after Interrupt or Term +// signals are triggered. +func Install() chan struct{} { + sigCh := make(chan os.Signal, 1) + signal.Notify(sigCh, os.Interrupt, syscall.SIGTERM) + + stopCh := make(chan struct{}) + + go func() { + sig := <-sigCh + log.Infof("got signal %q, shutting down...", sig) + stopCh <- struct{}{} + }() + + return stopCh +} From 63dbf00cb54e3e215721e4384fd11941a4b1e27e Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:25:49 +0200 Subject: [PATCH 2/8] fix(ebpf): Skip optional fork and vfork attach failures Some kernels refuse a perf link on the legacy fork/vfork syscall tracepoints while clone and clone3 still attach. Treat those links as optional so the process source can open. --- internal/ebpf/loader.go | 56 +++++++++++++++++++++++++----------- internal/ebpf/loader_test.go | 41 ++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 17 deletions(-) create mode 100644 internal/ebpf/loader_test.go diff --git a/internal/ebpf/loader.go b/internal/ebpf/loader.go index 1e2145ea1..eebb59583 100644 --- a/internal/ebpf/loader.go +++ b/internal/ebpf/loader.go @@ -21,14 +21,18 @@ package ebpf import ( + "errors" "fmt" "io" + "os" + "strings" "sync" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/rlimit" log "github.com/sirupsen/logrus" + "golang.org/x/sys/unix" ) type loader struct { @@ -118,25 +122,28 @@ func (l *loader) dropCount() uint64 { func (l *loader) attachHotPath() error { type tp struct { - group string - name string - prog *ebpf.Program + group string + name string + prog *ebpf.Program + optional bool } tracepoints := []tp{ - {"syscalls", "sys_enter_execve", l.exec.HandleSysEnterExecve}, - {"syscalls", "sys_exit_execve", l.exec.HandleSysExitExecve}, - {"syscalls", "sys_enter_execveat", l.exec.HandleSysEnterExecveat}, - {"syscalls", "sys_exit_execveat", l.exec.HandleSysExitExecveat}, - {"syscalls", "sys_exit_exit_group", l.exit.HandleSysExitExitGroup}, - {"syscalls", "sys_exit_exit", l.exit.HandleSysExitExit}, - {"syscalls", "sys_enter_clone", l.clone.HandleSysEnterClone}, - {"syscalls", "sys_exit_clone", l.clone.HandleSysExitClone}, - {"syscalls", "sys_enter_clone3", l.clone.HandleSysEnterClone3}, - {"syscalls", "sys_exit_clone3", l.clone.HandleSysExitClone3}, - {"syscalls", "sys_enter_fork", l.clone.HandleSysEnterFork}, - {"syscalls", "sys_exit_fork", l.clone.HandleSysExitFork}, - {"syscalls", "sys_enter_vfork", l.clone.HandleSysEnterVfork}, - {"syscalls", "sys_exit_vfork", l.clone.HandleSysExitVfork}, + {"syscalls", "sys_enter_execve", l.exec.HandleSysEnterExecve, false}, + {"syscalls", "sys_exit_execve", l.exec.HandleSysExitExecve, false}, + {"syscalls", "sys_enter_execveat", l.exec.HandleSysEnterExecveat, false}, + {"syscalls", "sys_exit_execveat", l.exec.HandleSysExitExecveat, false}, + {"syscalls", "sys_exit_exit_group", l.exit.HandleSysExitExitGroup, false}, + {"syscalls", "sys_exit_exit", l.exit.HandleSysExitExit, false}, + {"syscalls", "sys_enter_clone", l.clone.HandleSysEnterClone, false}, + {"syscalls", "sys_exit_clone", l.clone.HandleSysExitClone, false}, + {"syscalls", "sys_enter_clone3", l.clone.HandleSysEnterClone3, false}, + {"syscalls", "sys_exit_clone3", l.clone.HandleSysExitClone3, false}, + // fork/vfork are legacy wrappers. libc uses clone/clone3, and some + // kernels refuse a perf link on these syscall tracepoints. + {"syscalls", "sys_enter_fork", l.clone.HandleSysEnterFork, true}, + {"syscalls", "sys_exit_fork", l.clone.HandleSysExitFork, true}, + {"syscalls", "sys_enter_vfork", l.clone.HandleSysEnterVfork, true}, + {"syscalls", "sys_exit_vfork", l.clone.HandleSysExitVfork, true}, } for _, t := range tracepoints { if t.prog == nil { @@ -144,6 +151,10 @@ func (l *loader) attachHotPath() error { } lnk, err := link.Tracepoint(t.group, t.name, t.prog, nil) if err != nil { + if t.optional && isAttachUnavailable(err) { + log.Warnf("skipping optional %s/%s: %v", t.group, t.name, err) + continue + } return fmt.Errorf("attaching %s/%s: %w", t.group, t.name, err) } l.links = append(l.links, lnk) @@ -160,6 +171,17 @@ func (l *loader) attachHotPath() error { return nil } +func isAttachUnavailable(err error) bool { + if err == nil { + return false + } + if errors.Is(err, unix.EPERM) || errors.Is(err, unix.ENOENT) || errors.Is(err, os.ErrPermission) || errors.Is(err, os.ErrNotExist) { + return true + } + msg := strings.ToLower(err.Error()) + return strings.Contains(msg, "permission denied") || strings.Contains(msg, "no such file") +} + func (l *loader) runTaskIterator() error { if l.iter == nil || l.iter.DumpTask == nil { return fmt.Errorf("missing iter/task program") diff --git a/internal/ebpf/loader_test.go b/internal/ebpf/loader_test.go new file mode 100644 index 000000000..24f52cfe9 --- /dev/null +++ b/internal/ebpf/loader_test.go @@ -0,0 +1,41 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ebpf + +import ( + "errors" + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "golang.org/x/sys/unix" +) + +func TestIsAttachUnavailable(t *testing.T) { + assert.False(t, isAttachUnavailable(nil)) + assert.False(t, isAttachUnavailable(errors.New("verifier rejected"))) + assert.True(t, isAttachUnavailable(unix.EPERM)) + assert.True(t, isAttachUnavailable(unix.ENOENT)) + assert.True(t, isAttachUnavailable(os.ErrPermission)) + assert.True(t, isAttachUnavailable(fmt.Errorf("attaching syscalls/sys_enter_fork: cannot create bpf perf link: %w", unix.EPERM))) + assert.True(t, isAttachUnavailable(errors.New("cannot create bpf perf link: permission denied"))) +} From a95cdc6d60a57c68192cafe09d44c09695f87de8 Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:48:35 +0200 Subject: [PATCH 3/8] fix(ebpf): Emit process exits from sched_process_exit The exit and exit_group syscalls never return, so their raw syscall exit tracepoints never fire, and processes terminated by signals never enter exit_group at all. Attach a tp_btf program to sched_process_exit instead, emitting the raw wait status from the task exit code when the thread-group leader terminates. Also read effective instead of real credentials in every program so the uid/gid parameters match their documented semantics. --- internal/ebpf/c/clone.bpf.c | 8 ++--- internal/ebpf/c/execve.bpf.c | 4 +-- internal/ebpf/c/exit.bpf.c | 57 +++++++++++++++-------------- internal/ebpf/c/proc_iter.bpf.c | 4 +-- internal/ebpf/clone_bpfeb.o | Bin 30048 -> 30072 bytes internal/ebpf/clone_bpfel.o | Bin 30048 -> 30072 bytes internal/ebpf/execve_bpfeb.o | Bin 16352 -> 16376 bytes internal/ebpf/execve_bpfel.o | Bin 16352 -> 16376 bytes internal/ebpf/exit_bpfeb.go | 9 ++--- internal/ebpf/exit_bpfeb.o | Bin 11336 -> 6648 bytes internal/ebpf/exit_bpfel.go | 9 ++--- internal/ebpf/exit_bpfel.o | Bin 11336 -> 6648 bytes internal/ebpf/loader.go | 62 +++++++++++++++++++------------- internal/ebpf/prociter_bpfeb.o | Bin 6264 -> 6288 bytes internal/ebpf/prociter_bpfel.o | Bin 6264 -> 6288 bytes internal/ebpf/spike/c/vmlinux.h | 1 + 16 files changed, 80 insertions(+), 74 deletions(-) diff --git a/internal/ebpf/c/clone.bpf.c b/internal/ebpf/c/clone.bpf.c index d2e5bdfde..cc2deacea 100644 --- a/internal/ebpf/c/clone.bpf.c +++ b/internal/ebpf/c/clone.bpf.c @@ -49,8 +49,8 @@ static __always_inline int handle_clone_exit(long ret, u32 syscall_id) e->ppid = BPF_CORE_READ(parent, tgid); cred = BPF_CORE_READ(task, real_cred); if (cred) { - e->uid = BPF_CORE_READ(cred, uid.val); - e->gid = BPF_CORE_READ(cred, gid.val); + e->uid = BPF_CORE_READ(cred, euid.val); + e->gid = BPF_CORE_READ(cred, egid.val); } val = bpf_map_lookup_elem(&scratch, &key); @@ -149,8 +149,8 @@ int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_ cred = child->real_cred; if (cred) { - e->uid = cred->uid.val; - e->gid = cred->gid.val; + e->uid = cred->euid.val; + e->gid = cred->egid.val; } key = ((u64)parent->tgid << 32) | (u32)parent->pid; diff --git a/internal/ebpf/c/execve.bpf.c b/internal/ebpf/c/execve.bpf.c index 55ec98d4c..04ee29ba2 100644 --- a/internal/ebpf/c/execve.bpf.c +++ b/internal/ebpf/c/execve.bpf.c @@ -44,8 +44,8 @@ static __always_inline int handle_exec_exit(long ret, u32 syscall_id) e->ppid = BPF_CORE_READ(parent, tgid); cred = BPF_CORE_READ(task, real_cred); if (cred) { - e->uid = BPF_CORE_READ(cred, uid.val); - e->gid = BPF_CORE_READ(cred, gid.val); + e->uid = BPF_CORE_READ(cred, euid.val); + e->gid = BPF_CORE_READ(cred, egid.val); } val = bpf_map_lookup_elem(&scratch, &key); diff --git a/internal/ebpf/c/exit.bpf.c b/internal/ebpf/c/exit.bpf.c index 00e32465c..2228444bd 100644 --- a/internal/ebpf/c/exit.bpf.c +++ b/internal/ebpf/c/exit.bpf.c @@ -6,17 +6,25 @@ char LICENSE[] SEC("license") = "Dual MIT/GPL"; -static __always_inline int handle_exit(long ret, u32 syscall_id) +/* Process exits are captured from sched_process_exit rather than the + * syscalls/sys_exit_{exit,exit_group} tracepoints: those syscalls never + * return, so their exit tracepoints never fire. The scheduler hook also + * covers processes terminated by signals, which never enter exit_group. + */ +SEC("tp_btf/sched_process_exit") +int BPF_PROG(handle_sched_process_exit, struct task_struct *task) { struct fibratus_event *e; - struct task_struct *task; struct task_struct *parent; const struct cred *cred; - u64 id; - id = bpf_get_current_pid_tgid(); - /* Only emit process exits from the thread-group leader. */ - if ((u32)id != (id >> 32)) + if (!task) + return 0; + + /* Only emit process exits from the thread-group leader; thread + * exits are not process exits. + */ + if (task->pid != task->tgid) return 0; e = reserve_event(); @@ -25,33 +33,24 @@ static __always_inline int handle_exit(long ret, u32 syscall_id) e->kind = EVT_KIND_EXIT; e->type = EVT_TYPE_EXIT; - e->syscall_id = syscall_id; - e->retval = ret; - fill_current_ids(e); - - task = (struct task_struct *)bpf_get_current_task(); - e->start_boottime = BPF_CORE_READ(task, start_boottime); - parent = BPF_CORE_READ(task, real_parent); + /* Raw wait status: (code << 8) | termination signal. */ + e->retval = task->exit_code; + e->pid = task->tgid; + e->tid = task->pid; + e->tgid = task->tgid; + e->start_boottime = task->start_boottime; + __builtin_memcpy(&e->comm, task->comm, sizeof(e->comm)); + + parent = task->real_parent; if (parent) - e->ppid = BPF_CORE_READ(parent, tgid); - cred = BPF_CORE_READ(task, real_cred); + e->ppid = parent->tgid; + + cred = task->real_cred; if (cred) { - e->uid = BPF_CORE_READ(cred, uid.val); - e->gid = BPF_CORE_READ(cred, gid.val); + e->uid = cred->euid.val; + e->gid = cred->egid.val; } bpf_ringbuf_submit(e, 0); return 0; } - -SEC("tp/syscalls/sys_exit_exit_group") -int handle_sys_exit_exit_group(struct trace_event_raw_sys_exit *ctx) -{ - return handle_exit(ctx->ret, (u32)ctx->id); -} - -SEC("tp/syscalls/sys_exit_exit") -int handle_sys_exit_exit(struct trace_event_raw_sys_exit *ctx) -{ - return handle_exit(ctx->ret, (u32)ctx->id); -} diff --git a/internal/ebpf/c/proc_iter.bpf.c b/internal/ebpf/c/proc_iter.bpf.c index 31c7b8be4..eb41eadaa 100644 --- a/internal/ebpf/c/proc_iter.bpf.c +++ b/internal/ebpf/c/proc_iter.bpf.c @@ -51,8 +51,8 @@ int dump_task(struct bpf_iter__task *ctx) cred = task->real_cred; if (cred) { - e->uid = cred->uid.val; - e->gid = cred->gid.val; + e->uid = cred->euid.val; + e->gid = cred->egid.val; } bpf_ringbuf_submit(e, 0); diff --git a/internal/ebpf/clone_bpfeb.o b/internal/ebpf/clone_bpfeb.o index bd257bec4254700d3b62bd78dd04128ab236de7a..e03647d4529c7b5e2a10c51f75e09c3ad1479f8b 100644 GIT binary patch delta 4343 zcmeH~e`u9e7{{M;-h1EOyIt+h<&RkIzD}nz@AT@_U7c<(Ci4~s>jz{Ic%8fHO6Rg! zmePBxK_fzk$H>5N1=$FZ#Tzy#+J%f38QdUpM9_l%2=$K%L4zRsKJPtuyxq{G{?r5S zInU=j&-wM7=iGDd#3eCxNerwI$qy6H1w=x%&)OoQYBKPuu#{D`MZGzzULCEnL_`f& zO}bO&sT&%(!&Q$4Z`8}b8eo{K?qk)j$=L})I@Cc)EUi+twhW4{~0FtjOkVPWh63CSi3 z9!AMyXCe1%IS2U-$Sa8iir^_^jgW1~??aviISct5K0ZGp;J!x#fg~OSKmR(())<$dR{!C{>OGdr2 z>TN+P)|lE+Qo{%)VrwaoPm_31yKt6O~u`xDDLqjx~NQo%2K^ zbBGMq?!{IX8D5;`JE2Et$V(_sllTZBV_-{IZ$kdG0-P%0cFoI)awhaWnp=s^&j9xq z*fEP013m-pa9HymqA^6PkuTwK&Buu@U;sozVM0F=PSu@pXd4Q4`)nqF*^14|@U|oOJ^&w?pC<`K1};9p}5JBqTucvHoC zlB;oP$^mV_jQ5co*8C*NB-ShQn%^U7&jpWb9#ipD%=rN`j^a^+j~8zqC;2#TUPCXo z;y0`Stqe=IV#YL#`T|GpAlb?KgpXOD)O-(Ch)26&`xwhCzS5is8&spB-Dgk>-sWST zkoKEr&mHy@^kr*F_J+XPFJ&EYA=?jVeK*O&c=Q-2dG=@Ujy+g{w4;x&N%o%xycoI ze+T~iK-U=uuh~-9c?W+cT$lK}XJoR3OFYLXmOWV!LW#sD)bCk7k^ZfaC%_Ijcme{F z%pvo0zQEHl!5u?C?Xh`aUkvbB^Tz<6Ew4X4AwFAPdmU3CQio!5+^t&;b7dB^1Rwt` zK{D~CC+@JwJli@S54+iiu2qZqD%(9OCX~I;5tAy1JfZ9!N7yQdJge-d9WkJC$n(nH s@4zyL+*0;4Uy7GisJBU^mEGGaHmEG}L6z%mkxxaB9cUFPWgoEq0uo?$I{*Lx delta 4178 zcmeH~U2IfE6vxlZ-OsjaKPX*9+AXxO%9d^^(xuRLi`q8ILp6x2F{CXe6jQX6qNwO? z2oDWHY%)QsLTnlyYC=rOjam#z`(U&QjcL?}eWMQ$2{ExIByN-h|L5LWa$D0HjV~mf zWai9o&bjk(X6~JH)3?OzEiqUr!k1!ictk|C<~OSY1raro-zGvTT+l2?O?sP!M~xNC zS!v&=OBz{Y1uuA)>g68|FwAe(i>pt`!U!R~-9d4dUxm4(m`bl%mlxwIG%YOOj8H&D zi)%_lOoh4RVcA~FCDFbPvfJD9$YY%>enJF?iG0X8W7bT`?}j%3qlB7XSJKG6CWyT6 zLUO$y_4gpFkS9?;3t3GhAiU>MMq-0euzo%JaRGEImjAh zBp#TyA%{`F0LkqM)IWpd_BPZ%$AdfwgzpPTd;O?i=BXk*i2B3=n_5TZ`_{2h`Q;($ z|3gK~i)G-B+Erd6DpfbiGwL+TE9xT3U)6N^MiEx&@^yJ1qf<^#BV+8G5fxouxoY$w zXC|z9h6|(z*b`RY`nS9)y|GDbRSnTPv01$nJt(%QpQDFFOg&$*cIDB7ots{7?|yVU z#cHc+V-yVTuc$wu_ExO2Mk{uBM2)&pb?#wpwOy+Zc82T1`+E0xR@AHWo6oBq)y?hj zvh58Xdk0alz{U08r(B!>kGOaz_(vB%NtESvaTB=Kx?X)%mVQk%zLLmb?IA2N!|c6(96HnsF|^fya!21w80t?r%(U2hmgzJhOmPHk;%- zMKp~8u>JPDd_NG)$Hb;MtyIjoAA7eI&{ErdM2GB61 z4fYUS=LrpK#tLq(0*|^Fd+5RJ^401c}|~ixRr^Dv4wT zxFy3Xub9kpmwDWt{kB&e~04 zmalkRQ8rs7Ib`9xVV+*!16h`uYML;wk}NAElC9c`8MmTLYQ8}-f*VG*xfrKQb~%ju zOh#GX<6_ny)4Y;o41tU6cQI~NIpFZl>`f%MtU<$|Heg->9@D&+WE`hf&S?IKWTFV1 z(tJg=w3L|NW5&6N9*}vl2o~haxbzIY*zB8F0S7r@{S-5%VMH9xkt91=A9FG5oWwGLQX+eB1HTW5{bQS^KFDjoOrd|;Nu?$B%ZC!du=3R7IG`}(++zgn*Vgz4sn+p z$g%s|IOe|{XapVomw^@;$6RbhcF_Gl4z$QPCZc()2){Bi2I<-Y8DGH31>EIu)*VPB zesSkLKj3g?&!zT2_DgIH`$-g)okaH+@b;{X5v diff --git a/internal/ebpf/clone_bpfel.o b/internal/ebpf/clone_bpfel.o index 145a50e697c14faecd5b5dd1b9a377b028abd6b8..b0d4f7accca9d31e7e7bedd61e56010b9fd70553 100644 GIT binary patch delta 4163 zcmeH~e{54#6vxkf>q@&7Y&)P^=Grpo*kpr_32PbK3c*ngB%mf@B4LwNHXSf=7{;$R zz(h=lHaVD#1Ol3zBK`f{zbw~ z-@WH^&bja2ch9}=osL}<<5xwxQp8SI?Qn=Fuk^}Tz& zJSSK`BW%(%;V|1azsqdbR}^R2=P~vLmk9TJP9k}IY+MNanwRe;awPfu%+N~Qo)Wlr z<`c=0`*7`7at8A2kZaMBAUX=U4q^iGJCOa5`yiizyZ~~Bt7g@S4|wgY+4>t840CdL zp?@24Jz81bg&ap4#&t^R6Oaw1Z-qPv8Mk%FXBvi_eR85tAfvdZpgN!9v&b|Njb?o~ zFH)j8T^x)AHJ6*4B0Ya=0Xh>_N-gKxjn_-&RH(%xvIKR)bWPuPO*S5R&NyZxw)o%N@{Co z``j1Xx~du|x~Psf)s%~rk*-sdvGh+W_Dn+mFNj)mxWmQ;ux~S2Ct8RO zt}&v1<3?#JQYuT z4i4Hl(YSKB#%-}ct=uJEEQ>61T^hi&Oqe>!yTQ&Cg5=vk@~eCz7Mk%YwA$msJoi$2 z30$*AP*Qna;w!P&+)}GS8#4C9Z;E?6CA)&w&bga*%Id~|PrfhkGmDIQ7xy;m?k=db z{en&zqUm0Y@8-9`#U+91@|`~+#(=6p-rcx88|XnnK4Z_)dx5^21HHN>u%rbabD4r_h-PeTc3epKG8C)UxL@PSsJZE4rqP`z#i}##Xo`ll^V6$ z*bVMb>?wr3TBB|oyTRKOm&0BZ)u_+L1>pTzUP_DLQ1+lkY2_e!DtJKgvtTWz(Wv6L zz=4EDmlcn2RWhW11+5oz_NwH;%oU!}s95n0*$dAQws8TtQt^Gr`~vM7MQvOFjwyZs z_S$ZZ5;k^&RWhWLbSQ;lmCd0XyvD{(aEFcM4Z9VaxkV4wMX{M%U=h=b&D>(AM*TMS zgO8c~7|}XRpbV>gTscVgfd_3JO3)#rcn|D(FJT=OUz52-TBGZVTlvt#rFsUbp)6i# z=b`;mv3xAa4amV{k<*ggp?D0u5d5X$N@OgOoft(HnaN7i2%;$a-C`WwT)+P_kW7j1 zV4z!!gQV|HpGfA`|9zlajDzthcj@H0mw$euYU!*H;EYTN2~NWVmM2!Oqzgwu7HqBw zc6lQ0q38$eQt^!HkWmg6k1DqIgezwc6zA>%#nv8(DSvB1@@&~djiGsasKi*Y;*b2= zKDCjsk7zco12(P%_uDuE9x!?zy;&;CxOdaA7~}Y6U0mlO)KQLi>mtEJsQWnnye`r_ xgnE?YTXc91p$>EWg-^v^7CrT%mE%3lVlnrkKE^{m&6@W){;j7;Bssoy_FpZZYD544 delta 4204 zcmeH~U2IfE6vxlp?k>A+fp!=AA+}wh+iqz~cPX@`^kZA0G+L9cL9ND+EtJ?As|8wv zBHjjls2_xq30etF2=Y)9F(o%@F*Wo-w4`8K^`URnL?R&?Nrbpj68xXLv*vb_Vl=)q zVX`xGe&?Kf=gyqDbI(R^h`AeL&@a5_iVoU@Uq0&IE;~GaIqr^#ESclkE=Yc1-!5$O ztmii)>Nt5%C*!Q=N&CI-@{bM}=9cl)sy8){pST<>s51M{G@^jKo#RUnD1EcpzkJv! zi*qYiXPLB8*v!7&ZFaP~Af|6mw;8YJHVD@#2NBm%<4WG|Np>fZEhI0k&1>MhhEQL# zV;uDTsBc3KqMtxL4!ImX38G=hJU|HYN02u`?w6bL%f&g_m0zg8hXL>$wlwHZLas#5 zg$Le@-ivxh=|hk|RQd?yk0Il>HlD{PkYlqaIu9AexeU#P1fOctMD$h6M=tkeYkw?} z>wLMcJD4GEms%^+eVMK@SB&aq5OhRQgJ~OA^bp&`ukw{rRFT6sQhX2gv2xQ(E_|+*z7!jFi^cM5VkIJo$j~+M>)|9o|~+?tObYO6%nC zhGAJ#zO~&Qo1Hm!4O&lZ4kp*3-Dcqs__T!^!QWYUE7+bKn;}GMLajD_D4)_+e~uVu zjDeGgx-sWCJL4>`ROIPVc%_UJH7jkLTNURcI*tjV9>tr$OTQ!&Wr@%g{co+O# zlSI=AJR4)3-r@l+<1w*!_%Xi)r%n@PC9v1R$*_kk%>K=a#S&cqJy9fqJ1v|H`vH@6 zqII~yIY%_49G(C>e#E|4ybGLtooK|uZt$q$e%MofCK|Kw67YoLcVW-HNi=C;H+YtL z1!jC64qB8bsvNG$LsgkU-X&>R7B0LlWnkJWOdaz+u%kr~9~I1p<;AMJ)rX;fN9oB_Khpi#etlfeTqZlv{a$l0jTpmJbd0UlMn6RcHfG_Cl3 zaAruOS;gaWTXUZN4YX-^<}{T&n7P6;8f7WI4toaX;I(iv*soYawwKYNQNY5<;3~xr z!XE6=C}d$5SS3R`vBN4TS}hK1z-<~2aj1U$i@W|iuc2w^aj>Z@m0<(1~s~=IKEvtvp9maMzTed z=*!T5id2wG8hn{==}N(~Wkry^QTS<@cPgF(SAZ`o_9KyD{!=k0xGXL>&;sM=|8^kG zl>f^>3yh;LI3rHE|HpwA7zgp^1yqdUA;7CMFC=(L<}ts>9HKk8o(uNeJUhRr zY>&r$Nb%Enqqs~kcR+|Mrj^4j6)R`L9>_}E1B&B&pi242ABbN$b5F^JmbD*5S{%V% zUQiTl9x6obx3C}lj)i^TGsdyU?yeGcdF+J?Vp4`*)WxivL7OGRJv#o*&!Fv;;k~*z zAZO5y$?!fMo-=5jGQ9t5ShIU8MY9a|*5hB}F|-46rng>;J}2FMb@<;G?koHY#AIs~ diff --git a/internal/ebpf/execve_bpfeb.o b/internal/ebpf/execve_bpfeb.o index 4b62ac978b7947976e078a44d32c3664763f74f8..237dca6b17f25f9558e4e1ae2dbe6c893759d050 100644 GIT binary patch delta 1809 zcmeH|O-NKx6vxk`rmnX$|PvfrbQR;J^$Z5=YG9& z=bASQW+uYgW}~;9EXuRxNmj`X*F|RVv^UAu%D3|sufaUI%8I-r7u2u(De90?<|ZOh z^#i#f-NgQ3FuX5 zA9Nb}8*~e_1zTDuLd0MDw>aERUhvnOpPWSaqe|>>1-4a$LVrfHAJu?fgBAS==&!J% zzn@5D7%0|ZC7#U1)e<;aX`SDN}Yv$KN!)goZ17}l% z{7fjo>%u){d)lL<>#1F_G5$VO%`;(lAT>5TxcmIc(+wRIZEr65PnG;Em75Ry9aWdG zq&Tc7o!FQ+w_N+&QHx#N^yA|e;2pvDk*pxfX1=r~Y#t<<-bAD%WN(SPC65!$;DRVa zk|&9>m`O=*VDuMf6@D?_wB&n4cX6qdXB&9IX4CzaXih93x2bL=dgMXokajWQ4-T&5 zGWe0wU;Od_>QN#~jPI5H68EoTF@ChjCaEUT3k@9^8E~6u0UL5&mHdEc5hrp^tz#7| zPpG~mS_*-)(k~d-+&L$CmFNvVy4$i@+eh>PKPPuyF!H7K5iN_3f*pWZW`H%3SDZu? zaZ)WytgQin&DzYf+ivKh7tzRiZ~Y4UpLP3I{M%W_%cJ#iZRZZtEgoDH1xcj{EAQ`u_=%1Cm1dE%bhHZ8lk`LRgi^mGL*kQqnd4Io7$6qYOna{|18mpD2MXZ6B zrubUJxbkQM>v$P@^G|j=b3KuWhfVNw$77b^iB6MEaSLspC%R0w%B`-a>=>Vnwc_8L Y6^pZ(%kelH<%wdOj>jC08n@!ccN+*2p8x;= delta 1776 zcmeH|O-NKx6vxkfAM@Urj%Mk+C}y4}ewH&@sX2}_No2GLm$RIpKq*P+gJ{v4O(3m= z8{|$GfVrmXjK}XXcVi39w{V`+|JqdkL>KW+E(A&@#pszq4B4QBwI}DZ3lh8jy zKLS0&m#W(p{R`KE4NhJo!jCG5d~YE;(b0Ts7>=Ub(BDIf`6Tp@kYYX!eFGcCDG9Fx4^z8?Qvca3|7H` z-!8Z!N?-@B zzZI@6yE1gP_3D`mO)-jgxA8~eTK7%3TG@FM9z@up_23-E)oNcTH7e0mH8Qmc>qUGK z$q^ohg|u#%bX(C)daxRJm%T@3BQ=dX5@IJ;RE}hW3LAOg&f;0&5r)9 zkY7@fREnSm;*tj>UzdzpEZC i$olwXZzuk?a=i(5n_GzlOYu};98dHrE$cj&u)hN#3IpB% diff --git a/internal/ebpf/execve_bpfel.o b/internal/ebpf/execve_bpfel.o index 1594ed25c5a2f4a3c13fc225110c6f6dd7550369..26c83806da07f73403f18d135932ae48b82a1a1e 100644 GIT binary patch delta 1770 zcmeH|T}YEr7{{OU?!&#hO`WmLi?*3%il)n@Zu4WKn?(`Yg%A;?=qMr3B2zEoH3;%@ zak>Z!@}dht1?5fB2(k++sK7wMi;6{ESO^MzXb{x@xHA}o2)gQ`hn;i&zvp?*&Uw#s z_AaSyX^C+5tTb{S&$l zdY(Ua1{9;2e{+^;pRk${p8^Edp&Kv?`wShz=tJ9-{wVYh>FaHmc!wbq6p{VF! zHndg!nEQ!IjSu!*Jl)mX9p_F@j5Y96o@N%{w>>>Mb)G+v+oW=D8Z;?`X-ICgnSh@+#u750fu9Vnt z9?rW&Tc}VkwwPuzNaoACz1k6E<&9zQ(wpW(k_QksPY|7zJOZ{&p=?<^lEHb1k4c_E zzIu=7Sr#v*Sfe*$fmv)*ChWoSTOSe?pmt*0X?ACDF5Z!9y--3U$>PAs@ZmO)*RMcFU1vRh17?a#4`KsiE|H7%wQLIErWN6JhOhdrrOE|`B38{ zmf)c#jg9g-^hq9S*4PH0Ykta3@GC71`1_h`iLeR&9ep1UMMOUoi71vT{vcBJ3uxF8 AX#fBK delta 1691 zcmeH|O-NKx6vxkfGov%=j34F9NH#M~F^yS{AEndiIAcUcn{?+U7^5I5G3bK`>bVId z2$bWZ+SEc&u)umPB!Uzog$lG>xG0&(0vA$5p|q&}cf1o1A%a#dI(Xjs|L!^W%UvnqegRViGfG2fCMExa+iU zHr6~LJ)MJb@mGYzd*}v4X-m+15d-jL)t-d@tlCr1E6^C%ivIK+I*KSw%gqG43&9R; zu8Cz*M1^ZLJnShGCwbE27fbxsv$Z(j$P~B|2}@{&7@=+7Z`lRAR zvi}58$2v}#I1l;L8PoIgi&+r+ux5yg|p`{s$pOk4sU zSGDbD#dDh5NjIUxqQIFS$?+@5(3Bof-EN`9FL F{RG&H0{Q>| diff --git a/internal/ebpf/exit_bpfeb.go b/internal/ebpf/exit_bpfeb.go index 6570fe4bf..41ac2799d 100644 --- a/internal/ebpf/exit_bpfeb.go +++ b/internal/ebpf/exit_bpfeb.go @@ -62,8 +62,7 @@ type exitSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type exitProgramSpecs struct { - HandleSysExitExit *ebpf.ProgramSpec `ebpf:"handle_sys_exit_exit"` - HandleSysExitExitGroup *ebpf.ProgramSpec `ebpf:"handle_sys_exit_exit_group"` + HandleSchedProcessExit *ebpf.ProgramSpec `ebpf:"handle_sched_process_exit"` } // exitMapSpecs contains maps before they are loaded into the kernel. @@ -124,14 +123,12 @@ type exitVariables struct { // // It can be passed to loadExitObjects or ebpf.CollectionSpec.LoadAndAssign. type exitPrograms struct { - HandleSysExitExit *ebpf.Program `ebpf:"handle_sys_exit_exit"` - HandleSysExitExitGroup *ebpf.Program `ebpf:"handle_sys_exit_exit_group"` + HandleSchedProcessExit *ebpf.Program `ebpf:"handle_sched_process_exit"` } func (p *exitPrograms) Close() error { return _ExitClose( - p.HandleSysExitExit, - p.HandleSysExitExitGroup, + p.HandleSchedProcessExit, ) } diff --git a/internal/ebpf/exit_bpfeb.o b/internal/ebpf/exit_bpfeb.o index 8dd9327fa924f5a9bac20cc1fb7756c6c46b569b..c3d4e7f6955c97a79ff32f2370fc4a8b5a320ca0 100644 GIT binary patch literal 6648 zcmb_gUyPJj6+iQbmQ|2lS_<`FKeXa5!Y~WcZlU0mvarOpj(|!P{dRWdV|TK%GvoZh zT_ACyG$BUG+NdFEQ#UClq%YFg2qqf#!9*T-z(gbQPeObVV;}HA+e9M%e)pa``|TC# zOK^Fu-_a&1F>9&Nt1v#eZS|B`f*`S<0S4v{`Yetqeb%`hy z)=Jv&k6Zm$*2_4hOC5{X^;+(pmjX(OBbTyP^MYmHx-w>k&yUG_C>1ZvM^QD?MFBv1Xx$%Nx0igU_`l$#O3uU~PXe4TO=8TRFh z$q-lGR(@OgE#)_r-%x&C`Bmi$$}cOwsQiNRdFAJn&nZ8nyrX%Z*enB<6ceUg;Xk|zsRz&ump6E=^#>kNY~roy6*J~S zpWV;M`>@&ra@g%YUvJ|T$HmkI{$_me{~-OYJi;gTVPKf6&*@7R$`6$9Z5JxRXAT_R zvOhnns&|$B{YT~nFk=)+pI7<|lj!24ASU!RNrMNpQK^k?obv}j%bXwN0OVpkK-ur3 zzU<5xtnZ^Br~WWxyq1y!ZG0adXb*ynUB_n506#8#j9Gn5-S6QOkHheTX5)#hg1lX2 zj%lY3h;s};ZceKqj;P;5kVjSKemJOosLFvjN7tWo?4b^gb9^r-2Z}%|K^;&T)CXDs z)pYJSx|Yn{tl2&0#hSS>zMt~{`UF%vAMVE!7#!RZobMSB=f?J35ak@`MNk0hfL_wM za{RBT{21g{b>3t~9n1Y8=#a*GK)|1g$9m{nZ0bRr1F;^gK?lToaDOd;Ue|i8hirl+ zA)uM}5Cx=;s-J^C3i>5B0CkUpjUp*)KLYs(xOPn(RXKpZpmGlK6l9(h9mro-c>(fi z$o=48iL-d%>yyZVeh3-$N_0R!hP(-U0o*0p5R(Ten++_oIi`OCeO_Z^G`@F!9X|m* zros0my#s=iKLuR|gqD65k1QyFydRwU9Aus$)Q>^_96B-$L$DTGKxpZsCZPz*oF4al z2Gys$=(A*hxv^mx($^4xZ7T?Q(!a%vl`4?gpJc<8{FhQq_p=ZC{kTC?>?rVBG+RBbiNQB$JhkebrowiW(- zwyVwZL^Ub}4AFk#lT_o`Raa`&iGUB8ie}|_q0)||)Tq_NVy%rH&0?d_Do%#hP-skK z1&={FR<1Lsc7OkUqiqVBtwy`p3R;EcRH*Vsf&u9%9}D`| zTRCQydbuR62|Sn$GcHBahzgakUTC0tQ7-l%j?;6dO@Vs!hydW>%W5LZcOq z*J`a+8I>s3rl-ZsKD%Skrevzf~=8%TG z$rtOhSf9cs}Br3!+7DKYFqxP+mnBnK|oZ_M+PSO8@;yOOF z{61e+{5?s$0i48mnLqh6NnBOFLas0_Y<0^yr$sxu67WqE?^D&J1C68E~ z!ragG0guHn?g~lFhw)PQKE6zQJ%(wYQM^}Vp7}d5%=~8*ha%^3*D~wIc*GpfMYW&B z)H%QFim!=WWWF1U|0ePV>XW>w_+Kau?u5x(F^s!p^0vik#9qKBH2Jn-+%bNI{^k>m z{OPvHRouH$%hgVd@uqqe<1TZJ_H{8#d!YDrksBCqYRKa5{$hXbJ%0WvZ@~Ke1e}er zh<{^{T^r!!TZ)gUUHwh%|IiH7PdY!j<=U;-J=S95X{w1ZDe^!lwa$;W3+*#Ct%>AV}&Gn-l*GjhZ zX*_6mcn&i5SQlS_jCago-lt70^_QH7^Bx}aaNWZl4>Lc<>f+CP_@ajwJbc~5H$8lN zDK=$^_Q1ng59d6b_wbm9>mKfSc+SJ~9=_<|1rJ~M@J$cjUW##iEU7oo_IUl1 zfrqmm&UrZR;V}=_J>2o|oQLN<{0k4`j9xO{KY92~58qjeQ!6}-??XIZ%G>X$r#<_q zhmU%=;^9*s{*J?|R`>QjB$-w;(~?Xhs$}*Y-j^X*ycEB~PZsvL|IZfv^<}R%`qN1= zmCEsHUZ5NJCDy(0Wbg~6RT!5{b9UNr9ZL6pG`eR`HhfA9d(yJ)mJM=myc_MC2;!Ze zF6^3rs79(N4J01!3wEc zmiN5k8RGSO5w`oy-{sB0hx2#;N^to(f83$&mw%OhHt+}n(|1P_F)6Bb%Iat*<#`^j#NH8D!xc%w!ulp~2E+i=c literal 11336 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 WHs-<^Eot@JW&FJbFqhHocmD@szlUA` diff --git a/internal/ebpf/exit_bpfel.go b/internal/ebpf/exit_bpfel.go index 673523e58..0ddb7d384 100644 --- a/internal/ebpf/exit_bpfel.go +++ b/internal/ebpf/exit_bpfel.go @@ -62,8 +62,7 @@ type exitSpecs struct { // // It can be passed ebpf.CollectionSpec.Assign. type exitProgramSpecs struct { - HandleSysExitExit *ebpf.ProgramSpec `ebpf:"handle_sys_exit_exit"` - HandleSysExitExitGroup *ebpf.ProgramSpec `ebpf:"handle_sys_exit_exit_group"` + HandleSchedProcessExit *ebpf.ProgramSpec `ebpf:"handle_sched_process_exit"` } // exitMapSpecs contains maps before they are loaded into the kernel. @@ -124,14 +123,12 @@ type exitVariables struct { // // It can be passed to loadExitObjects or ebpf.CollectionSpec.LoadAndAssign. type exitPrograms struct { - HandleSysExitExit *ebpf.Program `ebpf:"handle_sys_exit_exit"` - HandleSysExitExitGroup *ebpf.Program `ebpf:"handle_sys_exit_exit_group"` + HandleSchedProcessExit *ebpf.Program `ebpf:"handle_sched_process_exit"` } func (p *exitPrograms) Close() error { return _ExitClose( - p.HandleSysExitExit, - p.HandleSysExitExitGroup, + p.HandleSchedProcessExit, ) } diff --git a/internal/ebpf/exit_bpfel.o b/internal/ebpf/exit_bpfel.o index f9b7dfc31e417f6665df399324e52e24a46c7122..664710f4c78c17568e7309afd0844bde5915a75a 100644 GIT binary patch literal 6648 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 C4JQWx literal 11336 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 R_XsDY07bn1Z8q1K{{aRnlZ^lX diff --git a/internal/ebpf/loader.go b/internal/ebpf/loader.go index eebb59583..98054c573 100644 --- a/internal/ebpf/loader.go +++ b/internal/ebpf/loader.go @@ -120,54 +120,66 @@ func (l *loader) dropCount() uint64 { return drop } +// syscallsGroup is the tracefs group hosting raw syscall tracepoints. +const syscallsGroup = "syscalls" + func (l *loader) attachHotPath() error { type tp struct { - group string name string prog *ebpf.Program optional bool } tracepoints := []tp{ - {"syscalls", "sys_enter_execve", l.exec.HandleSysEnterExecve, false}, - {"syscalls", "sys_exit_execve", l.exec.HandleSysExitExecve, false}, - {"syscalls", "sys_enter_execveat", l.exec.HandleSysEnterExecveat, false}, - {"syscalls", "sys_exit_execveat", l.exec.HandleSysExitExecveat, false}, - {"syscalls", "sys_exit_exit_group", l.exit.HandleSysExitExitGroup, false}, - {"syscalls", "sys_exit_exit", l.exit.HandleSysExitExit, false}, - {"syscalls", "sys_enter_clone", l.clone.HandleSysEnterClone, false}, - {"syscalls", "sys_exit_clone", l.clone.HandleSysExitClone, false}, - {"syscalls", "sys_enter_clone3", l.clone.HandleSysEnterClone3, false}, - {"syscalls", "sys_exit_clone3", l.clone.HandleSysExitClone3, false}, + {"sys_enter_execve", l.exec.HandleSysEnterExecve, false}, + {"sys_exit_execve", l.exec.HandleSysExitExecve, false}, + {"sys_enter_execveat", l.exec.HandleSysEnterExecveat, false}, + {"sys_exit_execveat", l.exec.HandleSysExitExecveat, false}, + {"sys_enter_clone", l.clone.HandleSysEnterClone, false}, + {"sys_exit_clone", l.clone.HandleSysExitClone, false}, + {"sys_enter_clone3", l.clone.HandleSysEnterClone3, false}, + {"sys_exit_clone3", l.clone.HandleSysExitClone3, false}, // fork/vfork are legacy wrappers. libc uses clone/clone3, and some // kernels refuse a perf link on these syscall tracepoints. - {"syscalls", "sys_enter_fork", l.clone.HandleSysEnterFork, true}, - {"syscalls", "sys_exit_fork", l.clone.HandleSysExitFork, true}, - {"syscalls", "sys_enter_vfork", l.clone.HandleSysEnterVfork, true}, - {"syscalls", "sys_exit_vfork", l.clone.HandleSysExitVfork, true}, + {"sys_enter_fork", l.clone.HandleSysEnterFork, true}, + {"sys_exit_fork", l.clone.HandleSysExitFork, true}, + {"sys_enter_vfork", l.clone.HandleSysEnterVfork, true}, + {"sys_exit_vfork", l.clone.HandleSysExitVfork, true}, } for _, t := range tracepoints { if t.prog == nil { - return fmt.Errorf("missing program for %s/%s", t.group, t.name) + return fmt.Errorf("missing program for %s/%s", syscallsGroup, t.name) } - lnk, err := link.Tracepoint(t.group, t.name, t.prog, nil) + lnk, err := link.Tracepoint(syscallsGroup, t.name, t.prog, nil) if err != nil { if t.optional && isAttachUnavailable(err) { - log.Warnf("skipping optional %s/%s: %v", t.group, t.name, err) + log.Warnf("skipping optional %s/%s: %v", syscallsGroup, t.name, err) continue } - return fmt.Errorf("attaching %s/%s: %w", t.group, t.name, err) + return fmt.Errorf("attaching %s/%s: %w", syscallsGroup, t.name, err) } l.links = append(l.links, lnk) } - if l.clone.HandleSchedProcessFork == nil { - return fmt.Errorf("missing sched_process_fork program") + // Successful clones and process exits are captured from scheduler + // tracepoints. Clone syscall exit tracepoints fire in the parent, and + // exit/exit_group never return, so their exit tracepoints never fire. + tracing := []struct { + name string + prog *ebpf.Program + }{ + {"sched_process_fork", l.clone.HandleSchedProcessFork}, + {"sched_process_exit", l.exit.HandleSchedProcessExit}, } - fork, err := link.AttachTracing(link.TracingOptions{Program: l.clone.HandleSchedProcessFork}) - if err != nil { - return fmt.Errorf("attaching sched_process_fork: %w", err) + for _, t := range tracing { + if t.prog == nil { + return fmt.Errorf("missing %s program", t.name) + } + lnk, err := link.AttachTracing(link.TracingOptions{Program: t.prog}) + if err != nil { + return fmt.Errorf("attaching %s: %w", t.name, err) + } + l.links = append(l.links, lnk) } - l.links = append(l.links, fork) return nil } diff --git a/internal/ebpf/prociter_bpfeb.o b/internal/ebpf/prociter_bpfeb.o index 565df7f816d49aeb569219e3c32824350e3e3f97..5340adcd9cb7c4b1b8e19f3d2785acedbb95adf1 100644 GIT binary patch delta 1304 zcmZ9LUr5tY6vxlKzhAoP%&l5mSoE`??2p^j!eObS0xhD99x{m9aw82Phzcv?XJO@A z!X;P_kz@~pjEYI^DfbYypumTq1wGVDL=Pnd;X@C7&u>>5UO4A`Klj{!=XdXKJ~G}k z9u_^%BS)n_LTFbL6y^BR%bubtEjRj{9%QvDj+>b+cFZ!!73WO7W@lZ#{DX$m^* zBaiYJlkTrXCfnm|&IlN`8%Zo9wjoLwnh3+OwxD-t9fR(G-UJ(gKA?3T`WW<9L36;X(Q@dzC1YCjm<^>dO1jqhZet)ft{Ur^cT~g`7I#(L z6{xvi>7>|+M0V7*Tewq00~b#9olB;&Gw#nqw5og6ArV!D>UPnhDm-0cr#j?0+1lI| zA-6l#*l}?nX|-E@=TphXjuaqtDKWT{~>`d?>!eU)VW~&tD5| zX`Uh)t$@bY60cyhBqo^SC6v5n|_9j>Yhz1WBIwme8;m?vcP zfa4@a`ElelUm-CKmbf;aARmy(twIczF&=@mFb4(5H$=~{fo#zHiD(LsQ1W$i{yVl} zjyi0U{7W>!6JkyW8CZCFEx5akc|%ml3m9EE8y{#)^G%{W-h>?2{E+B13X*BfPl*bC z@I)C`gEP!F7W_aoi%%|}=>a(5M_wRTVlzZk41n`x%m*%LK1}q5^Rs2l`J(2tL_hez zbDDABIeyGum{PTteFK6UDyJ>5pA47~g996=o}27}W8Ui^$)P38Uxf45bv|9<#r3#i z&cD<79AX&1u-TwHxFU3z`_&P{$|k)lMHA2A9|J!ZH**@!#exYT4~JqGxtPuPejDP6 oO8aL-zv^B6R!k}@&??4MZ=h4`Q)&2uD#B+}FxV;LsyAr;2Rrn>f&c&j delta 1231 zcmZ9LO-NKx6vxlK?~yajr1PqAW?}iH53Nk!nC4`D3=!leGFrGZCzT8&hN2pAUWw7#Bq!ti0)CKe?hmvZRp>+KLPz0n)?TN8Yc24 zyhH_J3=T5`d2MqD9pN&xlqv92{tKIvn!*M~-&ixH4>a*+bVx zhF#rsGdXsWoQ~LL{&HDf{RzI#W{5Ie8b28BM%*XLp(wr+mmd>NZiB|1i8=TO5;M$$ znm0&1Wghid8YIS;(;h>4meYBQM0z{ArajKeQzSAxVO9?~M`Dscgp%eI_618k9v2{| zNR)OXqTv6G$Cozd-~jS1Q2`ss2F;&{UiiWM=$v1}R?Kmivn0P0&G3YT+d)Pp(OeC< zw~Tp1oRAkVx=zLqSb2fLK$g8W5I-g*Cj4ulb4DM)=xuBsX};O^r=+!M{!TZBkdxkQjs3fs0wHcY8`D}Inf@` It5Q+>9~=9&mjD0& diff --git a/internal/ebpf/prociter_bpfel.o b/internal/ebpf/prociter_bpfel.o index d73f2d97c725078e11e7a9af33c2c348194eb255..57fbbc9e2769af6bfbbfaa904fc396153bb885d7 100644 GIT binary patch delta 1205 zcmZ9KT}YE*6vxl=PWP?b%(;cdW{@9|bJN0M_z?wK^r2VbMdi?uh7d)?N@Fv$yle6x z*hQq(#ULYPMqcGEq81S-1Q~QuH_}xJL3k0=|Ge)L3?JU}{Lk;4^FH5O;dCTjE4ml! zdbJ&4;x zk!E|EePQyMifu$iU2@GFIH{qm1J;IVs-Y>^apmXWpMk#*O3)Z=Kim%J5UdP-3Yvh0 z;OAt-QX_83UQ12#mkCqgb0fY5-`Et#U5rjPGB2aE# z4iH&O(N%7%Y0^_fdyE?S*RtDl*YHaZAcyhjo~*Yvh-rD&>MMC*FK)D(D4k0)?WC3JfHCke(C5q=zti>3i<|1s8wp{J!UN&iS3+xpy}k-;QOg zMa%P=bK0J2;x%5imY!C7;^iqA7hsf0_zZ67{SE9a?4cD-rft`kb2)YF8fnN(H=y@3T=h};~I`|ox za94>da>!kk-q6t&zYyY^@a=(#e!*`*EDHOr;+^pSs(2rKK8Ths2)VJvIfmuq0Fftv z&D>FQ#7Yr`?HcLx>@&x0KhTIA`q2&9>Zz}|WuFZ4fhOLtcEj%3)o$jV{2fn+$ba>& z32{_D^VNxF`OTLQ4YJICN;Jwre{WN)Ek<7N(Bby0$-!tMI&gVt(DLi9^^Y8*SX=ym z{(>Vf|M>Ii2@DgMz^u4w6LuFI%-HXQ`4PBa(tc<2@ZT}d2AB+1RuZ@IaP25tW649P&~?vlOZTKn1U892nsv65F7>bz^|dk z98tC6&rt7EJb>bLW?a@{ahm7{9AgGwk17b@-!Gdd>T+-qcvx`)c@5LuR6Gtgmx!{8 zr@;kpiKZ1l0T(Y5%{bTx&N1VDdV_-C3elV@Smy?-L<@=!;$tXT!)Nbc1H7#GBK_VvwnhK| diff --git a/internal/ebpf/spike/c/vmlinux.h b/internal/ebpf/spike/c/vmlinux.h index dc774a68b..da94af6b5 100644 --- a/internal/ebpf/spike/c/vmlinux.h +++ b/internal/ebpf/spike/c/vmlinux.h @@ -124,6 +124,7 @@ struct cgroup_namespace { struct task_struct { int pid; int tgid; + int exit_code; struct task_struct *real_parent; struct task_struct *group_leader; const struct cred *real_cred; From 8569d59be8145705a05aec940b0ea6bc33718776 Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:48:35 +0200 Subject: [PATCH 4/8] fix(ebpf): Preserve ring buffer order during startup replay Drain the pending queue in rounds and only flip to live dispatch once the queue is empty, so replayed and live events cannot interleave out of ring buffer order. Size the output channel to the pending capacity so a full replay cannot block before the aggregator starts consuming. Assign the sequence number at dispatch and increment it only for events that pass exclusion and filtering, mirroring the Windows consumer. --- internal/ebpf/consumer_test.go | 6 +++--- internal/ebpf/events.go | 6 ++++-- internal/ebpf/events_test.go | 9 +++++---- internal/ebpf/source.go | 37 +++++++++++++++++++++++----------- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/internal/ebpf/consumer_test.go b/internal/ebpf/consumer_test.go index 3977c626f..edc6d1646 100644 --- a/internal/ebpf/consumer_test.go +++ b/internal/ebpf/consumer_test.go @@ -33,7 +33,7 @@ import ( func TestApplyProcessStateExecveAndPIDReuse(t *testing.T) { snap := ps.NewSnapshotter() - first := sampleExecve().toEvent(1) + first := sampleExecve().toEvent() applyProcessState(snap, first) ok, got := snap.Find(4242) require.True(t, ok) @@ -41,7 +41,7 @@ func TestApplyProcessStateExecveAndPIDReuse(t *testing.T) { assert.Equal(t, uint64(123456789), got.StartBootTime) assert.Equal(t, got.UUID(), first.PS.UUID()) - reuse := sampleExecve().toEvent(2) + reuse := sampleExecve().toEvent() reuse.Params.Append(params.StartBootTime, params.Uint64, uint64(999)) reuse.Params.Append(params.Exe, params.Path, "/bin/sh") reuse.Params.Append(params.ProcessName, params.String, "sh") @@ -55,7 +55,7 @@ func TestApplyProcessStateExecveAndPIDReuse(t *testing.T) { func TestApplyProcessStateExitRemoves(t *testing.T) { snap := ps.NewSnapshotter() - evt := sampleExecve().toEvent(1) + evt := sampleExecve().toEvent() applyProcessState(snap, evt) exit := &event.Event{ Type: event.Exit, diff --git a/internal/ebpf/events.go b/internal/ebpf/events.go index c4c237857..acebb3960 100644 --- a/internal/ebpf/events.go +++ b/internal/ebpf/events.go @@ -92,11 +92,13 @@ func (r rawEvent) processID() uint64 { return uint64(r.TGID) } -func (r rawEvent) toEvent(seq uint64) *event.Event { +// toEvent converts the raw eBPF record into an event. The sequence number is +// assigned at dispatch time, mirroring the Windows consumer which increments +// the sequencer only for events that pass exclusion and filtering. +func (r rawEvent) toEvent() *event.Event { typ := r.eventType() info := event.TypeToEventInfo(typ) evt := &event.Event{ - Seq: seq, PID: r.processID(), Tid: uint64(r.TID), Type: typ, diff --git a/internal/ebpf/events_test.go b/internal/ebpf/events_test.go index 8c089337e..982f4ceeb 100644 --- a/internal/ebpf/events_test.go +++ b/internal/ebpf/events_test.go @@ -48,7 +48,8 @@ func TestDecodeRawEvent(t *testing.T) { } func TestRawEventToEventGolden(t *testing.T) { - evt := sampleExecve().toEvent(7) + evt := sampleExecve().toEvent() + evt.Seq = 7 got := goldenEvent(evt) path := filepath.Join("testdata", "execve.json") if os.Getenv("UPDATE_GOLDEN") == "1" { @@ -62,12 +63,12 @@ func TestRawEventToEventGolden(t *testing.T) { func TestCloneThreadSemantics(t *testing.T) { raw := sampleClone(0x00010000) - evt := raw.toEvent(1) + evt := raw.toEvent() assert.True(t, evt.IsCreateThread()) assert.False(t, evt.IsCreateProcess()) raw = sampleClone(0) - evt = raw.toEvent(2) + evt = raw.toEvent() assert.True(t, evt.IsCreateProcess()) assert.False(t, evt.IsCreateThread()) } @@ -143,7 +144,7 @@ func prettyJSON(t *testing.T, v any) []byte { } func TestSucceededHelper(t *testing.T) { - evt := sampleExecve().toEvent(1) + evt := sampleExecve().toEvent() assert.True(t, succeeded(evt)) evt.Params.Append(params.Retval, params.Int64, int64(-2)) assert.False(t, succeeded(evt)) diff --git a/internal/ebpf/source.go b/internal/ebpf/source.go index fb70ca48a..58b7759ca 100644 --- a/internal/ebpf/source.go +++ b/internal/ebpf/source.go @@ -68,7 +68,9 @@ type EventSource struct { // NewEventSource constructs the Linux eBPF event source. func NewEventSource(psnap ps.Snapshotter, cfg *config.Config, _ *config.RulesCompileResult) source.EventSource { - evts := make(chan *event.Event, 500) + // Startup replay pushes queued events before the aggregator starts + // consuming, so the channel must be able to absorb a full pending queue. + evts := make(chan *event.Event, defaultPendingCap) return &EventSource{ psnap: psnap, config: cfg, @@ -210,8 +212,7 @@ func (e *EventSource) handleRecord(raw []byte) { return } - e.sequencer.Increment() - evt := rec.toEvent(e.sequencer.Get()) + evt := rec.toEvent() if rec.Kind == eventKindExecve || rec.Kind == eventKindClone { enrichEvent(evt) } @@ -249,20 +250,31 @@ func (e *EventSource) enqueuePending(evt *event.Event) bool { return true } +// finishBaseline replays queued hot events in ring-buffer order and then +// switches to live dispatch. New records keep landing in the pending queue +// while a replay round runs, so loop until the queue drains before flipping +// live. This guarantees replayed and live events preserve global ring-buffer +// order. func (e *EventSource) finishBaseline() { - e.pendingMu.Lock() - pending := e.pending - e.pending = nil - e.live.Store(true) - e.pendingMu.Unlock() - - for _, evt := range pending { - e.dispatch(evt) - replayApplied.Add(1) + for { + e.pendingMu.Lock() + pending := e.pending + e.pending = nil + if len(pending) == 0 { + e.live.Store(true) + e.pendingMu.Unlock() + return + } + e.pendingMu.Unlock() + for _, evt := range pending { + e.dispatch(evt) + replayApplied.Add(1) + } } } func (e *EventSource) dispatch(evt *event.Event) { + evt.Seq = e.sequencer.Get() applyProcessState(e.psnap, evt) eventsProcessed.Add(1) @@ -283,6 +295,7 @@ func (e *EventSource) dispatch(evt *event.Event) { if e.filter != nil && !e.filter.Eval(evt) { return } + e.sequencer.Increment() if err := e.q.Push(evt); err != nil { select { case e.errs <- err: From 856660d85b1dbf42419c09df7c654edec68f2f6b Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:48:36 +0200 Subject: [PATCH 5/8] test(event): Cover the Linux sequencer --- pkg/event/sequencer_linux_test.go | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 pkg/event/sequencer_linux_test.go diff --git a/pkg/event/sequencer_linux_test.go b/pkg/event/sequencer_linux_test.go new file mode 100644 index 000000000..1fec3500e --- /dev/null +++ b/pkg/event/sequencer_linux_test.go @@ -0,0 +1,43 @@ +//go:build linux + +/* + * Copyright 2026 by Mostafa Moradian + * https://www.fibratus.io + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package event + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSequencer(t *testing.T) { + sequencer := NewSequencer() + defer sequencer.Close() + + assert.Equal(t, uint64(0), sequencer.Get()) + for i := 0; i < 10; i++ { + sequencer.Increment() + } + assert.Equal(t, uint64(10), sequencer.Get()) + + require.NoError(t, sequencer.Reset()) + assert.Equal(t, uint64(0), sequencer.Get()) + require.NoError(t, sequencer.Shutdown()) +} From 51dcb2a3597e2b6e026b4d569acdbb53340b33cb Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:48:36 +0200 Subject: [PATCH 6/8] refactor(bootstrap): Align Linux bootstrap with platform conventions Guard single instance with an abstract UNIX domain socket that the kernel releases automatically on process termination, replacing the flock file in a world-writable directory. Drop the no-op debug privilege option since the concept does not exist on Linux. Import internal/ebpf under the libebpf alias and give the Windows signal handler its platform suffix now that a Linux counterpart exists. --- internal/bootstrap/bootstrap_linux.go | 59 ++++++++----------- internal/bootstrap/source_linux.go | 4 +- .../{signals.go => signals_windows.go} | 0 3 files changed, 27 insertions(+), 36 deletions(-) rename pkg/util/signals/{signals.go => signals_windows.go} (100%) diff --git a/internal/bootstrap/bootstrap_linux.go b/internal/bootstrap/bootstrap_linux.go index 26ca98856..7a25fd788 100644 --- a/internal/bootstrap/bootstrap_linux.go +++ b/internal/bootstrap/bootstrap_linux.go @@ -22,10 +22,11 @@ package bootstrap import ( + "context" "errors" "fmt" + "net" "os" - "syscall" "github.com/rabbitstack/fibratus/pkg/aggregator" "github.com/rabbitstack/fibratus/pkg/alertsender" @@ -38,10 +39,12 @@ import ( "github.com/rabbitstack/fibratus/pkg/util/signals" "github.com/rabbitstack/fibratus/pkg/util/version" log "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" ) -const instanceLockPath = "/tmp/fibratus.lock" +// instanceSocket is an abstract UNIX domain socket name. Binding it acts as +// a kernel-wide mutex that is released automatically when the process +// terminates, so no filesystem cleanup is required. +const instanceSocket = "@fibratus" // ErrAlreadyRunning signals a Fibratus process is already running. var ErrAlreadyRunning = errors.New("an instance of Fibratus process is already running in the system") @@ -50,13 +53,13 @@ var ErrAlreadyRunning = errors.New("an instance of Fibratus process is already r // for event acquisition, rule engine initialization, // and event routing to the output sinks. type App struct { - config *config.Config - evs *EventSourceControl - engine *rules.Engine - psnap ps.Snapshotter - agg *aggregator.BufferedAggregator - signals chan struct{} - lock *os.File + config *config.Config + evs *EventSourceControl + engine *rules.Engine + psnap ps.Snapshotter + agg *aggregator.BufferedAggregator + signals chan struct{} + instance net.Listener } // Option enables changing the behaviour of the bootstrap application. @@ -73,11 +76,6 @@ func WithSignals() Option { } } -// WithDebugPrivilege is a no-op on Linux. -func WithDebugPrivilege() Option { - return func(*opts) {} -} - // NewApp constructs a new bootstrap application with the specified configuration // and a list of options. func NewApp(cfg *config.Config, options ...Option) (*App, error) { @@ -131,14 +129,9 @@ func (f *App) Run(args []string) error { return fmt.Errorf("filaments are not supported on Linux") } - lock, err := acquireInstanceLock() - if err != nil { - if errors.Is(err, syscall.EWOULDBLOCK) || errors.Is(err, syscall.EAGAIN) { - return ErrAlreadyRunning - } - return fmt.Errorf("acquiring instance lock: %w", err) + if !f.isSingleInstance() { + return ErrAlreadyRunning } - f.lock = lock log.Infof("bootstrapping with pid %d. Version: %s", os.Getpid(), version.Get()) log.Infof("configuration options: %s", cfg.Print()) @@ -203,22 +196,20 @@ func (f *App) Shutdown() error { if err := alertsender.ShutdownAll(); err != nil { errs = append(errs, err) } - if f.lock != nil { - _ = unix.Flock(int(f.lock.Fd()), unix.LOCK_UN) - _ = f.lock.Close() - _ = os.Remove(instanceLockPath) + if f.instance != nil { + _ = f.instance.Close() } return multierror.Wrap(errs...) } -func acquireInstanceLock() (*os.File, error) { - f, err := os.OpenFile(instanceLockPath, os.O_CREATE|os.O_RDWR, 0o600) +// isSingleInstance checks if there is already an instance of Fibratus +// running in the system. +func (f *App) isSingleInstance() bool { + var lc net.ListenConfig + l, err := lc.Listen(context.Background(), "unix", instanceSocket) if err != nil { - return nil, err - } - if err := unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil { - _ = f.Close() - return nil, err + return false } - return f, nil + f.instance = l + return true } diff --git a/internal/bootstrap/source_linux.go b/internal/bootstrap/source_linux.go index 8096eba53..c341c9b55 100644 --- a/internal/bootstrap/source_linux.go +++ b/internal/bootstrap/source_linux.go @@ -22,7 +22,7 @@ package bootstrap import ( - "github.com/rabbitstack/fibratus/internal/ebpf" + libebpf "github.com/rabbitstack/fibratus/internal/ebpf" "github.com/rabbitstack/fibratus/pkg/config" "github.com/rabbitstack/fibratus/pkg/event" "github.com/rabbitstack/fibratus/pkg/filter" @@ -40,7 +40,7 @@ func NewEventSourceControl( cfg *config.Config, compiler *config.RulesCompileResult, ) *EventSourceControl { - return &EventSourceControl{evs: ebpf.NewEventSource(psnap, cfg, compiler)} + return &EventSourceControl{evs: libebpf.NewEventSource(psnap, cfg, compiler)} } func (s *EventSourceControl) Open(cfg *config.Config) error { diff --git a/pkg/util/signals/signals.go b/pkg/util/signals/signals_windows.go similarity index 100% rename from pkg/util/signals/signals.go rename to pkg/util/signals/signals_windows.go From aa5ffb3d1b414554319358ed28f93523ff0f4e9e Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 13:48:36 +0200 Subject: [PATCH 7/8] ci: Validate PR titles targeting linux-port --- .github/workflows/linux-port.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/linux-port.yml b/.github/workflows/linux-port.yml index 1c4c950c6..31c68cf90 100644 --- a/.github/workflows/linux-port.yml +++ b/.github/workflows/linux-port.yml @@ -17,6 +17,11 @@ jobs: ebpf: runs-on: ubuntu-latest steps: + - name: Validate PR title + if: github.event_name == 'pull_request' + uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Checkout uses: actions/checkout@v4 - name: Install Go From 4c2ae548b326dec1164e8fec67fda045a64933b3 Mon Sep 17 00:00:00 2001 From: Mostafa Moradian Date: Mon, 14 Sep 2026 19:15:22 +0200 Subject: [PATCH 8/8] refactor(ebpf): Simplify the ring buffer record model Drop the kind field: it always mirrored the type, which alone designates the event topology, with zero reserved for iter/task snapshot records that map to the unknown type and can never leak as live events. Removing it also removes the explicit alignment padding. Rename the record struct to syscall_event, generalize clone_flags to a per-type flags field ahead of wider telemetry, and rename attachHotPath to attachPrograms with a comment contrasting the continuous live-capture programs against the one-shot snapshot iterator. --- internal/ebpf/c/clone.bpf.c | 10 ++++------ internal/ebpf/c/common/events.h | 27 +++++++++++-------------- internal/ebpf/c/execve.bpf.c | 3 +-- internal/ebpf/c/exit.bpf.c | 3 +-- internal/ebpf/c/proc_iter.bpf.c | 5 ++--- internal/ebpf/clone_bpfeb.o | Bin 30072 -> 30208 bytes internal/ebpf/clone_bpfel.o | Bin 30072 -> 29824 bytes internal/ebpf/events.go | 34 +++++++++++++++----------------- internal/ebpf/events_test.go | 5 +---- internal/ebpf/execve_bpfeb.o | Bin 16376 -> 16456 bytes internal/ebpf/execve_bpfel.o | Bin 16376 -> 16264 bytes internal/ebpf/exit_bpfeb.o | Bin 6648 -> 6632 bytes internal/ebpf/exit_bpfel.o | Bin 6648 -> 6632 bytes internal/ebpf/loader.go | 7 ++++++- internal/ebpf/prociter_bpfeb.o | Bin 6288 -> 6280 bytes internal/ebpf/prociter_bpfel.o | Bin 6288 -> 6280 bytes internal/ebpf/source.go | 6 +++--- internal/ebpf/source_test.go | 5 ++--- 18 files changed, 47 insertions(+), 58 deletions(-) diff --git a/internal/ebpf/c/clone.bpf.c b/internal/ebpf/c/clone.bpf.c index cc2deacea..3c452ff34 100644 --- a/internal/ebpf/c/clone.bpf.c +++ b/internal/ebpf/c/clone.bpf.c @@ -21,7 +21,7 @@ static __always_inline int handle_clone_exit(long ret, u32 syscall_id) { u64 key = bpf_get_current_pid_tgid(); struct scratch_value *val; - struct fibratus_event *e; + struct syscall_event *e; struct task_struct *task; struct task_struct *parent; const struct cred *cred; @@ -36,7 +36,6 @@ static __always_inline int handle_clone_exit(long ret, u32 syscall_id) if (!e) return 0; - e->kind = EVT_KIND_CLONE; e->type = EVT_TYPE_CLONE; e->syscall_id = syscall_id; e->retval = ret; @@ -55,7 +54,7 @@ static __always_inline int handle_clone_exit(long ret, u32 syscall_id) val = bpf_map_lookup_elem(&scratch, &key); if (val) { - e->clone_flags = val->arg0; + e->flags = val->arg0; if (!e->syscall_id) e->syscall_id = (u32)val->arg1; } @@ -119,7 +118,7 @@ int handle_sys_exit_vfork(struct trace_event_raw_sys_exit *ctx) SEC("tp_btf/sched_process_fork") int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_struct *child) { - struct fibratus_event *e; + struct syscall_event *e; struct scratch_value *val; const struct cred *cred; struct task_struct *real_parent; @@ -132,7 +131,6 @@ int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_ if (!e) return 0; - e->kind = EVT_KIND_CLONE; e->type = EVT_TYPE_CLONE; e->retval = child->tgid; e->pid = child->tgid; @@ -156,7 +154,7 @@ int BPF_PROG(handle_sched_process_fork, struct task_struct *parent, struct task_ key = ((u64)parent->tgid << 32) | (u32)parent->pid; val = bpf_map_lookup_elem(&scratch, &key); if (val) { - e->clone_flags = val->arg0; + e->flags = val->arg0; e->syscall_id = (u32)val->arg1; bpf_map_delete_elem(&scratch, &key); } diff --git a/internal/ebpf/c/common/events.h b/internal/ebpf/c/common/events.h index 56400585a..4328072f6 100644 --- a/internal/ebpf/c/common/events.h +++ b/internal/ebpf/c/common/events.h @@ -29,21 +29,16 @@ struct trace_event_raw_sys_exit { long ret; }; -enum fibratus_event_kind { - EVT_KIND_SNAPSHOT = 0, - EVT_KIND_EXECVE = 1, - EVT_KIND_EXIT = 2, - EVT_KIND_CLONE = 3, -}; - -/* Stable semantic type IDs. Keep in sync with pkg/event.Type on Linux. */ -#define EVT_TYPE_UNKNOWN 0 +/* Stable semantic type IDs. Keep in sync with pkg/event.Type on Linux. + * Zero is reserved for iter/task baseline snapshot records, which feed + * the process state and are never dispatched as events. + */ +#define EVT_TYPE_SNAPSHOT 0 #define EVT_TYPE_EXECVE 1 #define EVT_TYPE_EXIT 2 #define EVT_TYPE_CLONE 3 -struct fibratus_event { - u32 kind; +struct syscall_event { u32 type; u32 pid; u32 tid; @@ -52,9 +47,9 @@ struct fibratus_event { u32 uid; u32 gid; u32 syscall_id; - u32 pad; s64 retval; - u64 clone_flags; + /* Interpreted per event type; clone stores the raw clone flags. */ + u64 flags; u64 start_boottime; u64 timestamp_ns; u8 comm[TASK_COMM_LEN]; @@ -97,9 +92,9 @@ static __always_inline void account_drop(void) __sync_fetch_and_add(count, 1); } -static __always_inline struct fibratus_event *reserve_event(void) +static __always_inline struct syscall_event *reserve_event(void) { - struct fibratus_event *e; + struct syscall_event *e; e = bpf_ringbuf_reserve(&events, sizeof(*e), 0); if (!e) { @@ -111,7 +106,7 @@ static __always_inline struct fibratus_event *reserve_event(void) return e; } -static __always_inline void fill_current_ids(struct fibratus_event *e) +static __always_inline void fill_current_ids(struct syscall_event *e) { u64 id = bpf_get_current_pid_tgid(); u64 uidgid = bpf_get_current_uid_gid(); diff --git a/internal/ebpf/c/execve.bpf.c b/internal/ebpf/c/execve.bpf.c index 04ee29ba2..32211a588 100644 --- a/internal/ebpf/c/execve.bpf.c +++ b/internal/ebpf/c/execve.bpf.c @@ -22,7 +22,7 @@ static __always_inline int handle_exec_exit(long ret, u32 syscall_id) { u64 key = bpf_get_current_pid_tgid(); struct scratch_value *val; - struct fibratus_event *e; + struct syscall_event *e; struct task_struct *task; struct task_struct *parent; const struct cred *cred; @@ -31,7 +31,6 @@ static __always_inline int handle_exec_exit(long ret, u32 syscall_id) if (!e) return 0; - e->kind = EVT_KIND_EXECVE; e->type = EVT_TYPE_EXECVE; e->syscall_id = syscall_id; e->retval = ret; diff --git a/internal/ebpf/c/exit.bpf.c b/internal/ebpf/c/exit.bpf.c index 2228444bd..f5569f792 100644 --- a/internal/ebpf/c/exit.bpf.c +++ b/internal/ebpf/c/exit.bpf.c @@ -14,7 +14,7 @@ char LICENSE[] SEC("license") = "Dual MIT/GPL"; SEC("tp_btf/sched_process_exit") int BPF_PROG(handle_sched_process_exit, struct task_struct *task) { - struct fibratus_event *e; + struct syscall_event *e; struct task_struct *parent; const struct cred *cred; @@ -31,7 +31,6 @@ int BPF_PROG(handle_sched_process_exit, struct task_struct *task) if (!e) return 0; - e->kind = EVT_KIND_EXIT; e->type = EVT_TYPE_EXIT; /* Raw wait status: (code << 8) | termination signal. */ e->retval = task->exit_code; diff --git a/internal/ebpf/c/proc_iter.bpf.c b/internal/ebpf/c/proc_iter.bpf.c index eb41eadaa..628a190cf 100644 --- a/internal/ebpf/c/proc_iter.bpf.c +++ b/internal/ebpf/c/proc_iter.bpf.c @@ -20,7 +20,7 @@ int dump_task(struct bpf_iter__task *ctx) struct task_struct *task; struct task_struct *parent; const struct cred *cred; - struct fibratus_event *e; + struct syscall_event *e; pid_t pid; pid_t tgid; @@ -37,8 +37,7 @@ int dump_task(struct bpf_iter__task *ctx) if (!e) return 0; - e->kind = EVT_KIND_SNAPSHOT; - e->type = EVT_TYPE_UNKNOWN; + e->type = EVT_TYPE_SNAPSHOT; e->pid = tgid; e->tid = pid; e->tgid = tgid; diff --git a/internal/ebpf/clone_bpfeb.o b/internal/ebpf/clone_bpfeb.o index e03647d4529c7b5e2a10c51f75e09c3ad1479f8b..40ba3e2869a1db31af38fa3c0306908130a6cd76 100644 GIT binary patch literal 30208 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 literal 30072 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 a{tk&Ds|d<7{f6o94s{>j!FpSN^uGXIp?WO< diff --git a/internal/ebpf/clone_bpfel.o b/internal/ebpf/clone_bpfel.o index b0d4f7accca9d31e7e7bedd61e56010b9fd70553..9985e02c15cf0ad258cf5339cc58e33f6797ac08 100644 GIT binary patch literal 29824 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 delta 4125 zcmeH~O=w(I6vxkf@6A_QlZpLGC9$t++X>_gsm)ZAOpr<*(zw;G(n0VxU<}nMwy*=qw|&kVO^&E6gI`fJJ8EVisBWKkuH2O?2l%7r8L^ z+}}CpzI)F7dT&-2RKv^ltN7i4W0p$kr1k#s$Aoe}$~CE$a7xhy-R8@Ex^)fv;cVMObr><(^2zx}MaZ1gVJe*$Ef^|NW}V0oD(0Qp>Y!c@+*BRTAJ4&t=aGQLsUchtt_$adYd(8XS`tOoS41f%ydk_Oye7OLyed2|ToKL+mxVLJMd7q?fu)Ct zQ=-VT1Sf@;gvW(*!lS~A!g1jR;Q`@!;h1n%*cHw|cJY86qDZp@M}55yW0KjvU9bqg|4mZ! zEzstKE2OTme_r?rsq1{p7lnT(b&sP%6x`oAK%Ocegd)1FP~{iF9VTq&O`==Q%<%!oa@i^mOnLA=XXux55f92T)$7}Q?a#nAIU z3$mRbSP=e^=pHh*S9kH6@GnGVe!e6SIzjXhOX@^@yGPrK4%5MBEP)e$j=6(m5V}BA zVNQv?$?4~q`(tJ~;E+f3I1J7S-yvE&VE-ivx2AEE$HL5N*5+E{dHQ zYwnbV>qJkn9>I#?@EM{_tZ}d^JVo@3xnUTt$_0nNBO(`>aN$V9MN;u@0HWPy{oBdg z-Ep`__!Dc_kxuS9(#gFe?d%=ty(9f!9O)#^D1XLU9>05W!B`c@el&4Aax~J!RE7;JLo+C#&Dkk zjIP6qJFr<)39Y8Jtli#AFY%SvENTX==2YZ#vG>T)Ow)$@?uWmu!LqLQ9#Li8=$*tj z&3NB5>O$Y-(RujbT}R6F3$8X zhdTJ7M3zz6dMiG0XbUaAl*Q7*?frV{?733q>?upl>)Kn7R8AL$#t>C~Xi(*KY&d~G zxS8P*tFNjH!-+@@eNaMh7(Qe5)pcxS#A>hW%t%6!E}%~88tMTZOC%yI#=B)oXA^^w TtPts9Vo+suEs?-Lu~GL==>$LE diff --git a/internal/ebpf/execve_bpfel.o b/internal/ebpf/execve_bpfel.o index 26c83806da07f73403f18d135932ae48b82a1a1e..cc808bde2073750857c398eb7310f1aa9ab1372b 100644 GIT binary patch delta 3999 zcmeH~Ur5_m6vxl~8Ka5GOq^&OI%CH~XZ*tuYi<5mN>QlW(yb0RMrYB2ds-Mp^TV+? z=(ao*jNBKgg=Vx5f-9tCqj@OQJ#4tqLL7Y%=D-JGj{U($2ZQZou;32;p zKHq!pz31NF@7!NaA9mdAcy_ZfHDz0BK#-IC+*zPMLeCS7?g;Yu3QvPPmCDl4; z3G<%@Ek8BeZ280ZDc$C6jm*zhTk2dD5ywrKy5AX90e!yqWXhgzTp3muQ`s|3HP|{BV-U?Zj9T7Q)-u4u z!UtBr`G!H^ec?XgJ>d@F9pRwODYYd+Kmv-wKH&{vI}uZ@3)_j9svvA9Vyc|5ortMc zh3!O4l@;Exe9BbIBG`$TDkE$sVyd*TortLxgx4)^s-$p1I3b)9o)KOZo)pfqPm!r6 zL|C@Gsp7&J;ZfnV@UZZL@St!~xKB7C+#x(892A~}Zy_WQ5Mjddrt%5L^@@KyA8>h& zA7elEY;yzQuT zxQIseH*M2?hoWk%RFlE?wqDT<&Q_8R2z~pHY+*ehfAeigLn#FlUhtvg7{G z;>D1vzk{?&ggUSf%xMoHz=P2P!ee0Hb)pX8_rUHnQJ?hx1YGk#(Xj9uxOQF9sPGf8 z>ye_k@XroB1+^R40TKSv7~f-kWE;TQ2Mz*8VPC}YA-^8!*F;6^}D zgu@OU!s9%^1ES$F9xY)v+9!llh_ButN(!%mosWnXgztlEibQFfQ^Z0Qgp33{20#CV zXt{*zz**sav{!5qtzxCTf&rAEv4eXm{0{RTc0ibG%|PufLHEe*;(j@BXFq;8z*_^c zaHd~>-+rsJU(qwD@~298>?qnQ;j!ZqJ}%+o68=w07`5j^rShv4l})Sgz3?vQ2)<$a zB8+e=Y!G$N0oR0+!uN#vZ8?DUR-7kn)q1I`@xsxtw)*c!{j+PaiO-@^!%G@gNlE>A zsIe)IldaTlOKVndh8k5?{}P&0JNj~WV-v5T)ZVZnT@=?#Xw8<*vL>xr{dMrHNtu&F_=SU63ou6yZiIr0$!wSta$Rj_usB)xrzgm zjU`U=S}E(hgJ)Z|OB6nde=B9}J#{vpI2AM$Zp7(_YFJ+gk8rj(!UOnsycv!u(*8(H z$HVn{Iuddh-53JKA&~)HguELGsdep-#*Ct0zaEXMpk9iiN2FSBg*|#V8ghNuX7>u~ drr1e$kRK4(j!VBC8^8ohv6xZl(m%x7{{fM(8+iZ# delta 4086 zcmeI#UuauZ90%~)4zbS}izdx=hHJXas3< zshAe0245OEPUeHGV|(c69x_r{9wrMuRd7(H4+T3YQn1L73O*DR^!vN_7aHoTpbtIt z_T=+B=bn4cx#ygw>C|>A(`f_-?i^@0)R=zW7;XQKINeo5_@N(bEmApsYkipdJfd>< zjC1O#A=@8yT6t{3Z{1Q3S$6^yzR_SB~ksqI86Lzz)36~X*Iem6He*1C7 zoXwW+k6Rg4ps5jk#qov;=sz4EE3f{bd{+7NKId`OtY38=PVrqtqsp*yQ%SoG{-V(W zdeacGa#LBIgGa>MR=uf)A@PznR&J_qd{d{sOm&WMM^X>mY2E$$O1#hv1W*e{MFZ{aESNg!(Frm7W> zF4^tvKP?{^p3sHfw$nzE2=FC)WbfQ2XzD$sy-v~i%-F|cv?Vw5%#2shUESa z;mT!25%B_Cvw{JNS77%#1}Og7fmKkmrYOD>AP<}n|H(IOC`yWJ%Hd5#)8Y=e{*j_| ziQRBUTpaP7IE4Df7G?zI0Vc{*coCSdbqQdmth6bvb`mw*B--9(eu3NomwQp(DK6gM zCq9YtN-X!GT^t+mz$XE=2NeP(<{O5@qufA&Xl$3GCH9~^Ax@#b3*Ub-;sw~bOmx+D zitZv%xk5B271$NCNnnhO2uc{k1zvbesu=+ z;-`pLk>7q}d}9`Y{ehiL${r5rPdct~^fB!Epk3K|3^&sg*{RKLjqGjdy)C`BrT>#H zjlEcl{kdVhf&&I03OKTRk-Hg4E^JWtadAX^S)38y5wF7isNah7g_B1V-t^|G`(ombmQyF zFiP{gp0XgNdHqiJ3x=ygf75-Av-Y0TbxC<9SMlOn(lb5be@1iP_x$Ck-jvFmmFb7i zUNB1Gg- z#IHyOhlm|I2&P2T!6Ab~28Rk20wQD)>ZXepbnyS~UPADO_xSz)-}k=v9=AWYH&>0R zty=t;Qy_eKn*v^*;Yf6Il5(|`DV45Ot~i%jc^l$L>WlI+jSyYh{+m_f31)GPg1%#% zxZpw=lq}?>w9P6JILNvGBzTN@&89f#`+17%U1P>t64iSEYD$lp5f#$2X2d$eUf%L= zqC&WJ!#ow7xVu)m<(_vJt}bVnuca4S8>3H*$wvopHiy&@tNMo2CR#GEqX8cVySVcc`72Qc^^K_TAEGLs zPYt+CRcygn@&(f&+OWXsAwCGs2*=PX`?(=zzbHIU^o$!Q4KX*cDx4*190#xWxeS3j zYKZWdXp<*w3x6bf$q#mfzY{gFA!A4Q7t!mZ;O-EQf_pv(Q2YG^cu)8=QJe4o5WaYi z=mY)`V_yVjbZi27g@LC;9r$K&h}jPbZxijJVKb^{L+8p-s3%eFH!?nNuVD2`nD@y1 zNw_H7_jyn7&FkV}etNOV)t>>*N z71c#6=Jb5ui!TtpgFLowS`n3K?O2N=>YEPR4^%=o>@&*MT|22(bvT?<6`eus>N;Xd VcM+jGNwuysh&^3L4C}7r{sW-WreXj9 delta 1056 zcmZ9IPiPZS5XR^2rV_2#tW-CPfh4xrdXNMvbWtP<6;l)mR3sph#OYK`4meG2SZpr%(@mvzf@!cbIvKm@`9?=Tt^N1YZqYABHD@ML2m=1cez_S592u9odz)TUn=J-;8IbIT;B5H60PXo*i zc)|su)fl+i=L!VwXibC{7@jBmD7;0q#vg7Ae<#|&hKz5*zp}%+ zyeoW$XcO;EAP*FpenixM#pAE_!ZF>$D;w}ctUucXd4Ziu8u{{S6ry4@ZBGH z9DlE1^-h@g$oxsTB;1elo)9;0NxUxnT)4fDc}<+(>BZ0adkKEXdjN-c92|dc>>%_| zn04l=u9>6ytTm!sowrgBzw4hjxcCp@61riMZPz_F%_#$4UGHl?bR0 diff --git a/internal/ebpf/exit_bpfel.o b/internal/ebpf/exit_bpfel.o index 664710f4c78c17568e7309afd0844bde5915a75a..853c4d11a86c58c5a1cb2e49a9819fbda0f53a31 100644 GIT binary patch delta 1025 zcmZ9~L1+^}6b9fon>3_JF}ooq8!03KX;Y=N(1U>@iO|}dB!w!GqHS4)+)PBFrHAw) z77@uZMHD4?c@%dT5;99=A_Ar2fONhj+bhqPF#+U%1_#$QB+y?4a@O@4Es_-m#2LxBj$E6 z+?7_`Y|Z*k1tYhXs@KzJ)03BG=4LLJCR=M^Tg%)RG>_?Du>BHFRb~77+NLDD*r)@HHZox z*ZYttszlO9WULXDJnn_d$|GFIZs$Gjg)7Pvs7Id=@y2-r>=3-DJO}qTiB|r*PBA2! zD)9glv~{9w7uyt&c2Y>f4;tgf~Om@RBpZW&m zgwZdua={o8yV5oixbx49l*qN-8&f^vyEGDaMNU3WoDpf+N#w<%G|arz&7drsIk6*c ZGcO9VgC{DX@X;5)6VXLk`=|IY%d0;xu`z0+ zi5Sg^ux(MyYO!?OR`!bILSDsAU@p|#;dg4x=M%PG3?;>TZ9sPl&gYX#&4^ts!a;Gs z712#6gYIO?g~Ig-*AZ{u^=mh;L{CIdUN|>Baq9F~eX*yi#g>?65dImhTIkiY%rvL? zkmMWU*uF@qz*JLePOA5qUMao}))tu>imQ^>nEbFZ>l@f$gUM{;05}2ufiquWePyDg z;$7I_Goox8mwF*gD#3CQ>F8#rjl02fiif2CC6R1Q7LeQxE+`%Y2bPHLx3LdgRy+-V zc$uj3pGy>ku%d*g2+(l-kBT?J`U=sO;_qP38qqh!zrp)4o>m+8fVUL~@!a~>iFOnZ zf;}J5InK!o+;xu=85>02e*#7Z_A7pl1I8wi*~UI_RPieO+7?DEEdNL;i8B32`v+F8 zNyV?_i^h_3l>SeO3mtwrC+WY4>~hrUO_nr$$qqHF9Z(a z=ayo5P01CvsJi;aQ9aB_aYi5FZLy#m92GD02xsc=^zknKCD?ex8S&US&I!>pQexEf i3a^>unn;@|Zixl#S<%E^7Hp-sBGTBA3VU2Mt%1KvcBvZx diff --git a/internal/ebpf/loader.go b/internal/ebpf/loader.go index 98054c573..83cc1887a 100644 --- a/internal/ebpf/loader.go +++ b/internal/ebpf/loader.go @@ -123,7 +123,12 @@ func (l *loader) dropCount() uint64 { // syscallsGroup is the tracefs group hosting raw syscall tracepoints. const syscallsGroup = "syscalls" -func (l *loader) attachHotPath() error { +// attachPrograms attaches the live-capture programs: raw syscall tracepoints +// plus the scheduler tp_btf hooks. These feed the ring buffer continuously, +// in contrast to the one-shot iter/task snapshot program started by +// runTaskIterator. The caller starts the ring buffer reader first so no +// events are lost between attachment and consumption. +func (l *loader) attachPrograms() error { type tp struct { name string prog *ebpf.Program diff --git a/internal/ebpf/prociter_bpfeb.o b/internal/ebpf/prociter_bpfeb.o index 5340adcd9cb7c4b1b8e19f3d2785acedbb95adf1..772188f36c344e06c84596e50e257aa7ca709296 100644 GIT binary patch delta 939 zcmYk3F=$g!6o&tM6D3+DFVgg-R^&+s>(V5X8I(k{nu1`#qA4QHGZmuK8yu{PNe4AU zhlIOi^QjRX1S1jbAd*2uK_P>n;3^VCq?0%}c+PvLkUN}{??30>^PiVywvlb7Rk4+s zn!Ery?mdo_ZA3X=Ttlg~GOZR`jkA;Uth}-~LVZ&~#x~&Oi+{^1n`5q2u}gAHa_~;& z3KOtuBg6Toi;P*<2i>Bm!EI6t!oifJ1Ej2+Xp_3hMKrd{3uT0=FNtHQgXO3c?oreEQy zOMMDzWhY5(;nPr6?td!01m&^$4dEM5KCvdF!MyvZsSdf-w2=s^vxhiEmwr=z2Rx$z z+7bRjZ|@+U5&i*mnE88h{~y5T{#?irdZ)XOxG=&zuPl732E1fPbrJ3WuM@-#;fFw< zHquSu=fK+}aeIWvh<)Ktz{fZ-eE~KUA%g?H-wioB3w*YTH-t~Hz>fPOoB_Ua!}}5D z`at+9@SP10g~{+Q^C(`Z@yds2V({lvLs9rRWjflU!1XPz3$Ib86KqIz{0uPMi2FYX z^9E@kIwRZ{o)6iakL)$KtZ6kgzLi$K8CbR&n4z^_>%YBN;z?9JHr?c1B9ogqtjfll l$g7;`QY@NHifxmz^QvwNuIPxip8Wj7#&QrWeIs=#a2ZhC}KrvgB@N}oU0K-v1-PI zI~Z{=h~OI(t1Xy8q=QHtgbsoMJ2;i#AQ{BP#dDHV3Ets9@B7caC+AN&T}f9`%BxQ9 z9lHcN`3J02qp-PNUczj3VZXXiEuWn@!Oq(-E7UjDt=It^=dWTxd9Q!XskF;<#YguZ z)gj>luEhc?a5IWYuEjhuW?3I_JaIGPPMdc+u13tJjyo~-@{UZ(V|;bUx-$^ER=BZ{ z$R>`RKR0)B=JeF+qroG3qP~;v&eMMH0j;5)G5f;9P}j|$;dp6rH@P)&PeI-0TY>N_ z)UqaS2;YKQBi0nCJM{@vV}zL2`p0{S6ZGaM)sMgm&DTlcufU64#2lFWTPPu5BO}Id zpw1869$PecZJco5E*+P3~{>G51@-SAjM!+!3~E37LzokB|C2B$NC9?Im^Wsg>9M&O&Z>^7g;Qhv za{Jt+532{dWxj{^X$xoo4^>j6HX#a)rvyA0dq_m0 z1wAATNIV%!l?DVW5$r)E2N4B@90UbVf|ORoqj>P(d%I6r9KyW)-n=)nlWioH5{;lgxeX`r^!EjoNZKyk+#%5` z(b=eQ*D0@*C4|{r4$WLn{gaS7MlKk$+IrYl^HunLOA7 z8saD9@GGF@@({T#eoyQEcfhK6gZulyn)m>HsgV!B`ahqbH>N~S5@)#J6Y$1mAGt5S zOzXy1U{hQttM7W*f2_)vjsXbKnLIM$-vcGXN4Zaimt*rAMCdg4b zk_W#sd&;q%4;)sT`d(mSlsnzyh7Aw;`b%J+@yqBM!AAq?q3(olk@P|Hkjm+znNmsJ jp*f?sXm07)NJ`aok>(@1Lvv4W(cIQCE2Xl!XpR2?tiG2b delta 866 zcmZ9~J!n%=6bJDCd5NV>EP0VMc|swHOH&8iED{Gzz)D*ceS=>Gt$oBHejsE^Rd|S| zW=yys)x|_aGALG3Fd#?;kvIq)1S56nQp7eaxmrF+7i#6R`;L2=Hw5qx8)mO_7pA?3+YfDe{d=C$Igk0J zUOm6GTRbRjUch2;vgc?<-806*wx`8nMzS;Ntr4e)+A!i_%gZ@oSt&Z&3i{l^(Dkb~ zuOtp9j-5Mm`NZt0sn(-l&Pdh?3*7#Wk7yXcz%xQYJgj{~?F`4=5@A`gCw2A?p(&nY z8}|uq@h$CDg2_>@`h;VoO|bgBhn-;m;&(oB46Fbt@mG#RFMvt$7Dso8cgslP563_q z$cZf<$I zyrmcT0<^^k_)~~=fwevd*d6g{UH=ZO_c_4sim!3q_yKH-%YIJKwt5^o;_6F}^S>71 zJ@v4~N5!T8tS_zm?@C=?Qv0WVnTNiU_9v<9J4~=syrmxc%-XX(tMld|K1t1-pr(3n z#;B{lnQ;TY){gnyPcKzt;sF L(#q0;a;&kxaP^nu diff --git a/internal/ebpf/source.go b/internal/ebpf/source.go index 58b7759ca..e335732cd 100644 --- a/internal/ebpf/source.go +++ b/internal/ebpf/source.go @@ -118,7 +118,7 @@ func (e *EventSource) Open(cfg *config.Config) error { go e.consume() - if err := ldr.attachHotPath(); err != nil { + if err := ldr.attachPrograms(); err != nil { e.Close() return err } @@ -198,7 +198,7 @@ func (e *EventSource) handleRecord(raw []byte) { parseErrors.Add(1) return } - if rec.Kind == eventKindSnapshot { + if rec.Type == snapshotType { e.handleSnapshot(rec) return } @@ -213,7 +213,7 @@ func (e *EventSource) handleRecord(raw []byte) { } evt := rec.toEvent() - if rec.Kind == eventKindExecve || rec.Kind == eventKindClone { + if typ == event.Execve || typ == event.Clone { enrichEvent(evt) } diff --git a/internal/ebpf/source_test.go b/internal/ebpf/source_test.go index 439640af6..c57fbaf18 100644 --- a/internal/ebpf/source_test.go +++ b/internal/ebpf/source_test.go @@ -54,8 +54,7 @@ func TestStartupReplayAndLateSnapshot(t *testing.T) { assert.False(t, ok) snap := hot - snap.Kind = eventKindSnapshot - snap.Type = 0 + snap.Type = snapshotType snap.Comm = [16]byte{} copy(snap.Comm[:], "snap-a") es.handleSnapshot(snap) @@ -100,7 +99,7 @@ func TestPIDReuseKeepsDistinctUUIDs(t *testing.T) { copy(first.Comm[:], "one") es.handleRecord(encodeRaw(t, first)) - exit := rawEvent{Kind: eventKindExit, Type: uint32(event.Exit), PID: 8, TGID: 8, StartBootTime: 1} + exit := rawEvent{Type: uint32(event.Exit), PID: 8, TGID: 8, StartBootTime: 1} es.handleRecord(encodeRaw(t, exit)) second := first