Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/integ #2

Merged
merged 18 commits into from
Oct 15, 2024
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/go
ebpf-profiler
ci-kernels
ebpf-profiler
18 changes: 13 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ export GOARCH = $(TARGET_ARCH)
export CC = $(ARCH_PREFIX)gcc
export OBJCOPY = $(ARCH_PREFIX)objcopy

# Add EXTERNAL flag support
ifdef EXTERNAL
export EXTERNAL
GO_TAGS := osusergo,netgo,external_trigger
else
GO_TAGS := osusergo,netgo
endif

BRANCH = $(shell git rev-parse --abbrev-ref HEAD | tr -d '-' | tr '[:upper:]' '[:lower:]')
COMMIT_SHORT_SHA = $(shell git rev-parse --short=8 HEAD)

Expand All @@ -44,10 +52,10 @@ LDFLAGS := -X go.opentelemetry.io/ebpf-profiler/vc.version=$(VERSION) \
-X go.opentelemetry.io/ebpf-profiler/vc.buildTimestamp=$(BUILD_TIMESTAMP) \
-extldflags=-static

GO_TAGS := osusergo,netgo
EBPF_FLAGS :=

GO_FLAGS := -buildvcs=false -ldflags="$(LDFLAGS)"
GO_FLAGS := -buildvcs=false -ldflags="$(LDFLAGS)" -tags $(GO_TAGS)

EBPF_FLAGS :=

MAKEFLAGS += -j$(shell nproc)

Expand All @@ -69,7 +77,7 @@ generate:
go generate ./...

ebpf:
$(MAKE) $(EBPF_FLAGS) -C support/ebpf
$(MAKE) $(EBPF_FLAGS) -j$(shell nproc) -C support/ebpf $(if $(EXTERNAL),EXTERNAL=1,)

ebpf-profiler: generate ebpf
go build $(GO_FLAGS) -tags $(GO_TAGS)
Expand Down Expand Up @@ -120,7 +128,7 @@ docker-image:

agent:
docker run -v "$$PWD":/agent -it --rm --user $(shell id -u):$(shell id -g) profiling-agent \
"make TARGET_ARCH=$(TARGET_ARCH) VERSION=$(VERSION) REVISION=$(REVISION) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP)"
"make TARGET_ARCH=$(TARGET_ARCH) VERSION=$(VERSION) REVISION=$(REVISION) BUILD_TIMESTAMP=$(BUILD_TIMESTAMP) $(if $(EXTERNAL),EXTERNAL=1)"

debug-agent:
docker run -v "$$PWD":/agent -it --rm --user $(shell id -u):$(shell id -g) profiling-agent \
Expand Down
14 changes: 9 additions & 5 deletions cli_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ const (
var (
noKernelVersionCheckHelp = "Disable checking kernel version for eBPF support. " +
"Use at your own risk, to run the agent on older kernels with backported eBPF features."
copyrightHelp = "Show copyright and short license text."
collAgentAddrHelp = "The collection agent address in the format of host:port."
verboseModeHelp = "Enable verbose logging and debugging capabilities."
tracersHelp = "Comma-separated list of interpreter tracers to include."
mapScaleFactorHelp = fmt.Sprintf("Scaling factor for eBPF map sizes. "+
copyrightHelp = "Show copyright and short license text."
collAgentAddrHelp = "The collection agent address in the format of host:port."
verboseModeHelp = "Enable verbose logging and debugging capabilities."
tracersHelp = "Comma-separated list of interpreter tracers to include."
externallyManagedHelp = "Agent is externally managed."
mapScaleFactorHelp = fmt.Sprintf("Scaling factor for eBPF map sizes. "+
"Every increase by 1 doubles the map size. Increase if you see eBPF map size errors. "+
"Default is %d corresponding to 4GB of executable address space, max is %d.",
defaultArgMapScaleFactor, maxArgMapScaleFactor)
Expand Down Expand Up @@ -81,6 +82,7 @@ type arguments struct {
samplesPerSecond int
sendErrorFrames bool
tracers string
externallyManaged bool
verboseMode bool
version bool

Expand Down Expand Up @@ -134,6 +136,8 @@ func parseArgs() (*arguments, error) {
fs.StringVar(&args.tracers, "t", "all", "Shorthand for -tracers.")
fs.StringVar(&args.tracers, "tracers", "all", tracersHelp)

fs.BoolVar(&args.externallyManaged, "externally-managed", false, externallyManagedHelp)

fs.BoolVar(&args.verboseMode, "v", false, "Shorthand for -verbose.")
fs.BoolVar(&args.verboseMode, "verbose", false, verboseModeHelp)
fs.BoolVar(&args.version, "version", false, versionHelp)
Expand Down
29 changes: 18 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,18 +227,25 @@ func mainWithExitCode() exitCode {
metrics.Add(metrics.IDProcPIDStartupMs, metrics.MetricValue(time.Since(now).Milliseconds()))
log.Debug("Completed initial PID listing")

// Attach our tracer to the perf event
if err := trc.AttachTracer(); err != nil {
return failure("Failed to attach to perf event: %v", err)
}
log.Info("Attached tracer program")

if args.probabilisticThreshold < tracer.ProbabilisticThresholdMax {
trc.StartProbabilisticProfiling(mainCtx)
log.Printf("Enabled probabilistic profiling")
if args.externallyManaged {
_, err := trc.GetNativeTracerEntry()
if err != nil {
return failure("Failed to get native tracer entry: %v", err)
}
} else {
if err := trc.EnableProfiling(); err != nil {
return failure("Failed to enable perf events: %v", err)
// Attach our tracer to the perf event
if err := trc.AttachTracer(); err != nil {
return failure("Failed to attach to perf event: %v", err)
}
log.Info("Attached tracer program")

if args.probabilisticThreshold < tracer.ProbabilisticThresholdMax {
trc.StartProbabilisticProfiling(mainCtx)
log.Printf("Enabled probabilistic profiling")
} else {
if err := trc.EnableProfiling(); err != nil {
return failure("Failed to enable perf events: %v", err)
}
}
}

Expand Down
8 changes: 5 additions & 3 deletions process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"fmt"
"io"
"os"
"strconv"
"strings"

"golang.org/x/sys/unix"
Expand Down Expand Up @@ -62,7 +63,7 @@ func trimMappingPath(path string) string {
return path
}

func parseMappings(mapsFile io.Reader) ([]Mapping, error) {
func parseMappings(mapsFile io.Reader, pid libpf.PID) ([]Mapping, error) {
mappings := make([]Mapping, 0)
scanner := bufio.NewScanner(mapsFile)
buf := make([]byte, 512)
Expand Down Expand Up @@ -118,7 +119,8 @@ func parseMappings(mapsFile io.Reader) ([]Mapping, error) {
continue
}
} else {
path = trimMappingPath(path)
// This is needed to support running the profiler from whithin a container.
path = "/proc/" + strconv.Itoa(int(pid)) + "/root/" + trimMappingPath(path)
path = strings.Clone(path)
}

Expand Down Expand Up @@ -148,7 +150,7 @@ func (sp *systemProcess) GetMappings() ([]Mapping, error) {
}
defer mapsFile.Close()

mappings, err := parseMappings(mapsFile)
mappings, err := parseMappings(mapsFile, sp.pid)
if err == nil {
fileToMapping := make(map[string]*Mapping)
for idx := range mappings {
Expand Down
16 changes: 8 additions & 8 deletions process/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var testMappings = `55fe82710000-55fe8273c000 r--p 00000000 fd:01 1068432
7f63c8ebf000-7f63c8fef000 r-xp 0001c000 1fd:01 1075944 /tmp/usr_lib_x86_64-linux-gnu_libopensc.so.6.0.0`

func TestParseMappings(t *testing.T) {
mappings, err := parseMappings(strings.NewReader(testMappings))
mappings, err := parseMappings(strings.NewReader(testMappings), 1)
require.NoError(t, err)
assert.NotNil(t, mappings)

Expand All @@ -37,7 +37,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1068432,
Length: 0x2c000,
FileOffset: 0,
Path: "/tmp/usr_bin_seahorse",
Path: "/proc/1/root//tmp/usr_bin_seahorse",
},
{
Vaddr: 0x55fe8273c000,
Expand All @@ -46,7 +46,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1068432,
Length: 0x82000,
FileOffset: 0x2c000,
Path: "/tmp/usr_bin_seahorse",
Path: "/proc/1/root//tmp/usr_bin_seahorse",
},
{
Vaddr: 0x55fe827be000,
Expand All @@ -55,7 +55,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1068432,
Length: 0x78000,
FileOffset: 0xae000,
Path: "/tmp/usr_bin_seahorse",
Path: "/proc/1/root//tmp/usr_bin_seahorse",
},
{
Vaddr: 0x55fe82836000,
Expand All @@ -64,7 +64,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1068432,
Length: 0x7000,
FileOffset: 0x125000,
Path: "/tmp/usr_bin_seahorse",
Path: "/proc/1/root//tmp/usr_bin_seahorse",
},
{
Vaddr: 0x55fe8283d000,
Expand All @@ -73,7 +73,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1068432,
Length: 0x1000,
FileOffset: 0x12c000,
Path: "/tmp/usr_bin_seahorse",
Path: "/proc/1/root//tmp/usr_bin_seahorse",
},
{
Vaddr: 0x7f63c8c3e000,
Expand All @@ -82,7 +82,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1048922,
Length: 0x1A2000,
FileOffset: 544768,
Path: "/tmp/usr_lib_x86_64-linux-gnu_libcrypto.so.1.1",
Path: "/proc/1/root//tmp/usr_lib_x86_64-linux-gnu_libcrypto.so.1.1",
},
{
Vaddr: 0x7f63c8ebf000,
Expand All @@ -91,7 +91,7 @@ func TestParseMappings(t *testing.T) {
Inode: 1075944,
Length: 0x130000,
FileOffset: 114688,
Path: "/tmp/usr_lib_x86_64-linux-gnu_libopensc.so.6.0.0",
Path: "/proc/1/root//tmp/usr_lib_x86_64-linux-gnu_libopensc.so.6.0.0",
},
}
assert.Equal(t, expected, mappings)
Expand Down
10 changes: 9 additions & 1 deletion support/ebpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ LLC ?= llc-16

DEBUG_FLAGS = -DOPTI_DEBUG -g

# Check if EXTERNAL flag is set
ifdef EXTERNAL
EXTERNAL_FLAG = -DEXTERNAL_TRIGGER
else
EXTERNAL_FLAG =
endif

# Detect native architecture and translate to GOARCH.
NATIVE_ARCH := $(shell uname -m)
ifeq ($(NATIVE_ARCH),x86_64)
Expand Down Expand Up @@ -44,7 +51,8 @@ FLAGS=$(TARGET_FLAGS) \
-Wno-unused-label \
-Wno-unused-parameter \
-Wno-sign-compare \
-fno-stack-protector
-fno-stack-protector \
$(EXTERNAL_FLAG)

SRCS := $(wildcard *.ebpf.c)
OBJS := $(SRCS:.c=.$(BUILD_TYPE).$(TARGET_ARCH).o)
Expand Down
6 changes: 4 additions & 2 deletions support/ebpf/dotnet_tracer.ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "bpfdefs.h"
#include "tracemgmt.h"
#include "types.h"
#include "helpers.h"

// The number of dotnet frames to unwind per frame-unwinding eBPF program.
#define DOTNET_FRAMES_PER_PROGRAM 5
Expand Down Expand Up @@ -244,8 +245,9 @@ ErrorCode unwind_one_dotnet_frame(PerCPURecord *record, DotnetProcInfo *vi, bool
// unwind_dotnet is the entry point for tracing when invoked from the native tracer
// or interpreter dispatcher. It does not reset the trace object and will append the
// dotnet stack frames to the trace object for the current CPU.
SEC("perf_event/unwind_dotnet")
int unwind_dotnet(struct pt_regs *ctx) {
BPF_PROBE(unwind_dotnet)
int unwind_dotnet(struct pt_regs *ctx)
{
PerCPURecord *record = get_per_cpu_record();
if (!record) {
return -1;
Expand Down
13 changes: 13 additions & 0 deletions support/ebpf/helpers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Provide helpers for the eBPF code.

#ifndef OPTI_HELPERS_H
#define OPTI_HELPERS_H

// Macros for BPF program type and context handling.
#ifdef EXTERNAL_TRIGGER
#define BPF_PROBE(name) SEC("kprobe/"#name)
#else
#define BPF_PROBE(name) SEC("perf_event/"#name)
#endif

#endif // OPTI_HELPERS_H
6 changes: 4 additions & 2 deletions support/ebpf/hotspot_tracer.ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "tracemgmt.h"
#include "types.h"
#include "errors.h"
#include "helpers.h"

// Information extracted from a JDK `CodeBlob` instance.
typedef struct CodeBlobInfo {
Expand Down Expand Up @@ -890,8 +891,9 @@ static ErrorCode hotspot_unwind_one_frame(PerCPURecord *record, HotspotProcInfo
// unwind_hotspot is the entry point for tracing when invoked from the native tracer
// and it recursive unwinds all HotSpot frames and then jumps back to unwind further
// native frames that follow.
SEC("perf_event/unwind_hotspot")
int unwind_hotspot(struct pt_regs *ctx) {
BPF_PROBE(unwind_hotspot)
int unwind_hotspot(struct pt_regs *ctx)
{
PerCPURecord *record = get_per_cpu_record();
if (!record)
return -1;
Expand Down
6 changes: 4 additions & 2 deletions support/ebpf/interpreter_dispatcher.ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "types.h"
#include "tracemgmt.h"
#include "tsd.h"
#include "helpers.h"

// Begin shared maps

Expand Down Expand Up @@ -172,8 +173,9 @@ void maybe_add_apm_info(Trace *trace) {
trace->apm_transaction_id.as_int, corr_buf.trace_flags);
}

SEC("perf_event/unwind_stop")
int unwind_stop(struct pt_regs *ctx) {
BPF_PROBE(unwind_stop)
int unwind_stop(struct pt_regs *ctx)
{
PerCPURecord *record = get_per_cpu_record();
if (!record)
return -1;
Expand Down
11 changes: 7 additions & 4 deletions support/ebpf/native_stack_trace.ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "types.h"
#include "tracemgmt.h"
#include "stackdeltatypes.h"
#include "helpers.h"

#ifndef __USER32_CS
// defined in arch/x86/include/asm/segment.h
Expand Down Expand Up @@ -747,8 +748,9 @@ static inline ErrorCode get_usermode_regs(struct pt_regs *ctx,

#endif

SEC("perf_event/unwind_native")
int unwind_native(struct pt_regs *ctx) {
BPF_PROBE(unwind_native)
int unwind_native(struct pt_regs *ctx)
{
PerCPURecord *record = get_per_cpu_record();
if (!record)
return -1;
Expand Down Expand Up @@ -857,7 +859,8 @@ int collect_trace(struct pt_regs *ctx) {
return -1;
}

SEC("perf_event/native_tracer_entry")
int native_tracer_entry(struct bpf_perf_event_data *ctx) {
BPF_PROBE(native_tracer_entry)
int native_tracer_entry(struct bpf_perf_event_data *ctx)
{
return collect_trace((struct pt_regs*) &ctx->regs);
}
6 changes: 4 additions & 2 deletions support/ebpf/perl_tracer.ebpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "tracemgmt.h"
#include "types.h"
#include "tsd.h"
#include "helpers.h"

// The number of Perl frames to unwind per frame-unwinding eBPF program.
#define PERL_FRAMES_PER_PROGRAM 12
Expand Down Expand Up @@ -356,8 +357,9 @@ int walk_perl_stack(PerCPURecord *record, const PerlProcInfo *perlinfo) {
// unwind_perl is the entry point for tracing when invoked from the native tracer
// or interpreter dispatcher. It does not reset the trace object and will append the
// Perl stack frames to the trace object for the current CPU.
SEC("perf_event/unwind_perl")
int unwind_perl(struct pt_regs *ctx) {
BPF_PROBE(unwind_perl)
int unwind_perl(struct pt_regs *ctx)
{
PerCPURecord *record = get_per_cpu_record();
if (!record) {
return -1;
Expand Down
Loading