From 3c1969f5ba871e29b7e46ff6288fb4ffcf2efb8d Mon Sep 17 00:00:00 2001 From: Nikki371 Date: Tue, 11 Mar 2025 16:41:29 +0530 Subject: [PATCH 1/3] fix: add cross-platform perf reader implementation and fix type mismatches - Created platform-specific implementations for perf reader with Linux implementation using github.com/cilium/ebpf/perf and stub implementations for non-Linux platforms - Fixed type mismatches by updating process.TargetDetails to process.Info in bpffs package - Changed GetCPUCount return type from int to uint64 for consistency - Improved documentation for kernel lockdown mode constants - Updated import statements to use the new internal perf package --- CHANGELOG.md | 1 + .../bpf/go.opentelemetry.io/auto/sdk/probe.go | 2 +- .../otel/traceglobal/probe.go | 5 +- .../pkg/instrumentation/bpffs/bpfsfs_other.go | 6 +- .../pkg/instrumentation/perf/perf_linux.go | 22 ++++ .../pkg/instrumentation/perf/perf_other.go | 116 ++++++++++++++++++ internal/pkg/instrumentation/probe/probe.go | 2 +- .../pkg/instrumentation/utils/kernel_other.go | 15 ++- 8 files changed, 156 insertions(+), 13 deletions(-) create mode 100644 internal/pkg/instrumentation/perf/perf_linux.go create mode 100644 internal/pkg/instrumentation/perf/perf_other.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3afaca3ff..69014cbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ OpenTelemetry Go Automatic Instrumentation adheres to [Semantic Versioning](http - Cache offsets for Go `1.24.1`. ([#1940](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1940)) - Cache offsets for `go.opentelemetry.io/otel@v1.35.0`. ([#1948](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1948)) - Cache offsets for `golang.org/x/net` `0.37.0`. ([#1948](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1948)) +- Added cross-platform build support for perf reader with Linux implementation using `github.com/cilium/ebpf/perf` and stub implementations for non-Linux platforms. ([#1969](https://github.com/open-telemetry/opentelemetry-go-instrumentation/pull/1969)) ### Removed diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/auto/sdk/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/auto/sdk/probe.go index 2803bd8e2..4fb4ae007 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/auto/sdk/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/auto/sdk/probe.go @@ -8,10 +8,10 @@ import ( "encoding/binary" "log/slog" - "github.com/cilium/ebpf/perf" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/perf" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" "go.opentelemetry.io/auto/internal/pkg/structfield" ) diff --git a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go index 5b72cc882..32610989a 100644 --- a/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go +++ b/internal/pkg/instrumentation/bpf/go.opentelemetry.io/otel/traceglobal/probe.go @@ -14,20 +14,19 @@ import ( "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/auto/internal/pkg/inject" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/perf" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe" "go.opentelemetry.io/auto/internal/pkg/instrumentation/utils" "go.opentelemetry.io/auto/internal/pkg/process" "go.opentelemetry.io/auto/internal/pkg/structfield" "github.com/Masterminds/semver/v3" - "github.com/cilium/ebpf/perf" "golang.org/x/sys/unix" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" - - "go.opentelemetry.io/auto/internal/pkg/instrumentation/context" ) //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target amd64,arm64 bpf ./bpf/probe.bpf.c diff --git a/internal/pkg/instrumentation/bpffs/bpfsfs_other.go b/internal/pkg/instrumentation/bpffs/bpfsfs_other.go index 193a8da28..141d62796 100644 --- a/internal/pkg/instrumentation/bpffs/bpfsfs_other.go +++ b/internal/pkg/instrumentation/bpffs/bpfsfs_other.go @@ -9,14 +9,14 @@ import "go.opentelemetry.io/auto/internal/pkg/process" // Stubs for non-linux systems -func PathForTargetApplication(target *process.TargetDetails) string { +func PathForTargetApplication(target *process.Info) string { return "" } -func Mount(target *process.TargetDetails) error { +func Mount(target *process.Info) error { return nil } -func Cleanup(target *process.TargetDetails) error { +func Cleanup(target *process.Info) error { return nil } diff --git a/internal/pkg/instrumentation/perf/perf_linux.go b/internal/pkg/instrumentation/perf/perf_linux.go new file mode 100644 index 000000000..10a1ab9e9 --- /dev/null +++ b/internal/pkg/instrumentation/perf/perf_linux.go @@ -0,0 +1,22 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:build linux + +package perf + +import ( + "github.com/cilium/ebpf/perf" +) + +// Re-export the types from github.com/cilium/ebpf/perf +type Record = perf.Record +type Reader = perf.Reader +type ReaderOptions = perf.ReaderOptions + +// Re-export the functions from github.com/cilium/ebpf/perf +var NewReader = perf.NewReader +var NewReaderWithOptions = perf.NewReaderWithOptions +var ErrClosed = perf.ErrClosed +var ErrFlushed = perf.ErrFlushed +var IsUnknownEvent = perf.IsUnknownEvent diff --git a/internal/pkg/instrumentation/perf/perf_other.go b/internal/pkg/instrumentation/perf/perf_other.go new file mode 100644 index 000000000..28042ce3b --- /dev/null +++ b/internal/pkg/instrumentation/perf/perf_other.go @@ -0,0 +1,116 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:build !linux + +// Package perf provides stub implementation for non-Linux platforms. +// These stubs allow the code to compile but will not work at runtime. +package perf + +import ( + "errors" + "fmt" + "os" + "time" + + "github.com/cilium/ebpf" +) + +// ErrClosed is returned when interacting with a closed Reader. +var ErrClosed = os.ErrClosed + +// ErrFlushed is returned when the Reader has been flushed. +var ErrFlushed = errors.New("perf reader flushed") + +// Record contains data from a perf event. +type Record struct { + // The CPU this record was generated on. + CPU int + + // The data submitted via bpf_perf_event_output. + RawSample []byte + + // The number of samples which could not be output, since + // the ring buffer was full. + LostSamples uint64 + + // The minimum number of bytes remaining in the per-CPU buffer after this Record has been read. + Remaining int +} + +// Reader allows reading from a perf event array. +type Reader struct{} + +// ReaderOptions control the behavior of the Reader. +type ReaderOptions struct { + // The number of events required in any per CPU buffer before + // Read will process data. This is mutually exclusive with Watermark. + WakeupEvents int + // The number of written bytes required in any per CPU buffer before + // Read will process data. Must be smaller than PerCPUBuffer. + Watermark int + // This perf ring buffer is overwritable, once full the oldest event will be + // overwritten by newest. + Overwritable bool +} + +// NewReader creates a new reader with default options. +func NewReader(array *ebpf.Map, perCPUBuffer int) (*Reader, error) { + return nil, errors.New("perf.Reader not supported on non-Linux platforms") +} + +// NewReaderWithOptions creates a new reader with the given options. +func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) (*Reader, error) { + return nil, errors.New("perf.Reader not supported on non-Linux platforms") +} + +// Close frees resources used by the reader. +func (r *Reader) Close() error { + return fmt.Errorf("perf reader: %w", ErrClosed) +} + +// Read the next record from the perf ring buffer. +func (r *Reader) Read() (Record, error) { + return Record{}, fmt.Errorf("perf reader: %w", ErrClosed) +} + +// ReadInto is like Read but allows reusing the Record. +func (r *Reader) ReadInto(rec *Record) error { + return fmt.Errorf("perf reader: %w", ErrClosed) +} + +// SetDeadline controls how long Read and ReadInto will block. +func (r *Reader) SetDeadline(t time.Time) { + // No-op on non-Linux +} + +// Pause stops all notifications from this Reader. +func (r *Reader) Pause() error { + return fmt.Errorf("perf reader: %w", ErrClosed) +} + +// Resume allows this perf reader to emit notifications. +func (r *Reader) Resume() error { + return fmt.Errorf("perf reader: %w", ErrClosed) +} + +// FlushAndClose flushes all pending events and closes the reader. +func (r *Reader) FlushAndClose() error { + return fmt.Errorf("perf reader: %w", ErrClosed) +} + +// Flush unblocks Read/ReadInto and returns pending samples. +func (r *Reader) Flush() error { + return fmt.Errorf("perf reader: %w", ErrClosed) +} + +// BufferSize returns the size in bytes of each per-CPU buffer. +func (r *Reader) BufferSize() int { + return 0 +} + +// IsUnknownEvent returns true if the error occurred because an +// unknown event was submitted to the perf event ring. +func IsUnknownEvent(err error) bool { + return false +} diff --git a/internal/pkg/instrumentation/probe/probe.go b/internal/pkg/instrumentation/probe/probe.go index fa9dac729..c50db55b7 100644 --- a/internal/pkg/instrumentation/probe/probe.go +++ b/internal/pkg/instrumentation/probe/probe.go @@ -17,12 +17,12 @@ import ( "github.com/Masterminds/semver/v3" "github.com/cilium/ebpf" "github.com/cilium/ebpf/link" - "github.com/cilium/ebpf/perf" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/auto/internal/pkg/inject" "go.opentelemetry.io/auto/internal/pkg/instrumentation/bpffs" + "go.opentelemetry.io/auto/internal/pkg/instrumentation/perf" "go.opentelemetry.io/auto/internal/pkg/instrumentation/probe/sampling" "go.opentelemetry.io/auto/internal/pkg/instrumentation/utils" "go.opentelemetry.io/auto/internal/pkg/process" diff --git a/internal/pkg/instrumentation/utils/kernel_other.go b/internal/pkg/instrumentation/utils/kernel_other.go index 7daa7a0cb..5e2f3206b 100644 --- a/internal/pkg/instrumentation/utils/kernel_other.go +++ b/internal/pkg/instrumentation/utils/kernel_other.go @@ -13,20 +13,25 @@ import ( func GetLinuxKernelVersion() *semver.Version { return nil } +// KernelLockdown represents different Linux Kernel security lockdown modes. type KernelLockdown uint8 const ( - KernelLockdownNone KernelLockdown = iota + 1 // Linux Kernel security lockdown mode [none] - KernelLockdownIntegrity // Linux Kernel security lockdown mode [integrity] - KernelLockdownConfidentiality // Linux Kernel security lockdown mode [confidentiality] - KernelLockdownOther // Linux Kernel security lockdown mode unknown + // KernelLockdownNone represents the 'none' security lockdown mode. + KernelLockdownNone KernelLockdown = iota + 1 + // KernelLockdownIntegrity represents the 'integrity' security lockdown mode. + KernelLockdownIntegrity + // KernelLockdownConfidentiality represents the 'confidentiality' security lockdown mode. + KernelLockdownConfidentiality + // KernelLockdownOther represents an unknown security lockdown mode. + KernelLockdownOther ) func KernelLockdownMode() KernelLockdown { return 0 } -func GetCPUCount() (int, error) { +func GetCPUCount() (uint64, error) { return 0, nil } From d6e84e642a932a6560342d8f44bfc0f2e37aec3a Mon Sep 17 00:00:00 2001 From: Nikki371 Date: Wed, 12 Mar 2025 22:25:24 +0530 Subject: [PATCH 2/3] Fix linter errors --- internal/pkg/instrumentation/perf/perf_linux.go | 13 +++++++++++-- internal/pkg/instrumentation/perf/perf_other.go | 15 +++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/internal/pkg/instrumentation/perf/perf_linux.go b/internal/pkg/instrumentation/perf/perf_linux.go index 10a1ab9e9..d8f1d9247 100644 --- a/internal/pkg/instrumentation/perf/perf_linux.go +++ b/internal/pkg/instrumentation/perf/perf_linux.go @@ -9,14 +9,23 @@ import ( "github.com/cilium/ebpf/perf" ) -// Re-export the types from github.com/cilium/ebpf/perf +// Re-export the types from github.com/cilium/ebpf/perf. type Record = perf.Record type Reader = perf.Reader type ReaderOptions = perf.ReaderOptions -// Re-export the functions from github.com/cilium/ebpf/perf +// Re-export the functions from github.com/cilium/ebpf/perf. +// NewReader is a re-export of perf.NewReader. var NewReader = perf.NewReader + +// NewReaderWithOptions is a re-export of perf.NewReaderWithOptions. var NewReaderWithOptions = perf.NewReaderWithOptions + +// ErrClosed is a re-export of perf.ErrClosed. var ErrClosed = perf.ErrClosed + +// ErrFlushed is a re-export of perf.ErrFlushed. var ErrFlushed = perf.ErrFlushed + +// IsUnknownEvent is a re-export of perf.IsUnknownEvent. var IsUnknownEvent = perf.IsUnknownEvent diff --git a/internal/pkg/instrumentation/perf/perf_other.go b/internal/pkg/instrumentation/perf/perf_other.go index 28042ce3b..001979104 100644 --- a/internal/pkg/instrumentation/perf/perf_other.go +++ b/internal/pkg/instrumentation/perf/perf_other.go @@ -9,7 +9,6 @@ package perf import ( "errors" - "fmt" "os" "time" @@ -66,17 +65,17 @@ func NewReaderWithOptions(array *ebpf.Map, perCPUBuffer int, opts ReaderOptions) // Close frees resources used by the reader. func (r *Reader) Close() error { - return fmt.Errorf("perf reader: %w", ErrClosed) + return errors.New("perf reader already closed") } // Read the next record from the perf ring buffer. func (r *Reader) Read() (Record, error) { - return Record{}, fmt.Errorf("perf reader: %w", ErrClosed) + return Record{}, errors.New("perf reader already closed") } // ReadInto is like Read but allows reusing the Record. func (r *Reader) ReadInto(rec *Record) error { - return fmt.Errorf("perf reader: %w", ErrClosed) + return errors.New("perf reader already closed") } // SetDeadline controls how long Read and ReadInto will block. @@ -86,22 +85,22 @@ func (r *Reader) SetDeadline(t time.Time) { // Pause stops all notifications from this Reader. func (r *Reader) Pause() error { - return fmt.Errorf("perf reader: %w", ErrClosed) + return errors.New("perf reader already closed") } // Resume allows this perf reader to emit notifications. func (r *Reader) Resume() error { - return fmt.Errorf("perf reader: %w", ErrClosed) + return errors.New("perf reader already closed") } // FlushAndClose flushes all pending events and closes the reader. func (r *Reader) FlushAndClose() error { - return fmt.Errorf("perf reader: %w", ErrClosed) + return errors.New("perf reader already closed") } // Flush unblocks Read/ReadInto and returns pending samples. func (r *Reader) Flush() error { - return fmt.Errorf("perf reader: %w", ErrClosed) + return errors.New("perf reader already closed") } // BufferSize returns the size in bytes of each per-CPU buffer. From 6875c671e1c104c045eab7022181e713e49b8074 Mon Sep 17 00:00:00 2001 From: Nikki371 Date: Fri, 14 Mar 2025 01:34:07 +0530 Subject: [PATCH 3/3] Fix formatting in perf_linux.go to comply with gofumpt standards --- internal/pkg/instrumentation/perf/perf_linux.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/pkg/instrumentation/perf/perf_linux.go b/internal/pkg/instrumentation/perf/perf_linux.go index d8f1d9247..5a8c1db33 100644 --- a/internal/pkg/instrumentation/perf/perf_linux.go +++ b/internal/pkg/instrumentation/perf/perf_linux.go @@ -10,9 +10,11 @@ import ( ) // Re-export the types from github.com/cilium/ebpf/perf. -type Record = perf.Record -type Reader = perf.Reader -type ReaderOptions = perf.ReaderOptions +type ( + Record = perf.Record + Reader = perf.Reader + ReaderOptions = perf.ReaderOptions +) // Re-export the functions from github.com/cilium/ebpf/perf. // NewReader is a re-export of perf.NewReader.