From 6bb5ad0546ec832e22737fd31308a8b6ff868c2b Mon Sep 17 00:00:00 2001 From: Tolya Korniltsev Date: Wed, 16 Jul 2025 19:34:01 +0700 Subject: [PATCH] split ebpf to interface and implementation --- processmanager/ebpf/handler.go | 108 ++++++++++++++++++ processmanager/ebpf/{ => impl}/asyncupdate.go | 2 +- .../asyncupdate_integration_test.go | 2 +- processmanager/ebpf/{ => impl}/ebpf.go | 92 ++------------- .../ebpf/{ => impl}/ebpf_integration_test.go | 2 +- processmanager/ebpf/{ => impl}/ebpf_test.go | 2 +- processmanager/ebpf/types.go | 12 -- tracer/tracer.go | 2 +- 8 files changed, 120 insertions(+), 102 deletions(-) create mode 100644 processmanager/ebpf/handler.go rename processmanager/ebpf/{ => impl}/asyncupdate.go (98%) rename processmanager/ebpf/{ => impl}/asyncupdate_integration_test.go (99%) rename processmanager/ebpf/{ => impl}/ebpf.go (87%) rename processmanager/ebpf/{ => impl}/ebpf_integration_test.go (99%) rename processmanager/ebpf/{ => impl}/ebpf_test.go (98%) delete mode 100644 processmanager/ebpf/types.go diff --git a/processmanager/ebpf/handler.go b/processmanager/ebpf/handler.go new file mode 100644 index 000000000..4e2435c9a --- /dev/null +++ b/processmanager/ebpf/handler.go @@ -0,0 +1,108 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package ebpf // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + +/* +#include +#include "../../support/ebpf/types.h" +*/ +import "C" + +import ( + "fmt" + + "go.opentelemetry.io/ebpf-profiler/host" + "go.opentelemetry.io/ebpf-profiler/interpreter" + "go.opentelemetry.io/ebpf-profiler/libpf" + "go.opentelemetry.io/ebpf-profiler/lpm" + "go.opentelemetry.io/ebpf-profiler/metrics" + "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" + "go.opentelemetry.io/ebpf-profiler/util" +) + +// EbpfHandler provides the functionality to interact with eBPF maps. +type EbpfHandler interface { + // Embed interpreter.EbpfHandler as subset of this interface. + interpreter.EbpfHandler + + // RemoveReportedPID removes a PID from the reported_pids eBPF map. + RemoveReportedPID(pid libpf.PID) + + // UpdateUnwindInfo writes UnwindInfo to given unwind info array index + UpdateUnwindInfo(index uint16, info stackdeltatypes.UnwindInfo) error + + // UpdateExeIDToStackDeltas defines a function that updates the eBPF map exe_id_to_stack_deltas + // for host.FileID with the elements of StackDeltaEBPF. It returns the mapID used. + UpdateExeIDToStackDeltas(fileID host.FileID, deltas []StackDeltaEBPF) (uint16, error) + + // DeleteExeIDToStackDeltas defines a function that removes the entries from the outer eBPF + // map exe_id_to_stack_deltas and its associated inner map entries. + DeleteExeIDToStackDeltas(fileID host.FileID, mapID uint16) error + + // UpdateStackDeltaPages defines a function that updates the mapping in a eBPF map from + // a FileID and page to its stack delta lookup information. + UpdateStackDeltaPages(fileID host.FileID, numDeltasPerPage []uint16, + mapID uint16, firstPageAddr uint64) error + + // DeleteStackDeltaPage defines a function that removes the element specified by fileID and page + // from the eBPF map. + DeleteStackDeltaPage(fileID host.FileID, page uint64) error + + // UpdatePidPageMappingInfo defines a function that updates the eBPF map + // pid_page_to_mapping_info with the given pidAndPage and fileIDAndOffset encoded values + // as key/value pair. + UpdatePidPageMappingInfo(pid libpf.PID, prefix lpm.Prefix, fileID, bias uint64) error + + // DeletePidPageMappingInfo removes the elements specified by prefixes from eBPF map + // pid_page_to_mapping_info and returns the number of elements removed. + DeletePidPageMappingInfo(pid libpf.PID, prefixes []lpm.Prefix) (int, error) + + // CollectMetrics returns gathered errors for changes to eBPF maps. + CollectMetrics() []metrics.Metric + + // SupportsGenericBatchOperations returns true if the kernel supports eBPF batch operations + // on hash and array maps. + SupportsGenericBatchOperations() bool + + // SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations + // on LPM trie maps. + SupportsLPMTrieBatchOperations() bool +} + +// StackDeltaEBPF represents stack deltas preprocessed by the ProcessManager which are +// then loaded to the eBPF map. This is Go equivalent of 'struct StackDelta' in eBPF types.h. +// See the eBPF header file for details. +type StackDeltaEBPF struct { + AddressLow uint16 + UnwindInfo uint16 +} + +func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, + offsetRanges []util.Range) (key uint64, value C.OffsetRange, err error) { + rLen := len(offsetRanges) + if rLen < 1 || rLen > 2 { + return 0, C.OffsetRange{}, fmt.Errorf("invalid ranges %v", offsetRanges) + } + // The keys of this map are executable-id-and-offset-into-text entries, and + // the offset_range associated with them gives the precise area in that page + // where the main interpreter loop is located. This is required to unwind + // nicely from native code into interpreted code. + key = uint64(fileID) + first := offsetRanges[0] + value = C.OffsetRange{ + lower_offset1: C.u64(first.Start), + upper_offset1: C.u64(first.End), + program_index: C.u16(ebpfProgIndex), + } + if len(offsetRanges) == 2 { + // Fields {lower,upper}_offset2 may be used to specify an optional second range + // of an interpreter function. This may be useful if the interpreter function + // consists of two non-contiguous memory ranges, which may happen due to Hot/Cold + // split compiler optimization + second := offsetRanges[1] + value.lower_offset2 = C.u64(second.Start) + value.upper_offset2 = C.u64(second.End) + } + return key, value, nil +} diff --git a/processmanager/ebpf/asyncupdate.go b/processmanager/ebpf/impl/asyncupdate.go similarity index 98% rename from processmanager/ebpf/asyncupdate.go rename to processmanager/ebpf/impl/asyncupdate.go index da7c91f4a..9991de381 100644 --- a/processmanager/ebpf/asyncupdate.go +++ b/processmanager/ebpf/impl/asyncupdate.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package ebpf // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" +package impl // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" import ( "context" diff --git a/processmanager/ebpf/asyncupdate_integration_test.go b/processmanager/ebpf/impl/asyncupdate_integration_test.go similarity index 99% rename from processmanager/ebpf/asyncupdate_integration_test.go rename to processmanager/ebpf/impl/asyncupdate_integration_test.go index 9c469d288..5bc4438fa 100644 --- a/processmanager/ebpf/asyncupdate_integration_test.go +++ b/processmanager/ebpf/impl/asyncupdate_integration_test.go @@ -3,7 +3,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package ebpf +package impl import ( "context" diff --git a/processmanager/ebpf/ebpf.go b/processmanager/ebpf/impl/ebpf.go similarity index 87% rename from processmanager/ebpf/ebpf.go rename to processmanager/ebpf/impl/ebpf.go index 3c0d22583..ef18758d5 100644 --- a/processmanager/ebpf/ebpf.go +++ b/processmanager/ebpf/impl/ebpf.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package ebpf // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" +package impl // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" import ( "context" @@ -15,11 +15,11 @@ import ( "github.com/cilium/ebpf/features" log "github.com/sirupsen/logrus" "go.opentelemetry.io/ebpf-profiler/host" - "go.opentelemetry.io/ebpf-profiler/interpreter" "go.opentelemetry.io/ebpf-profiler/libpf" "go.opentelemetry.io/ebpf-profiler/lpm" "go.opentelemetry.io/ebpf-profiler/metrics" sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes" + "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" "go.opentelemetry.io/ebpf-profiler/rlimit" "go.opentelemetry.io/ebpf-profiler/support" "go.opentelemetry.io/ebpf-profiler/util" @@ -28,7 +28,7 @@ import ( /* #include -#include "../../support/ebpf/types.h" +#include "../../../support/ebpf/types.h" */ import "C" @@ -40,55 +40,6 @@ const ( updatePoolQueueCap = 8 ) -// EbpfHandler provides the functionality to interact with eBPF maps. -type EbpfHandler interface { - // Embed interpreter.EbpfHandler as subset of this interface. - interpreter.EbpfHandler - - // RemoveReportedPID removes a PID from the reported_pids eBPF map. - RemoveReportedPID(pid libpf.PID) - - // UpdateUnwindInfo writes UnwindInfo to given unwind info array index - UpdateUnwindInfo(index uint16, info sdtypes.UnwindInfo) error - - // UpdateExeIDToStackDeltas defines a function that updates the eBPF map exe_id_to_stack_deltas - // for host.FileID with the elements of StackDeltaEBPF. It returns the mapID used. - UpdateExeIDToStackDeltas(fileID host.FileID, deltas []StackDeltaEBPF) (uint16, error) - - // DeleteExeIDToStackDeltas defines a function that removes the entries from the outer eBPF - // map exe_id_to_stack_deltas and its associated inner map entries. - DeleteExeIDToStackDeltas(fileID host.FileID, mapID uint16) error - - // UpdateStackDeltaPages defines a function that updates the mapping in a eBPF map from - // a FileID and page to its stack delta lookup information. - UpdateStackDeltaPages(fileID host.FileID, numDeltasPerPage []uint16, - mapID uint16, firstPageAddr uint64) error - - // DeleteStackDeltaPage defines a function that removes the element specified by fileID and page - // from the eBPF map. - DeleteStackDeltaPage(fileID host.FileID, page uint64) error - - // UpdatePidPageMappingInfo defines a function that updates the eBPF map - // pid_page_to_mapping_info with the given pidAndPage and fileIDAndOffset encoded values - // as key/value pair. - UpdatePidPageMappingInfo(pid libpf.PID, prefix lpm.Prefix, fileID, bias uint64) error - - // DeletePidPageMappingInfo removes the elements specified by prefixes from eBPF map - // pid_page_to_mapping_info and returns the number of elements removed. - DeletePidPageMappingInfo(pid libpf.PID, prefixes []lpm.Prefix) (int, error) - - // CollectMetrics returns gathered errors for changes to eBPF maps. - CollectMetrics() []metrics.Metric - - // SupportsGenericBatchOperations returns true if the kernel supports eBPF batch operations - // on hash and array maps. - SupportsGenericBatchOperations() bool - - // SupportsLPMTrieBatchOperations returns true if the kernel supports eBPF batch operations - // on LPM trie maps. - SupportsLPMTrieBatchOperations() bool -} - type ebpfMapsImpl struct { // Interpreter related eBPF maps interpreterOffsets *cebpf.Map @@ -138,14 +89,14 @@ var outerMapsName = [...]string{ } // Compile time check to make sure ebpfMapsImpl satisfies the interface . -var _ EbpfHandler = &ebpfMapsImpl{} +var _ ebpf.EbpfHandler = &ebpfMapsImpl{} // LoadMaps checks if the needed maps for the process manager are available // and loads their references into a package-internal structure. // // 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) (EbpfHandler, error) { +func LoadMaps(ctx context.Context, maps map[string]*cebpf.Map) (ebpf.EbpfHandler, error) { impl := &ebpfMapsImpl{} impl.errCounter = make(map[metrics.MetricID]int64) @@ -256,7 +207,7 @@ func LoadMaps(ctx context.Context, maps map[string]*cebpf.Map) (EbpfHandler, err // UpdateInterpreterOffsets adds the given moduleRanges to the eBPF map interpreterOffsets. func (impl *ebpfMapsImpl) UpdateInterpreterOffsets(ebpfProgIndex uint16, fileID host.FileID, offsetRanges []util.Range) error { - key, value, err := InterpreterOffsetKeyValue(ebpfProgIndex, fileID, offsetRanges) + key, value, err := ebpf.InterpreterOffsetKeyValue(ebpfProgIndex, fileID, offsetRanges) if err != nil { return err } @@ -268,35 +219,6 @@ func (impl *ebpfMapsImpl) UpdateInterpreterOffsets(ebpfProgIndex uint16, fileID return nil } -func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID, - offsetRanges []util.Range) (key uint64, value C.OffsetRange, err error) { - rLen := len(offsetRanges) - if rLen < 1 || rLen > 2 { - return 0, C.OffsetRange{}, fmt.Errorf("invalid ranges %v", offsetRanges) - } - // The keys of this map are executable-id-and-offset-into-text entries, and - // the offset_range associated with them gives the precise area in that page - // where the main interpreter loop is located. This is required to unwind - // nicely from native code into interpreted code. - key = uint64(fileID) - first := offsetRanges[0] - value = C.OffsetRange{ - lower_offset1: C.u64(first.Start), - upper_offset1: C.u64(first.End), - program_index: C.u16(ebpfProgIndex), - } - if len(offsetRanges) == 2 { - // Fields {lower,upper}_offset2 may be used to specify an optional second range - // of an interpreter function. This may be useful if the interpreter function - // consists of two non-contiguous memory ranges, which may happen due to Hot/Cold - // split compiler optimization - second := offsetRanges[1] - value.lower_offset2 = C.u64(second.Start) - value.upper_offset2 = C.u64(second.End) - } - return key, value, nil -} - // getInterpreterTypeMap returns the eBPF map for the given typ // or an error if typ is not supported. func (impl *ebpfMapsImpl) getInterpreterTypeMap(typ libpf.InterpreterType) (*cebpf.Map, error) { @@ -592,7 +514,7 @@ func (impl *ebpfMapsImpl) UpdateUnwindInfo(index uint16, info sdtypes.UnwindInfo // UpdateExeIDToStackDeltas creates a nested map for fileID in the eBPF map exeIDTostack_deltas // and inserts the elements of the deltas array in this nested map. Returns mapID or error. -func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas []StackDeltaEBPF) ( +func (impl *ebpfMapsImpl) UpdateExeIDToStackDeltas(fileID host.FileID, deltas []ebpf.StackDeltaEBPF) ( uint16, error) { numDeltas := len(deltas) mapID, err := getMapID(uint32(numDeltas)) diff --git a/processmanager/ebpf/ebpf_integration_test.go b/processmanager/ebpf/impl/ebpf_integration_test.go similarity index 99% rename from processmanager/ebpf/ebpf_integration_test.go rename to processmanager/ebpf/impl/ebpf_integration_test.go index c1af9a3c5..032c11d65 100644 --- a/processmanager/ebpf/ebpf_integration_test.go +++ b/processmanager/ebpf/impl/ebpf_integration_test.go @@ -3,7 +3,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package ebpf +package impl import ( "testing" diff --git a/processmanager/ebpf/ebpf_test.go b/processmanager/ebpf/impl/ebpf_test.go similarity index 98% rename from processmanager/ebpf/ebpf_test.go rename to processmanager/ebpf/impl/ebpf_test.go index fec5bf5dd..371d63baa 100644 --- a/processmanager/ebpf/ebpf_test.go +++ b/processmanager/ebpf/impl/ebpf_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package ebpf +package impl import ( "fmt" diff --git a/processmanager/ebpf/types.go b/processmanager/ebpf/types.go deleted file mode 100644 index bd5de07d0..000000000 --- a/processmanager/ebpf/types.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package ebpf // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" - -// StackDeltaEBPF represents stack deltas preprocessed by the ProcessManager which are -// then loaded to the eBPF map. This is Go equivalent of 'struct StackDelta' in eBPF types.h. -// See the eBPF header file for details. -type StackDeltaEBPF struct { - AddressLow uint16 - UnwindInfo uint16 -} diff --git a/tracer/tracer.go b/tracer/tracer.go index 00ea5058f..6970c982a 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -31,7 +31,7 @@ import ( "go.opentelemetry.io/ebpf-profiler/nativeunwind/elfunwindinfo" "go.opentelemetry.io/ebpf-profiler/periodiccaller" pm "go.opentelemetry.io/ebpf-profiler/processmanager" - pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf" + pmebpf "go.opentelemetry.io/ebpf-profiler/processmanager/ebpf/impl" "go.opentelemetry.io/ebpf-profiler/reporter" "go.opentelemetry.io/ebpf-profiler/rlimit" "go.opentelemetry.io/ebpf-profiler/support"