Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/unit-test-on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,28 @@ jobs:
name: integration-test-binaries-${{ matrix.target_arch }}
path: support/*.test

coredump-test-macos:
name: Coredump tests (macOS)
runs-on: macos-latest
steps:
- name: Clone code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version-file: go.mod
cache-dependency-path: go.sum
- name: Cache coredump modules
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: tools/coredump/modulecache
key: coredumps-arm64-${{ hashFiles('tools/coredump/testdata/*/*.json') }}
restore-keys: |
coredumps-arm64
coredumps-
- name: Run coredump tests
run: GODEBUG=asyncpreemptoff=1 go test -v ./tools/coredump/

integration-tests:
name: Integration tests (v${{ matrix.kernel }} ${{ matrix.target_arch }})
runs-on: ubuntu-24.04
Expand Down
10 changes: 10 additions & 0 deletions libpf/pfelf/machine_amd64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build amd64

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package pfelf // import "go.opentelemetry.io/ebpf-profiler/libpf/pfelf"

import "debug/elf"

const CurrentMachine = elf.EM_X86_64
10 changes: 10 additions & 0 deletions libpf/pfelf/machine_arm64.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//go:build arm64

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package pfelf // import "go.opentelemetry.io/ebpf-profiler/libpf/pfelf"

import "debug/elf"

const CurrentMachine = elf.EM_AARCH64
4 changes: 1 addition & 3 deletions process/debug_amd64.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build amd64
//go:build linux && amd64

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
Expand All @@ -11,8 +11,6 @@ import (
"fmt"
)

const currentMachine = elf.EM_X86_64

func (sp *ptraceProcess) getThreadInfo(tid int) (ThreadInfo, error) {
prStatus := make([]byte, 28*8)
if err := ptraceGetRegset(tid, int(elf.NT_PRSTATUS), prStatus); err != nil {
Expand Down
4 changes: 1 addition & 3 deletions process/debug_arm64.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build arm64
//go:build linux && arm64

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
Expand All @@ -11,8 +11,6 @@ import (
"fmt"
)

const currentMachine = elf.EM_AARCH64

func (sp *ptraceProcess) GetMachineData() MachineData {
pacMask := make([]byte, 16)
_ = ptraceGetRegset(int(sp.pid), int(NT_ARM_PAC_MASK), pacMask)
Expand Down
2 changes: 2 additions & 0 deletions process/debug.go → process/debug_linux.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build linux

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

Expand Down
19 changes: 19 additions & 0 deletions process/debug_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//go:build !linux

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package process // import "go.opentelemetry.io/ebpf-profiler/process"

import (
"fmt"
"runtime"

"go.opentelemetry.io/ebpf-profiler/libpf"
)

// NewPtrace is the stub implementation, allowing to compile the process
// package on non linux systems, always failing at runtime with an error if used.
func NewPtrace(_ libpf.PID) (Process, error) {
return nil, fmt.Errorf("unsupported os %s", runtime.GOOS)
}
2 changes: 1 addition & 1 deletion process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func (sp *systemProcess) PID() libpf.PID {
}

func (sp *systemProcess) GetMachineData() MachineData {
return MachineData{Machine: currentMachine}
return MachineData{Machine: pfelf.CurrentMachine}
}

func trimMappingPath(path string) string {
Expand Down
4 changes: 4 additions & 0 deletions process/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package process
import (
"debug/elf"
"os"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -113,6 +114,9 @@ func TestParseMappings(t *testing.T) {
}

func TestNewPIDOfSelf(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skipf("unsupported os %s", runtime.GOOS)
}
pid := libpf.PID(os.Getpid())
pr := New(pid, pid)
assert.NotNil(t, pr)
Expand Down
1 change: 1 addition & 0 deletions processmanager/ebpf/asyncupdate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

cebpf "github.com/cilium/ebpf"
log "github.com/sirupsen/logrus"

"go.opentelemetry.io/ebpf-profiler/host"
)

Expand Down
92 changes: 8 additions & 84 deletions processmanager/ebpf/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ import (
cebpf "github.com/cilium/ebpf"
"github.com/cilium/ebpf/features"
log "github.com/sirupsen/logrus"
"golang.org/x/exp/constraints"

"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/ebpfapi"
"go.opentelemetry.io/ebpf-profiler/rlimit"
"go.opentelemetry.io/ebpf-profiler/support"
"go.opentelemetry.io/ebpf-profiler/util"
"golang.org/x/exp/constraints"
)

const (
Expand All @@ -35,55 +36,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 `name:"interpreter_offsets"`
Expand Down Expand Up @@ -114,14 +66,14 @@ type ebpfMapsImpl struct {
}

// Compile time check to make sure ebpfMapsImpl satisfies the interface .
var _ EbpfHandler = &ebpfMapsImpl{}
var _ ebpfapi.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) (ebpfapi.EbpfHandler, error) {
impl := &ebpfMapsImpl{}
impl.errCounter = make(map[metrics.MetricID]int64)

Expand Down Expand Up @@ -169,7 +121,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 := ebpfapi.InterpreterOffsetKeyValue(ebpfProgIndex, fileID, offsetRanges)
if err != nil {
return err
}
Expand All @@ -181,35 +133,6 @@ func (impl *ebpfMapsImpl) UpdateInterpreterOffsets(ebpfProgIndex uint16, fileID
return nil
}

func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID,
offsetRanges []util.Range) (key uint64, value support.OffsetRange, err error) {
rLen := len(offsetRanges)
if rLen < 1 || rLen > 2 {
return 0, support.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 = support.OffsetRange{
Lower_offset1: first.Start,
Upper_offset1: first.End,
Program_index: 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 = second.Start
value.Upper_offset2 = 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) {
Expand Down Expand Up @@ -492,7 +415,8 @@ 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 []ebpfapi.StackDeltaEBPF) (
uint16, error) {
numDeltas := len(deltas)
mapID, err := getMapID(uint32(numDeltas))
Expand Down
1 change: 1 addition & 0 deletions processmanager/ebpf/ebpf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/ebpf-profiler/support"
)

Expand Down
95 changes: 95 additions & 0 deletions processmanager/ebpfapi/ebpf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package ebpfapi // import "go.opentelemetry.io/ebpf-profiler/processmanager/ebpfapi"

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/support"
"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
}

func InterpreterOffsetKeyValue(ebpfProgIndex uint16, fileID host.FileID,
offsetRanges []util.Range) (key uint64, value support.OffsetRange, err error) {
rLen := len(offsetRanges)
if rLen < 1 || rLen > 2 {
return 0, support.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 = support.OffsetRange{
Lower_offset1: first.Start,
Upper_offset1: first.End,
Program_index: 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 = second.Start
value.Upper_offset2 = second.End
}
return key, value, nil
}
Loading
Loading