diff --git a/internal/controller/controller.go b/internal/controller/controller.go index c696d1e36..4b70ed2a3 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -20,6 +20,8 @@ import ( const MiB = 1 << 20 +const MaxTailCalls = 29 + // Controller is an instance that runs, manages and stops the agent. type Controller struct { config *Config @@ -85,6 +87,7 @@ func (c *Controller) Start(ctx context.Context) error { ProbabilisticInterval: c.config.ProbabilisticInterval, ProbabilisticThreshold: c.config.ProbabilisticThreshold, OffCPUThreshold: uint32(c.config.OffCPUThreshold), + MaxTailCalls: MaxTailCalls, }) if err != nil { return fmt.Errorf("failed to load eBPF tracer: %w", err) diff --git a/support/ebpf/tracemgmt.h b/support/ebpf/tracemgmt.h index c50a1d665..72a159a70 100644 --- a/support/ebpf/tracemgmt.h +++ b/support/ebpf/tracemgmt.h @@ -204,6 +204,10 @@ static inline PerCPURecord *get_per_cpu_record(void) static inline PerCPURecord *get_pristine_per_cpu_record() { PerCPURecord *record = get_per_cpu_record(); + int key0 = 0; + SystemConfig *syscfg = bpf_map_lookup_elem(&system_config, &key0); + if (!syscfg) + return NULL; if (!record) return record; @@ -235,7 +239,7 @@ static inline PerCPURecord *get_pristine_per_cpu_record() record->luajitUnwindState.cframe = 0; record->luajitUnwindState.is_jit = false; record->unwindersDone = 0; - record->tailCalls = 0; + record->tailCallsRemaining = syscfg->max_tail_calls; record->ratelimitAction = RATELIMIT_ACTION_DEFAULT; record->customLabelsState.go_m_ptr = NULL; @@ -516,8 +520,8 @@ static inline __attribute__((__always_inline__)) void tail_call(void *ctx, int n return; } - if (record->tailCalls >= 29) { - // The maximum tail call count we need to support on older kernels is 32. At this point + if (!record->tailCallsRemaining) { + // At this point // there is a chance that continuing unwinding the stack would further increase the number of // tail calls. As a result we might lose the unwound stack as no further tail calls are left // to report it to user space. To make sure we do not run into this issue we stop unwinding @@ -526,7 +530,7 @@ static inline __attribute__((__always_inline__)) void tail_call(void *ctx, int n record->state.unwind_error = ERR_MAX_TAIL_CALLS; increment_metric(metricID_MaxTailCalls); } - record->tailCalls += 1; + --record->tailCallsRemaining; bpf_tail_call(ctx, &perf_progs, next); } diff --git a/support/ebpf/tracer.ebpf.debug.amd64 b/support/ebpf/tracer.ebpf.debug.amd64 index b40a13091..942239d14 100644 Binary files a/support/ebpf/tracer.ebpf.debug.amd64 and b/support/ebpf/tracer.ebpf.debug.amd64 differ diff --git a/support/ebpf/tracer.ebpf.debug.arm64 b/support/ebpf/tracer.ebpf.debug.arm64 index 2b8a9f9b1..eca05bf20 100644 Binary files a/support/ebpf/tracer.ebpf.debug.arm64 and b/support/ebpf/tracer.ebpf.debug.arm64 differ diff --git a/support/ebpf/tracer.ebpf.release.amd64 b/support/ebpf/tracer.ebpf.release.amd64 index fcfe3adb0..4bcdf7884 100644 Binary files a/support/ebpf/tracer.ebpf.release.amd64 and b/support/ebpf/tracer.ebpf.release.amd64 differ diff --git a/support/ebpf/tracer.ebpf.release.arm64 b/support/ebpf/tracer.ebpf.release.arm64 index 6d2962166..f74a881a4 100644 Binary files a/support/ebpf/tracer.ebpf.release.arm64 and b/support/ebpf/tracer.ebpf.release.arm64 differ diff --git a/support/ebpf/types.h b/support/ebpf/types.h index b36176bb0..bed966bb8 100644 --- a/support/ebpf/types.h +++ b/support/ebpf/types.h @@ -386,7 +386,7 @@ typedef enum TraceOrigin { // MAX_FRAME_UNWINDS defines the maximum number of frames per // Trace we can unwind and respect the limit of eBPF instructions, // limit of tail calls and limit of stack size per eBPF program. -#define MAX_FRAME_UNWINDS 128 +#define MAX_FRAME_UNWINDS 256 // MAX_NON_ERROR_FRAME_UNWINDS defines the maximum number of frames // to be pushed by unwinders while still leaving space for an error frame. @@ -886,7 +886,7 @@ typedef struct PerCPURecord { u32 unwindersDone; // tailCalls tracks the number of calls to bpf_tail_call(). - u8 tailCalls; + u8 tailCallsRemaining; // ratelimitAction determines the PID event rate limiting mode u8 ratelimitAction; @@ -1051,6 +1051,9 @@ typedef struct SystemConfig { // Enables the temporary hack that drops pure errors frames in unwind_stop. bool drop_error_only_traces; + + // Maximum number of eBPF tail calls before giving up. + u8 max_tail_calls; } SystemConfig; // Avoid including all of arch/arm64/include/uapi/asm/ptrace.h by copying the diff --git a/support/types.go b/support/types.go index 980a8fbc3..c2db4d3f9 100644 --- a/support/types.go +++ b/support/types.go @@ -47,7 +47,7 @@ const ( EventTypeGenericPID = 0x1 ) -const MaxFrameUnwinds = 0x80 +const MaxFrameUnwinds = 0x100 const ( MetricIDBeginCumulative = 0x6d diff --git a/tracer/systemconfig.go b/tracer/systemconfig.go index 75ba5d177..d28841531 100644 --- a/tracer/systemconfig.go +++ b/tracer/systemconfig.go @@ -227,7 +227,8 @@ func determineStackLayout(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map func loadSystemConfig(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, kernelSymbols *libpf.SymbolMap, includeTracers types.IncludedTracers, - offCPUThreshold uint32, filterErrorFrames bool) error { + offCPUThreshold uint32, filterErrorFrames bool, + maxTailCalls uint8) error { pacMask := pacmask.GetPACMask() if pacMask != 0 { log.Infof("Determined PAC mask to be 0x%016X", pacMask) @@ -238,6 +239,7 @@ func loadSystemConfig(coll *cebpf.CollectionSpec, maps map[string]*cebpf.Map, inverse_pac_mask: ^C.u64(pacMask), drop_error_only_traces: C.bool(filterErrorFrames), off_cpu_threshold: C.u32(offCPUThreshold), + max_tail_calls: C.u8(maxTailCalls), } if err := parseBTF(&syscfg); err != nil { diff --git a/tracer/tracer.go b/tracer/tracer.go index 57e4d8e4d..c324382c1 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -159,6 +159,8 @@ type Config struct { ProbabilisticThreshold uint // OffCPUThreshold is the user defined threshold for off-cpu profiling. OffCPUThreshold uint32 + // MaxTailCalls is the number of tail calls the eBPF is allowed to make before giving up. + MaxTailCalls uint8 } // hookPoint specifies the group and name of the hooked point in the kernel. @@ -511,7 +513,7 @@ func initializeMapsAndPrograms(kernelSymbols *libpf.SymbolMap, cfg *Config) ( } if err = loadSystemConfig(coll, ebpfMaps, kernelSymbols, cfg.IncludeTracers, - cfg.OffCPUThreshold, cfg.FilterErrorFrames); err != nil { + cfg.OffCPUThreshold, cfg.FilterErrorFrames, cfg.MaxTailCalls); err != nil { return nil, nil, fmt.Errorf("failed to load system config: %v", err) }