From 3f9d9d562099bbed7accc7823bdc55d2e21e693a Mon Sep 17 00:00:00 2001 From: KR Ravindra <42912207+KR-Ravindra@users.noreply.github.com> Date: Tue, 8 Sep 2026 06:35:10 +0000 Subject: [PATCH] ebpftracer: report missing BPF tracing program types clearly On kernels built without CONFIG_BPF_EVENTS (e.g. NVIDIA JetPack 5 / L4T 5.10 with CONFIG_KPROBES=n), BPF_PROG_TYPE_TRACEPOINT and BPF_PROG_TYPE_KPROBE are compiled out and bpf(BPF_PROG_LOAD) fails with a bare EINVAL. The agent then exits with failed to load collection: program sched_process_exit: load program: invalid argument where the program name is whichever program happened to load first, and nothing points at the kernel configuration. Probe TracePoint and Kprobe support with cilium/ebpf/features before loading the collection and fail with an error naming the missing kernel option. Only a conclusive ebpf.ErrNotSupported is reported; any other probe result falls through to the real collection load so existing error paths are unchanged. The probe is behind a package-level function variable so the message mapping is unit-tested without root or a VM. Document the CONFIG_BPF_EVENTS requirement in the README. --- README.md | 1 + ebpftracer/tracer.go | 22 ++++++++++++++++ ebpftracer/tracer_progtype_test.go | 40 ++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 ebpftracer/tracer_progtype_test.go diff --git a/README.md b/README.md index bcc2609..7fa6206 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ The agent gathers metrics related to a node and the containers running on it, and it exposes them in the Prometheus format. It uses eBPF to track container related events such as TCP connects, so the minimum supported Linux kernel version is 5.1. +The kernel must also be built with `CONFIG_BPF_EVENTS=y` (kprobe and tracepoint BPF programs); some embedded and vendor kernels disable it. diff --git a/ebpftracer/tracer.go b/ebpftracer/tracer.go index de1e336..730ce3b 100644 --- a/ebpftracer/tracer.go +++ b/ebpftracer/tracer.go @@ -18,6 +18,7 @@ import ( "time" "github.com/cilium/ebpf" + "github.com/cilium/ebpf/features" "github.com/cilium/ebpf/link" "github.com/cilium/ebpf/perf" "github.com/coroot/coroot-node-agent/common" @@ -237,6 +238,24 @@ type Connection struct { _ [7]uint8 } +var haveProgramType = features.HaveProgramType + +// checkProgramTypes verifies that the kernel supports the BPF program types used by the collection +// (uprobes are BPF_PROG_TYPE_KPROBE programs). Kernels built without CONFIG_BPF_EVENTS reject +// them with a bare EINVAL, which is indistinguishable from other load failures without this probe. +// Only a conclusive "not supported" result is reported; any other probe failure is left +// for the actual collection load to surface. The returned error wraps ebpf.ErrNotSupported +// rather than the probe error, whose text names the kernel version that introduced the +// program type and would point away from the kernel configuration. +func checkProgramTypes() error { + for _, pt := range []ebpf.ProgramType{ebpf.TracePoint, ebpf.Kprobe} { + if err := haveProgramType(pt); errors.Is(err, ebpf.ErrNotSupported) { + return fmt.Errorf("kernel does not support BPF %s programs (CONFIG_BPF_EVENTS is not set?): %w", pt, ebpf.ErrNotSupported) + } + } + return nil +} + type perfMap struct { name string perCPUBufferSizePages int @@ -294,6 +313,9 @@ func (t *Tracer) ebpf(ch chan<- Event) error { return fmt.Errorf("failed to load collection spec: %w", err) } _ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY}) + if err = checkProgramTypes(); err != nil { + return err + } c, err := ebpf.NewCollectionWithOptions(collectionSpec, ebpf.CollectionOptions{ //Programs: ebpf.ProgramOptions{LogLevel: 2, LogSize: 20 * 1024 * 1024}, }) diff --git a/ebpftracer/tracer_progtype_test.go b/ebpftracer/tracer_progtype_test.go new file mode 100644 index 0000000..5418de7 --- /dev/null +++ b/ebpftracer/tracer_progtype_test.go @@ -0,0 +1,40 @@ +package ebpftracer + +import ( + "fmt" + "testing" + + "github.com/cilium/ebpf" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/sys/unix" +) + +func TestCheckProgramTypes(t *testing.T) { + orig := haveProgramType + defer func() { haveProgramType = orig }() + + t.Run("supported", func(t *testing.T) { + haveProgramType = func(ebpf.ProgramType) error { return nil } + assert.NoError(t, checkProgramTypes()) + }) + + t.Run("not supported", func(t *testing.T) { + // Shaped like the *ebpf.UnsupportedFeatureError returned by features.HaveProgramType. + haveProgramType = func(pt ebpf.ProgramType) error { + if pt == ebpf.Kprobe { + return fmt.Errorf("Kprobe not supported (requires >= v4.1): %w", ebpf.ErrNotSupported) + } + return nil + } + err := checkProgramTypes() + require.Error(t, err) + assert.ErrorIs(t, err, ebpf.ErrNotSupported) + assert.EqualError(t, err, "kernel does not support BPF Kprobe programs (CONFIG_BPF_EVENTS is not set?): not supported") + }) + + t.Run("inconclusive probe", func(t *testing.T) { + haveProgramType = func(ebpf.ProgramType) error { return unix.EPERM } + assert.NoError(t, checkProgramTypes()) + }) +}