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
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
uses: ./.github/workflows/env

- name: Initialize CodeQL
uses: github/codeql-action/init@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.10
uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
languages: go

Expand All @@ -37,7 +37,7 @@ jobs:
make TARGET_ARCH=${{ matrix.target_arch }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.10
uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
category: "/language:Go"
timeout-minutes: 10
2 changes: 1 addition & 1 deletion .github/workflows/ossf-scorecard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard (optional).
# Commenting out will disable upload of results to your repo's Code Scanning dashboard
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@96f518a34f7a870018057716cc4d7a5c014bd61c # v3.29.10
uses: github/codeql-action/upload-sarif@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with:
sarif_file: results.sarif
1 change: 1 addition & 0 deletions .github/workflows/push-docker-image.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
name: "Update builder docker image"

on:
workflow_dispatch: # Allows manual triggering of the workflow
push:
branches: ["main"]
paths:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit-test-on-pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ jobs:
check-binary-blobs:
name: Check for differences in the eBPF binary blobs
runs-on: ubuntu-24.04
container: otel/opentelemetry-ebpf-profiler-dev:latest@sha256:db6081344e85ef95317b19dbf667d56df35812353b23d0fd54e1db0f55436b80
container: otel/opentelemetry-ebpf-profiler-dev:latest@sha256:6ab9b5ff6c2a457be97a389887caf9f3cd5344f760fdab0101b9965236bbb2db
defaults:
run:
shell: bash --login {0}
Expand Down
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions cli_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ var (
defaultOffCPUThreshold)
envVarsHelp = "Comma separated list of environment variables that will be reported with the" +
"captured profiling samples."
probeLinkHelper = "Attach a probe to a symbol of an executable. " +
"Expected format: /path/to/executable:symbol"
loadProbeHelper = "Load generic eBPF program that can be attached externally to " +
"various user or kernel space hooks."
)

// Package-scope variable, so that conditionally compiled other components can refer
Expand Down Expand Up @@ -127,6 +131,13 @@ func parseArgs() (*controller.Config, error) {

fs.StringVar(&args.IncludeEnvVars, "env-vars", defaultEnvVarsValue, envVarsHelp)

fs.Func("uprobe-link", probeLinkHelper, func(link string) error {
args.UProbeLinks = append(args.UProbeLinks, link)
return nil
})

fs.BoolVar(&args.LoadProbe, "load-probe", false, loadProbeHelper)

fs.Usage = func() {
fs.PrintDefaults()
}
Expand Down
2 changes: 2 additions & 0 deletions internal/controller/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ type Config struct {
VerboseMode bool
Version bool
OffCPUThreshold float64
UProbeLinks []string
LoadProbe bool

Reporter reporter.Reporter

Expand Down
9 changes: 9 additions & 0 deletions internal/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ func (c *Controller) Start(ctx context.Context) error {
ProbabilisticThreshold: c.config.ProbabilisticThreshold,
OffCPUThreshold: uint32(c.config.OffCPUThreshold * float64(math.MaxUint32)),
IncludeEnvVars: envVars,
UProbeLinks: c.config.UProbeLinks,
LoadProbe: c.config.LoadProbe,
})
if err != nil {
return fmt.Errorf("failed to load eBPF tracer: %w", err)
Expand Down Expand Up @@ -125,6 +127,13 @@ func (c *Controller) Start(ctx context.Context) error {
log.Printf("Enabled off-cpu profiling with p=%f", c.config.OffCPUThreshold)
}

if len(c.config.UProbeLinks) > 0 {
if err := trc.AttachUProbes(c.config.UProbeLinks); err != nil {
return fmt.Errorf("failed to attach uprobes: %v", err)
}
log.Printf("Attached uprobes")
}

if c.config.ProbabilisticThreshold < tracer.ProbabilisticThresholdMax {
trc.StartProbabilisticProfiling(ctx)
log.Printf("Enabled probabilistic profiling")
Expand Down
3 changes: 1 addition & 2 deletions interpreter/apmint/apmint.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"go.opentelemetry.io/ebpf-profiler/host"
"go.opentelemetry.io/ebpf-profiler/interpreter"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
"go.opentelemetry.io/ebpf-profiler/remotememory"
"go.opentelemetry.io/ebpf-profiler/support"
)
Expand Down Expand Up @@ -65,7 +64,7 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete
// Resolve process storage symbol.
procStorageSym, err := ef.LookupSymbol(procStorageExport)
if err != nil {
if errors.Is(err, pfelf.ErrSymbolNotFound) {
if errors.Is(err, libpf.ErrSymbolNotFound) {
// APM<->profiling integration not supported by agent.
return nil, nil
}
Expand Down
3 changes: 1 addition & 2 deletions interpreter/customlabels/customlabels.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (

"go.opentelemetry.io/ebpf-profiler/interpreter"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
"go.opentelemetry.io/ebpf-profiler/remotememory"
"go.opentelemetry.io/ebpf-profiler/support"
)
Expand Down Expand Up @@ -44,7 +43,7 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete
}
abiVersionSym, err := ef.LookupSymbol(abiVersionExport)
if err != nil {
if errors.Is(err, pfelf.ErrSymbolNotFound) {
if errors.Is(err, libpf.ErrSymbolNotFound) {
return nil, nil
}

Expand Down
2 changes: 1 addition & 1 deletion interpreter/golabels/golabels.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func Loader(_ interpreter.EbpfHandler, info *interpreter.LoaderInfo) (interprete
log.Debugf("file %s detected as go version %s", info.FileName(), goVersion)

offsets := getOffsets(goVersion)
tlsOffset, err := extractTLSGOffset(file, info.FileName())
tlsOffset, err := extractTLSGOffset(file)
if err != nil {
return nil, fmt.Errorf("failed to extract TLS offset: %w", err)
}
Expand Down
15 changes: 8 additions & 7 deletions interpreter/golabels/tls_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,27 @@ package golabels // import "go.opentelemetry.io/ebpf-profiler/interpreter/golabe
import (
log "github.com/sirupsen/logrus"
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
"go.opentelemetry.io/ebpf-profiler/nativeunwind/elfunwindinfo"
"golang.org/x/arch/x86/x86asm"
)

// Most normal amd64 Go binaries use -8 as offset into TLS space for
// storing the current g but "static" binaries it ends up as -80. There
// may be dynamic relocating going on so just read it from a known
// symbol if possible.
func extractTLSGOffset(f *pfelf.File, path string) (int32, error) {
syms, err := f.ReadSymbols()
func extractTLSGOffset(f *pfelf.File) (int32, error) {
pclntab, err := elfunwindinfo.NewGopclntab(f)
if err != nil {
log.Debugf("Failed to find symbols (%v) using default TLSG offset", err)
return -8, nil
}
defer pclntab.Close()

// Dump of assembler code for function runtime.stackcheck:
// 0x0000000000470080 <+0>: mov %fs:0xfffffffffffffff8,%rax
sym, err := syms.LookupSymbol("runtime.stackcheck.abi0")
sym, err := pclntab.LookupSymbol("runtime.stackcheck")
if err != nil {
// Binary must be stripped, hope default is correct and warn.
log.Warnf("Failed to find stackcheck symbol, Go labels might not work: %v (%s)", err, path)
return -8, nil
return 0, err
}
b, err := f.VirtualMemory(int64(sym.Address), 16, 16)
if err != nil {
Expand Down Expand Up @@ -62,6 +63,6 @@ func extractTLSGOffset(f *pfelf.File, path string) (int32, error) {
}
}
exit:
log.Warnf("Failed to decode stackcheck symbol, Go label collection might not work %s", path)
log.Warnf("Failed to decode stackcheck symbol, Go label collection might not work")
return -8, nil
}
4 changes: 2 additions & 2 deletions interpreter/golabels/tls_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
// 0x000000000007f270 <+16>: mov x27, #0x30 // #48
// 0x000000000007f274 <+20>: ldr x28, [x0, x27]
// 0x000000000007f278 <+24>: ret
func extractTLSGOffset(f *pfelf.File, path string) (int32, error) {
func extractTLSGOffset(f *pfelf.File) (int32, error) {
iscgo, err := f.IsCgoEnabled()
if err != nil || !iscgo {
return 0, err
Expand Down Expand Up @@ -55,6 +55,6 @@ func extractTLSGOffset(f *pfelf.File, path string) (int32, error) {
}
}
}
log.Warnf("Failed to decode load_g symbol, Go label collection might not work with CGO frames (%s)", path)
log.Warnf("Failed to decode load_g symbol, Go label collection might not work with CGO frames")
return 0, nil
}
2 changes: 1 addition & 1 deletion interpreter/luajit/offsets.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ func (o *offsetData) readSym(sym *libpf.Symbol) ([]byte, error) {

func (o *offsetData) lookupSymbol(name libpf.SymbolName) (s *libpf.Symbol, err error) {
s, err = o.f.LookupSymbol(name)
if err == pfelf.ErrSymbolNotFound && o.syms != nil {
if err == libpf.ErrSymbolNotFound && o.syms != nil {
s, err = o.syms.LookupSymbol(name)
}
if s == nil && o.dsyms != nil {
Expand Down
9 changes: 3 additions & 6 deletions libpf/pfelf/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ const (

// List of public errors.
var (
// ErrSymbolNotFound is returned when the requested symbol was not found.
ErrSymbolNotFound = errors.New("symbol not found")

// ErrNotELF is returned when the file is not an ELF file.
ErrNotELF = errors.New("not an ELF file")
)
Expand Down Expand Up @@ -1050,7 +1047,7 @@ func (f *File) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) {
mask := uint(1)<<(h%ptrSizeBits) |
uint(1)<<((h>>hdr.bloomShift)%ptrSizeBits)
if bloom&mask != mask {
return nil, ErrSymbolNotFound
return nil, libpf.ErrSymbolNotFound
}

// Read the initial symbol index to start looking from
Expand All @@ -1061,7 +1058,7 @@ func (f *File) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) {
return nil, err
}
if i == 0 {
return nil, ErrSymbolNotFound
return nil, libpf.ErrSymbolNotFound
}

// Search the hash bucket
Expand Down Expand Up @@ -1116,7 +1113,7 @@ func (f *File) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) {
return nil, errors.New("symbol hash not present")
}

return nil, ErrSymbolNotFound
return nil, libpf.ErrSymbolNotFound
}

// LookupSymbol searches for a given symbol in the ELF
Expand Down
7 changes: 7 additions & 0 deletions libpf/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@
package libpf // import "go.opentelemetry.io/ebpf-profiler/libpf"

import (
"errors"
"fmt"
"sort"
"strings"
)

// List of public errors.
var (
// ErrSymbolNotFound is returned when the requested symbol was not found.
ErrSymbolNotFound = errors.New("symbol not found")
)

// SymbolValue represents the value associated with a symbol, e.g. either an
// offset or an absolute address
type SymbolValue uint64
Expand Down
25 changes: 25 additions & 0 deletions nativeunwind/elfunwindinfo/elfgopclntab.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"strings"
"unsafe"

"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/libpf/pfelf"
sdtypes "go.opentelemetry.io/ebpf-profiler/nativeunwind/stackdeltatypes"
"go.opentelemetry.io/ebpf-profiler/support"
Expand Down Expand Up @@ -351,6 +352,30 @@ type Gopclntab struct {
functab, funcdata, funcnametab, filetab, pctab, cutab []byte
}

// LookupSymbol searches for a given symbol in .gopclntab.
func (g *Gopclntab) LookupSymbol(symbol libpf.SymbolName) (*libpf.Symbol, error) {
symString := string(symbol)
for i := 0; i < g.numFuncs; i++ {
_, funcOff := g.getFuncMapEntry(i)
pc, fun := g.getFunc(funcOff)
if fun == nil {
continue
}
name := getString(g.funcnametab, int(fun.nameOff))
if name == symString {
nextPc, _ := g.getFuncMapEntry(i + 1)
size := uint64(nextPc - pc)

return &libpf.Symbol{
Name: symbol,
Address: libpf.SymbolValue(pc),
Size: size,
}, nil
}
}
return nil, libpf.ErrSymbolNotFound
}

// NewGopclntab parses and returns the parsed data for further operations.
func NewGopclntab(ef *pfelf.File) (*Gopclntab, error) {
data, err := extractGoPclntab(ef)
Expand Down
31 changes: 6 additions & 25 deletions reporter/base_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@
package reporter // import "go.opentelemetry.io/ebpf-profiler/reporter"

import (
"context"
"errors"
"fmt"
"time"

lru "github.com/elastic/go-freelru"
"go.opentelemetry.io/ebpf-profiler/libpf"
"go.opentelemetry.io/ebpf-profiler/libpf/xsync"
"go.opentelemetry.io/ebpf-profiler/reporter/internal/pdata"
Expand All @@ -35,9 +32,6 @@ type baseReporter struct {

// traceEvents stores reported trace events (trace metadata with frames and counts)
traceEvents xsync.RWMutex[samples.TraceEventsTree]

// hostmetadata stores metadata that is sent out with every request.
hostmetadata *lru.SyncedLRU[string, string]
}

var errUnknownOrigin = errors.New("unknown trace origin")
Expand All @@ -46,23 +40,6 @@ func (b *baseReporter) Stop() {
b.runLoop.Stop()
}

func (b *baseReporter) ReportHostMetadata(metadataMap map[string]string) {
b.addHostmetadata(metadataMap)
}

func (b *baseReporter) ReportHostMetadataBlocking(_ context.Context,
metadataMap map[string]string, _ int, _ time.Duration) error {
b.addHostmetadata(metadataMap)
return nil
}

// addHostmetadata adds to and overwrites host metadata.
func (b *baseReporter) addHostmetadata(metadataMap map[string]string) {
for k, v := range metadataMap {
b.hostmetadata.Add(k, v)
}
}

func (b *baseReporter) ExecutableKnown(fileID libpf.FileID) bool {
_, known := b.pdata.Executables.GetAndRefresh(fileID, pdata.ExecutableCacheLifetime)
return known
Expand All @@ -76,8 +53,11 @@ func (b *baseReporter) ExecutableMetadata(args *ExecutableMetadataArgs) {
}

func (b *baseReporter) ReportTraceEvent(trace *libpf.Trace, meta *samples.TraceEventMeta) error {
if meta.Origin != support.TraceOriginSampling && meta.Origin != support.TraceOriginOffCPU {
// At the moment only on-CPU and off-CPU traces are reported.
switch meta.Origin {
case support.TraceOriginSampling:
case support.TraceOriginOffCPU:
case support.TraceOriginUProbe:
default:
return fmt.Errorf("skip reporting trace for %d origin: %w", meta.Origin,
errUnknownOrigin)
}
Expand Down Expand Up @@ -124,6 +104,7 @@ func (b *baseReporter) ReportTraceEvent(trace *libpf.Trace, meta *samples.TraceE
Timestamps: []uint64{uint64(meta.Timestamp)},
OffTimes: []int64{meta.OffTime},
EnvVars: meta.EnvVars,
Labels: trace.CustomLabels,
}
return nil
}
Loading