From 44357ef872214b657999734143fc60457511fdc0 Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Tue, 13 Jan 2026 11:26:58 +0100 Subject: [PATCH 1/3] tracer: load only interpreter specific maps for enabled interpreters Reduce memory requirement by loading only eBPF maps of enabled tracers. Signed-off-by: Florian Lehner --- tracer/tracer.go | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tracer/tracer.go b/tracer/tracer.go index f25bec4c4..fc08f22a0 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -522,6 +522,34 @@ func rewriteMaps(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map) error { return nil } +// isTracerEnabled checks if an interpreter tracer for the given map is enabled. +func isTracerEnabled(mapName string, includeTracers types.IncludedTracers) bool { + switch mapName { + case "perl_procs": + return includeTracers.Has(types.PerlTracer) + case "php_procs": + return includeTracers.Has(types.PHPTracer) + case "py_procs": + return includeTracers.Has(types.PythonTracer) + case "hotspot_procs": + return includeTracers.Has(types.HotspotTracer) + case "ruby_procs": + return includeTracers.Has(types.RubyTracer) + case "v8_procs": + return includeTracers.Has(types.V8Tracer) + case "dotnet_procs": + return includeTracers.Has(types.DotnetTracer) + case "beam_procs": + return includeTracers.Has(types.BEAMTracer) + case "go_labels_procs", "apm_int_procs": + // go_labels_procs and apm_int_procs are called from + // unwind_stop and therefore need to be available all the time. + return true + default: + return true // Not an interpreter map, so it should be loaded + } +} + // loadAllMaps loads all eBPF maps that are used in our eBPF programs. func loadAllMaps(coll *cebpf.CollectionSpec, cfg *Config, ebpfMaps map[string]*cebpf.Map, @@ -559,6 +587,11 @@ func loadAllMaps(coll *cebpf.CollectionSpec, cfg *Config, // Off CPU Profiling is disabled. So do not load this map. continue } + + if !isTracerEnabled(mapName, cfg.IncludeTracers) { + log.Debugf("Skipping eBPF map %s: tracer not enabled", mapName) + continue + } if newSize, ok := adaption[mapName]; ok { log.Debugf("Size of eBPF map %s: %v", mapName, newSize) mapSpec.MaxEntries = newSize From d81238d5914fdcde9e3d437559d706157e7a994d Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Tue, 13 Jan 2026 12:34:04 +0100 Subject: [PATCH 2/3] rename function Signed-off-by: Florian Lehner --- tracer/tracer.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tracer/tracer.go b/tracer/tracer.go index fc08f22a0..d681277e2 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -522,8 +522,8 @@ func rewriteMaps(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map) error { return nil } -// isTracerEnabled checks if an interpreter tracer for the given map is enabled. -func isTracerEnabled(mapName string, includeTracers types.IncludedTracers) bool { +// isMapEnabled checks if the given map is enabled and should be loaded. +func isMapEnabled(mapName string, includeTracers types.IncludedTracers) bool { switch mapName { case "perl_procs": return includeTracers.Has(types.PerlTracer) @@ -588,7 +588,7 @@ func loadAllMaps(coll *cebpf.CollectionSpec, cfg *Config, continue } - if !isTracerEnabled(mapName, cfg.IncludeTracers) { + if !isMapEnabled(mapName, cfg.IncludeTracers) { log.Debugf("Skipping eBPF map %s: tracer not enabled", mapName) continue } From 19aabe4159ac8dd901e2f384f645e90f57bdba5f Mon Sep 17 00:00:00 2001 From: Florian Lehner Date: Tue, 13 Jan 2026 12:55:10 +0100 Subject: [PATCH 3/3] move IsMapEnabled to types package Signed-off-by: Florian Lehner --- processmanager/ebpf/ebpf.go | 7 ++++++- tracer/tracer.go | 32 ++------------------------------ tracer/types/parse.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/processmanager/ebpf/ebpf.go b/processmanager/ebpf/ebpf.go index 071bf81b3..9b9a1812b 100644 --- a/processmanager/ebpf/ebpf.go +++ b/processmanager/ebpf/ebpf.go @@ -15,6 +15,7 @@ import ( cebpf "github.com/cilium/ebpf" "github.com/cilium/ebpf/features" "go.opentelemetry.io/ebpf-profiler/internal/log" + "go.opentelemetry.io/ebpf-profiler/tracer/types" "golang.org/x/exp/constraints" "go.opentelemetry.io/ebpf-profiler/host" @@ -78,7 +79,8 @@ var _ ebpfapi.EbpfHandler = &ebpfMapsImpl{} // // It further spawns background workers for deferred map updates; the given // context can be used to terminate them on shutdown. -func LoadMaps(ctx context.Context, maps map[string]*cebpf.Map, stackdeltaInnerMapSpec *cebpf.MapSpec) (ebpfapi.EbpfHandler, error) { +func LoadMaps(ctx context.Context, includeTracers types.IncludedTracers, + maps map[string]*cebpf.Map, stackdeltaInnerMapSpec *cebpf.MapSpec) (ebpfapi.EbpfHandler, error) { impl := &ebpfMapsImpl{ stackdeltaInnerMapTemplate: stackdeltaInnerMapSpec, } @@ -94,6 +96,9 @@ func LoadMaps(ctx context.Context, maps map[string]*cebpf.Map, stackdeltaInnerMa } mapVal, ok := maps[nameTag] if !ok { + if !types.IsMapEnabled(nameTag, includeTracers) { + continue + } log.Fatalf("Map %v is not available", nameTag) } implRefVal.Field(i).Set(reflect.ValueOf(mapVal)) diff --git a/tracer/tracer.go b/tracer/tracer.go index d681277e2..f77c9bee8 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -225,7 +225,7 @@ func NewTracer(ctx context.Context, cfg *Config) (*Tracer, error) { return nil, fmt.Errorf("failed to load eBPF code: %v", err) } - ebpfHandler, err := pmebpf.LoadMaps(ctx, ebpfMaps, stackdeltaInnerMapSpec) + ebpfHandler, err := pmebpf.LoadMaps(ctx, cfg.IncludeTracers, ebpfMaps, stackdeltaInnerMapSpec) if err != nil { return nil, fmt.Errorf("failed to load eBPF maps: %v", err) } @@ -522,34 +522,6 @@ func rewriteMaps(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map) error { return nil } -// isMapEnabled checks if the given map is enabled and should be loaded. -func isMapEnabled(mapName string, includeTracers types.IncludedTracers) bool { - switch mapName { - case "perl_procs": - return includeTracers.Has(types.PerlTracer) - case "php_procs": - return includeTracers.Has(types.PHPTracer) - case "py_procs": - return includeTracers.Has(types.PythonTracer) - case "hotspot_procs": - return includeTracers.Has(types.HotspotTracer) - case "ruby_procs": - return includeTracers.Has(types.RubyTracer) - case "v8_procs": - return includeTracers.Has(types.V8Tracer) - case "dotnet_procs": - return includeTracers.Has(types.DotnetTracer) - case "beam_procs": - return includeTracers.Has(types.BEAMTracer) - case "go_labels_procs", "apm_int_procs": - // go_labels_procs and apm_int_procs are called from - // unwind_stop and therefore need to be available all the time. - return true - default: - return true // Not an interpreter map, so it should be loaded - } -} - // loadAllMaps loads all eBPF maps that are used in our eBPF programs. func loadAllMaps(coll *cebpf.CollectionSpec, cfg *Config, ebpfMaps map[string]*cebpf.Map, @@ -588,7 +560,7 @@ func loadAllMaps(coll *cebpf.CollectionSpec, cfg *Config, continue } - if !isMapEnabled(mapName, cfg.IncludeTracers) { + if !types.IsMapEnabled(mapName, cfg.IncludeTracers) { log.Debugf("Skipping eBPF map %s: tracer not enabled", mapName) continue } diff --git a/tracer/types/parse.go b/tracer/types/parse.go index ed36934fc..78b55eacf 100644 --- a/tracer/types/parse.go +++ b/tracer/types/parse.go @@ -51,6 +51,34 @@ func init() { } } +// IsMapEnabled checks if the given map is enabled and should be loaded. +func IsMapEnabled(mapName string, includeTracers IncludedTracers) bool { + switch mapName { + case "perl_procs": + return includeTracers.Has(PerlTracer) + case "php_procs": + return includeTracers.Has(PHPTracer) + case "py_procs": + return includeTracers.Has(PythonTracer) + case "hotspot_procs": + return includeTracers.Has(HotspotTracer) + case "ruby_procs": + return includeTracers.Has(RubyTracer) + case "v8_procs": + return includeTracers.Has(V8Tracer) + case "dotnet_procs": + return includeTracers.Has(DotnetTracer) + case "beam_procs": + return includeTracers.Has(BEAMTracer) + case "go_labels_procs", "apm_int_procs": + // go_labels_procs and apm_int_procs are called from + // unwind_stop and therefore need to be available all the time. + return true + default: + return true // Not an interpreter map, so it should be loaded + } +} + // tracerTypeFromName returns the tracer type for the given name. func tracerTypeFromName(s string) (tracerType, bool) { tt, ok := tracerNameToType[s]