Note: This guide covers simple, single-purpose eBPF checks under pkg/collector/corechecks/ebpf/ ("container integration" checks). For complex/standalone features (e.g., network, GPU, dynamic instrumentation), see their dedicated packages and docs; refer to .cursor/rules/system_probe_modules.mdc for pointers.
Each eBPF-based check consists of three components:
See also: .cursor/rules/system_probe_modules.mdc for system-probe module context and cross-links.
-
Probe (
probe/<check>/) - System-probe side eBPF implementation<check>.go- Tracer with eBPF map management, NewTracer(), GetAndFlush(), Close()<check>_kern_types.go- CGO bridge for C structs (build tag://go:build ignore)model/*.go- API data models (exported types for stats)<check>_stub.go- Stub implementation for non-linux_bpf platforms- Build tags:
//go:build linux_bpffor main implementation
-
Check (
<check>/) - Agent side metric collection<check>.go- Check implementation with system-probe client- Communicates via HTTP with system-probe module
- Converts probe stats to Datadog metrics
- Build tags:
//go:build linux stub.go- Stub for non-linux platforms
-
System-Probe Module (
cmd/system-probe/modules/<check>.go)- Module registration and HTTP endpoint
- Wraps the probe tracer
- Build tags:
//go:build linux && linux_bpf
Located in c/runtime/<check>-kern.c:
- Per-CPU maps for lock-free stats collection
- Helper functions from
cgroup.h,bpf_helpers.h,bpf_tracing.h - CO-RE (Compile Once Run Everywhere) macros for portability
- Shared header
<check>-kern-user.hdefines C structs
type StatsKey struct { /* fields */ }
type StatsValue struct { /* fields */ }
type Stats map[StatsKey]StatsValue- Define BPF maps (usually per-CPU hash maps)
- Implement kprobes/kretprobes/tracepoints
- Update counters in maps
NewTracer(cfg *ebpf.Config)- Initialize with CO-RE + runtime fallbackGetAndFlush()- Iterate maps, aggregate per-CPU data, delete entriesClose()- Cleanup resources
- Register module with factory
- Expose
/checkHTTP endpoint - Track last check time for health monitoring
- Factory function accepting tagger component
- Configure() - Setup system-probe client
- Run() - Fetch stats, extract container IDs, tag metrics, submit
corecheckLoader.RegisterCheck(<check>.CheckName, <check>.Factory(tagger))Use the dda invschema.add-setting command.
if enabled := pkgconfigsetup.SystemProbe().GetBool("<check_name>.enabled"); enabled {
l.newService <- &StaticConfigService{adIdentifier: "_<check_name>"}
}eBPF programs, CGO type generation, and runtime compilation bundles are all managed by Bazel.
Add the eBPF CO-RE program in the check's c/runtime/BUILD.bazel using
ebpf_program_suite (see existing targets in
pkg/collector/corechecks/ebpf/c/runtime/BUILD.bazel). Then:
- Add the target to
_BAZEL_EBPF_CORE_TARGETSintasks/system_probe.py(needed for the copy step that stages.ofiles). - Add it to the
all_ebpf_programsfilegroup inpkg/ebpf/BUILD.bazel.
Add runtime compilation support by creating a runtime_compilation_bundle
target in pkg/ebpf/bytecode/BUILD.bazel:
runtime_compilation_bundle(
name = "<check-name>",
header_deps = _CORECHECK_HEADERS,
include_dirs = ["pkg/ebpf/c"],
out_go_file = "//pkg/ebpf/bytecode/runtime:<check-name>.go",
out_name = "<check-name>",
src_c = "//pkg/collector/corechecks/ebpf/c/runtime:<check-name>-kern.c",
)Then add the _flat target to _BAZEL_RUNTIME_FLAT_TARGETS in
tasks/system_probe.py and both the _flat and _verify_test targets to the
convenience targets in pkg/ebpf/BUILD.bazel (all_ebpf_programs and
verify_generated_files respectively).
Add CGO type generation by creating a cgo_godefs target in the check's
BUILD.bazel:
load("//bazel/rules/ebpf:cgo_godefs.bzl", "cgo_godefs")
exports_files(["<check>_kern_types.go", "<check>_kern_types_linux.go", "<check>_kern_types_linux_test.go"])
cgo_godefs(
name = "<check>_kern_types_godefs",
src = "<check>_kern_types.go",
)Then add the _test and _test_file_test targets to the
verify_generated_files test suite in pkg/ebpf/BUILD.bazel.
- Generates Go types from C structs for BPF map keys/values
- Header file must use
__u32,__u64etc. types and includektypes.h bazel test //pkg/ebpf:verify_generated_fileschecks all committed filesbazel run //<pkg>:<name>_godefsregenerates a single output
- Runtime compilation: Requires kernel headers
- CO-RE: Pre-compiled with BTF, fallback to runtime compilation
- Build:
dda inv system-probe.build --build-include linux_bpf
- Test file:
probe/<check>/<check>_test.gowith build tag//go:build linux_bpf - Use
ebpftest.TestBuildModesto test both CO-RE and runtime-compiled modes - Create sample C program in
testdata/to trigger the monitored events - Tests should verify:
- Probe loads successfully
- Events are captured correctly
- Stats are aggregated properly
Example test structure:
type checkTestSuite struct { suite.Suite }
func TestCheck(t *testing.T) {
ebpftest.TestBuildModes(t, []ebpftest.BuildMode{ebpftest.CORE, ebpftest.RuntimeCompiled}, "",
func(t *testing.T) {
suite.Run(t, new(checkTestSuite))
})
}- tcp_queue_length: Monitors TCP queue usage per container
- oom_kill: Detects OOM kill events
- seccomp_tracer: Tracks seccomp denial events
- Use per-CPU maps to avoid lock contention
- Always aggregate per-CPU data in GetAndFlush()
- Clean up maps after reading to prevent memory leaks
- Use cgroup helpers to extract container IDs
- Wrap errors with context:
fmt.Errorf("operation failed: %w", err) - Tag metrics with container ID and other relevant dimensions
- Provide CO-RE + runtime compilation fallback
- Test both build modes