Skip to content

Commit

Permalink
Merge branch 'main' into extract-lookupcgroup
Browse files Browse the repository at this point in the history
  • Loading branch information
dmathieu authored Nov 19, 2024
2 parents 59a9b7c + 9c8ddce commit 6b675a7
Show file tree
Hide file tree
Showing 18 changed files with 47 additions and 453 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ devfiler spins up a local server that listens on `0.0.0.0:11000`.

To run it, simply download and unpack the archive from the following URL:

https://upload.elastic.co/d/87e7697991940ec37f0c6e39ac38d213f65e8dc1ef9dbedff6aab9cba0adfaba
https://upload.elastic.co/d/f8aa0c386baa808a616ca29f86b34c726edb5af36f8840a4cf28468ad534a4b5

Authentication token: `c74dfc4db2212015`
Authentication token: `2635c0750bf8ea69`


The archive contains a build for each of the following platforms:
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ func (c *Controller) Start(ctx context.Context) error {
traceHandlerCacheSize :=
traceCacheSize(c.config.MonitorInterval, c.config.SamplesPerSecond, uint16(presentCores))

intervals := times.New(c.config.MonitorInterval,
c.config.ReporterInterval, c.config.ProbabilisticInterval)
intervals := times.New(c.config.ReporterInterval, c.config.MonitorInterval,
c.config.ProbabilisticInterval)

// Start periodic synchronization with the realtime clock
times.StartRealtimeSync(ctx, c.config.ClockSyncInterval)
Expand Down
20 changes: 0 additions & 20 deletions libpf/convenience.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,14 @@ package libpf // import "go.opentelemetry.io/ebpf-profiler/libpf"

import (
"context"
"fmt"
"math/rand/v2"
"os"
"reflect"
"time"
"unsafe"

log "github.com/sirupsen/logrus"
)

// WriteTempFile writes a data buffer to a temporary file on the filesystem. It
// is the callers responsibility to clean up that file again. The function returns
// the filename if successful.
func WriteTempFile(data []byte, directory, prefix string) (string, error) {
file, err := os.CreateTemp(directory, prefix)
if err != nil {
return "", err
}
defer file.Close()
if _, err := file.Write(data); err != nil {
return "", fmt.Errorf("failed to write data to temporary file: %w", err)
}
if err := file.Sync(); err != nil {
return "", fmt.Errorf("failed to synchronize file data: %w", err)
}
return file.Name(), nil
}

// SleepWithJitter sleeps for baseDuration +/- jitter (jitter is [0..1])
func SleepWithJitter(baseDuration time.Duration, jitter float64) {
time.Sleep(AddJitter(baseDuration, jitter))
Expand Down
126 changes: 0 additions & 126 deletions libpf/pfelf/pfelf.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,6 @@ import (
"go.opentelemetry.io/ebpf-profiler/libpf"
)

// SafeOpenELF opens the given ELF file in a safely way in that
// it recovers from panics inside elf.Open().
// Under circumstances we see fatal errors from inside the runtime, which
// are not recoverable, e.g. "fatal error: runtime: out of memory".
func SafeOpenELF(name string) (elfFile *elf.File, err error) {
defer func() {
// debug/elf has issues with malformed ELF files
if r := recover(); r != nil {
if elfFile != nil {
elfFile.Close()
elfFile = nil
}
err = fmt.Errorf("failed to open ELF file (recovered from panic): %s", name)
}
}()
return elf.Open(name)
}

// HasDWARFData returns true if the provided ELF file contains actionable DWARF debugging
// information.
// This function does not call `elfFile.DWARF()` on purpose, as it can be extremely expensive in
Expand Down Expand Up @@ -132,41 +114,6 @@ func GetDebugLink(elfFile *elf.File) (linkName string, crc32 int32, err error) {
return ParseDebugLink(sectionData)
}

// GetDebugAltLink returns the contents of the `.gnu_debugaltlink` section (path and build
// ID). If no link is present, ErrNoDebugLink is returned.
func GetDebugAltLink(elfFile *elf.File) (fileName, buildID string, err error) {
// The .gnu_debugaltlink section is not always present
sectionData, err := getSectionData(elfFile, ".gnu_debugaltlink")
if err != nil {
return "", "", ErrNoDebugLink
}

// The whole .gnu_debugaltlink section consists of:
// 1) path to target (variable-length string)
// 2) null character separator (1 byte)
// 3) build ID (usually 20 bytes, but can vary)
//
// First, find the position of the null character:
nullCharIdx := bytes.IndexByte(sectionData, 0)
if nullCharIdx == -1 {
return "", "", nil
}

// The path consists of all the characters before the first null character
path := strings.ToValidUTF8(string(sectionData[:nullCharIdx]), "")

// Check that we can read a build ID: there should be at least 1 byte after the null character.
if nullCharIdx+1 == len(sectionData) {
return "", "", errors.New("malformed .gnu_debugaltlink section (missing build ID)")
}

// The build ID consists of all the bytes after the first null character
buildIDBytes := sectionData[nullCharIdx+1:]
buildID = hex.EncodeToString(buildIDBytes)

return path, buildID, nil
}

var ErrNoBuildID = errors.New("no build ID")
var ubuntuKernelSignature = regexp.MustCompile(` \(Ubuntu[^)]*\)\n$`)

Expand Down Expand Up @@ -303,32 +250,6 @@ func getNoteHexString(sectionBytes []byte, name string, noteType uint32) (
return hex.EncodeToString(sectionBytes[idxDataStart:idxDataEnd]), true, nil
}

// GetLinuxBuildSalt extracts the linux kernel build salt from the provided ELF path.
// It is read from the .notes ELF section.
// It should be present in both kernel modules and the kernel image of most distro-vended kernel
// packages, and should be identical across all the files: kernel modules will have the same salt
// as their corresponding vmlinux image if they were built at the same time.
// This can be used to identify the kernel image corresponding to a module.
// See https://lkml.org/lkml/2018/7/3/1156
func GetLinuxBuildSalt(filePath string) (salt string, found bool, err error) {
elfFile, err := SafeOpenELF(filePath)
if err != nil {
return "", false, fmt.Errorf("could not open %s: %w", filePath, err)
}
defer elfFile.Close()

sectionData, err := getSectionData(elfFile, ".note.Linux")
if err != nil {
sectionData, err = getSectionData(elfFile, ".notes")
if err != nil {
return "", false, nil
}
}

// 0x100 is defined as LINUX_ELFNOTE_BUILD_SALT in include/linux/build-salt.h
return getNoteHexString(sectionData, "Linux", 0x100)
}

func symbolMapFromELFSymbols(syms []elf.Symbol) *libpf.SymbolMap {
symmap := &libpf.SymbolMap{}
for _, sym := range syms {
Expand All @@ -342,16 +263,6 @@ func symbolMapFromELFSymbols(syms []elf.Symbol) *libpf.SymbolMap {
return symmap
}

// GetSymbols gets the symbols of elf.File and returns them as libpf.SymbolMap for
// fast lookup by address and name.
func GetSymbols(elfFile *elf.File) (*libpf.SymbolMap, error) {
syms, err := elfFile.Symbols()
if err != nil {
return nil, err
}
return symbolMapFromELFSymbols(syms), nil
}

// GetDynamicSymbols gets the dynamic symbols of elf.File and returns them as libpf.SymbolMap for
// fast lookup by address and name.
func GetDynamicSymbols(elfFile *elf.File) (*libpf.SymbolMap, error) {
Expand All @@ -362,43 +273,6 @@ func GetDynamicSymbols(elfFile *elf.File) (*libpf.SymbolMap, error) {
return symbolMapFromELFSymbols(syms), nil
}

// IsKernelModule returns true if the provided ELF file looks like a kernel module (an ELF with a
// .modinfo and .gnu.linkonce.this_module sections).
func IsKernelModule(file *elf.File) (bool, error) {
sectionFound, err := HasSection(file, ".modinfo")
if err != nil {
return false, err
}

if !sectionFound {
return false, nil
}

return HasSection(file, ".gnu.linkonce.this_module")
}

// IsKernelImage returns true if the provided ELF file looks like a kernel image (an ELF with a
// __modver section).
func IsKernelImage(file *elf.File) (bool, error) {
return HasSection(file, "__modver")
}

// IsKernelFile returns true if the provided ELF file looks like a kernel file (either a kernel
// image or a kernel module).
func IsKernelFile(file *elf.File) (bool, error) {
isModule, err := IsKernelImage(file)
if err != nil {
return false, err
}

isImage, err := IsKernelModule(file)
if err != nil {
return false, err
}

return isModule || isImage, nil
}

// IsGoBinary returns true if the provided file is a Go binary (= an ELF file with
// a known Golang section).
func IsGoBinary(file *elf.File) (bool, error) {
Expand Down
9 changes: 0 additions & 9 deletions libpf/tracehash.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@ func TraceHashFromBytes(b []byte) (TraceHash, error) {
return TraceHash{h}, nil
}

// TraceHashFromString parses a byte slice of a trace hash into the internal data representation.
func TraceHashFromString(s string) (TraceHash, error) {
h, err := basehash.New128FromString(s)
if err != nil {
return TraceHash{}, err
}
return TraceHash{h}, nil
}

func (h TraceHash) Equal(other TraceHash) bool {
return h.Hash128.Equal(other.Hash128)
}
Expand Down
2 changes: 2 additions & 0 deletions processmanager/ebpf/ebpf.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ var outerMapsName = [...]string{
"exe_id_to_19_stack_deltas",
"exe_id_to_20_stack_deltas",
"exe_id_to_21_stack_deltas",
"exe_id_to_22_stack_deltas",
"exe_id_to_23_stack_deltas",
}

// Compile time check to make sure ebpfMapsImpl satisfies the interface .
Expand Down
11 changes: 5 additions & 6 deletions processmanager/ebpf/ebpf_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
//go:build !integration
// +build !integration

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

Expand All @@ -12,6 +9,7 @@ import (

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/ebpf-profiler/support"
)

func TestMapID(t *testing.T) {
Expand All @@ -26,18 +24,19 @@ func TestMapID(t *testing.T) {
0x3FF: 10, // 1023
0x400: 11, // 1024
0xFFFFF: 20, // 1048575 (2^20 - 1)
(1 << support.StackDeltaBucketLargest) - 1: support.StackDeltaBucketLargest,
}
for numStackDeltas, expectedShift := range testCases {
numStackDeltas := numStackDeltas
expectedShift := expectedShift
t.Run(fmt.Sprintf("deltas %d", numStackDeltas), func(t *testing.T) {
shift, err := getMapID(numStackDeltas)
require.NoError(t, err)
assert.Equal(t, expectedShift, shift,
fmt.Sprintf("wrong map name for %d deltas", numStackDeltas))
assert.Equal(t, expectedShift, shift, "wrong map name for %d deltas",
numStackDeltas)
})
}

_, err := getMapID(1 << 22)
_, err := getMapID(1 << (support.StackDeltaBucketLargest + 1))
require.Error(t, err)
}
Loading

0 comments on commit 6b675a7

Please sign in to comment.