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

feat(ebpf): pythonless, portable ebpf #1314

Merged
merged 59 commits into from
Aug 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
7816559
pythonless ebpfspy
korniltsev Jul 22, 2022
8164367
libbpf
korniltsev Jul 25, 2022
52fc163
tmp
korniltsev Jul 26, 2022
3b45faf
ebpf makefile & dockerfile
korniltsev Jul 27, 2022
bc481fa
bring back pid filter
korniltsev Jul 29, 2022
9b0f57f
commit profile.bpf.o, vmlinux.h
korniltsev Aug 4, 2022
f586519
listen for dead processes and remove caches, also add lru cache
korniltsev Aug 4, 2022
2c97259
cleanup pid_exits read
korniltsev Aug 4, 2022
fc48284
fix mem leak
korniltsev Aug 7, 2022
66bbbdf
rm pid_exit listener, cleanup
korniltsev Aug 9, 2022
0a37993
batch delete from counts map, tmp disable sym resolve
korniltsev Aug 9, 2022
6658a1d
more debug logging
korniltsev Aug 9, 2022
93c83b3
moar debug logs
korniltsev Aug 9, 2022
1ed2b7f
fix mem leak
korniltsev Aug 9, 2022
f972c73
moar debug logs
korniltsev Aug 9, 2022
f5a7c27
tmp disable symbols
korniltsev Aug 9, 2022
197f413
moar debug logs
korniltsev Aug 9, 2022
0264372
moar debug logs
korniltsev Aug 9, 2022
a45556b
moar debug logs
korniltsev Aug 9, 2022
b3adc6e
moar debug logs
korniltsev Aug 9, 2022
a0e1b9b
fix: call fstat less
korniltsev Aug 9, 2022
79e3f57
k8s labels
korniltsev Aug 11, 2022
f1950aa
fixbug
korniltsev Aug 11, 2022
0da97fe
bufix
korniltsev Aug 11, 2022
cf637fb
bufix
korniltsev Aug 11, 2022
238c050
bufix
korniltsev Aug 11, 2022
bdd204e
moar tags
korniltsev Aug 11, 2022
21f6a93
Merge remote-tracking branch 'origin/main' into feat/pythonless_ebpf
korniltsev Aug 11, 2022
f078391
tmp
korniltsev Aug 15, 2022
734281c
rm vmlinux & ebpf.o
korniltsev Aug 15, 2022
d10ef78
do not commit vmlinux.h & profile.bpf.o
korniltsev Aug 16, 2022
f7d664a
fix
korniltsev Aug 16, 2022
65bf0ee
containerd
korniltsev Aug 16, 2022
715d3d3
parsing containerd cgroup
korniltsev Aug 16, 2022
6115338
rework buildscripts
korniltsev Aug 16, 2022
ddc56a3
linter
korniltsev Aug 16, 2022
35eb43f
linter
korniltsev Aug 16, 2022
2d0482a
try to use upstream revive temporarily
korniltsev Aug 16, 2022
be03880
try to use golang 1.19 revive
korniltsev Aug 16, 2022
8c0942d
try to fail linter
korniltsev Aug 16, 2022
7a71cdc
revert $(EXTRA_GOBUILD_ARGS)
korniltsev Aug 16, 2022
c188d9f
Revert " try to fail linter"
korniltsev Aug 16, 2022
cc06650
separate command for ebpfspy
korniltsev Aug 16, 2022
f955894
linter
korniltsev Aug 16, 2022
5aa9e95
rm unused file
korniltsev Aug 16, 2022
d0f266c
Update cmd/pyroscope/command/ebpfspy.go
korniltsev Aug 18, 2022
115e4a0
Update pkg/config/config.go
korniltsev Aug 18, 2022
02c5669
Update pkg/config/config.go
korniltsev Aug 18, 2022
ffd46e6
fixes
korniltsev Aug 18, 2022
daad730
fixes
korniltsev Aug 18, 2022
87606b7
fixes
korniltsev Aug 18, 2022
d09170a
fixes
korniltsev Aug 18, 2022
b9cdb5c
debug log
korniltsev Aug 18, 2022
4997ed3
moar debug logs
korniltsev Aug 18, 2022
7a66e00
rename src
korniltsev Aug 18, 2022
863d413
fix high numbers bug
korniltsev Aug 18, 2022
bf9c4c3
ebpf OnlyServices flag
korniltsev Aug 18, 2022
36f64fa
Merge remote-tracking branch 'origin/main' into feat/pythonless_ebpf
korniltsev Aug 18, 2022
295aba3
linter
korniltsev Aug 18, 2022
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
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,10 @@ benchmark/docker-compose.yml
benchmark/grafana-*
benchmark/node_modules
benchmark/*.env

third_party/bcc/lib
third_party/bcc/src
third_party/libbpf/lib
third_party/libbpf/src
pkg/agent/ebpfspy/bpf/profile.bpf.o
pkg/agent/ebpfspy/bpf/vmlinux.h
2 changes: 1 addition & 1 deletion .github/workflows/lint-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- name: Run revive action
uses: petethepig/revive-action@v5
uses: korniltsev/revive-action@v6
with:
config: revive.toml
# same as in the `lint` rule of Makefile
Expand Down
28 changes: 25 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ ARG EXTRA_METADATA=""

RUN EXTRA_METADATA=$EXTRA_METADATA make assets-release



# _ __
# | | / _|
# ___| |__ _ __ | |_
# / _ \ '_ \| '_ \| _|
# | __/ |_) | |_) | |
# \___|_.__/| .__/|_|
# | |
# |_|
FROM alpine:3.12 as ebpf-builder
RUN apk add cmake make binutils gcc g++ clang musl-dev linux-headers zlib-dev elfutils-dev libelf-static zlib-static git openssh
ADD third_party/libbpf/Makefile /build/libbpf/
RUN make -C /build/libbpf/
ADD third_party/bcc/Makefile /build/bcc/
RUN make -C /build/bcc/
ADD pkg/agent/ebpfspy/bpf/Makefile pkg/agent/ebpfspy/bpf/profile.bpf.c pkg/agent/ebpfspy/bpf/profile.bpf.h /build/profile.bpf/
RUN CFLAGS=-I/build/libbpf/lib/include make -C /build/profile.bpf

# _
# | |
# __ _ ___ | | __ _ _ __ __ _
Expand All @@ -84,8 +103,8 @@ RUN EXTRA_METADATA=$EXTRA_METADATA make assets-release
# see https://github.com/pyroscope-io/pyroscope/pull/372 for more context
FROM pyroscope/golang:1.18.0-alpine3.12 AS go-builder


RUN apk add --no-cache make git zstd gcc g++ libc-dev musl-dev bash
RUN apk add --no-cache make git zstd gcc g++ libc-dev musl-dev bash zlib-dev elfutils-dev libelf-static zlib-static \
linux-headers
RUN apk upgrade binutils
RUN apk upgrade elfutils

Expand All @@ -98,6 +117,9 @@ COPY third_party/rustdeps/pyspy.h /opt/pyroscope/third_party/rustdeps/pyspy.h
COPY third_party/phpspy/phpspy.h /opt/pyroscope/third_party/phpspy/phpspy.h
COPY --from=phpspy-builder /var/www/html/third_party/phpspy/libphpspy.a /opt/pyroscope/third_party/phpspy/libphpspy.a
COPY --from=js-builder /opt/pyroscope/webapp/public ./webapp/public
COPY --from=ebpf-builder /build/bcc/lib third_party/bcc/lib
COPY --from=ebpf-builder /build/libbpf/lib third_party/libbpf/lib
COPY --from=ebpf-builder /build/profile.bpf/profile.bpf.o pkg/agent/ebpfspy/bpf/profile.bpf.o
COPY Makefile ./
COPY tools ./tools
COPY go.mod go.sum ./
Expand Down Expand Up @@ -159,7 +181,7 @@ LABEL maintainer="Pyroscope team <hello@pyroscope.io>"
WORKDIR /var/lib/pyroscope

RUN apk add --no-cache ca-certificates bash tzdata openssl musl-utils
RUN apk add --no-cache bcc-tools python3

RUN ln -s $(which python3) /usr/bin/python

RUN addgroup -S pyroscope && adduser -S pyroscope -G pyroscope
Expand Down
34 changes: 32 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,21 @@ else
endif

EXTRA_GO_TAGS ?=
CGO_CFLAGS ?=
CGO_LDFLAGS ?=
EXTRA_CGO_CFLAGS ?=
EXTRA_CGO_LDFLAGS ?=
GO_TAGS = $(ENABLED_SPIES)$(EXTRA_GO_TAGS)
ALPINE_TAG =

ifneq (,$(findstring ebpfspy,$(GO_TAGS)))
EXTRA_CGO_CFLAGS := $(EXTRA_CGO_CFLAGS) -I$(abspath ./third_party/libbpf/lib/include) \
-I$(abspath ./third_party/bcc/lib/include)
EXTRA_CGO_LDFLAGS := $(EXTRA_CGO_LDFLAGS) -L$(abspath ./third_party/libbpf/lib/lib64) -lbpf \
-L$(abspath ./third_party/bcc/lib/lib) -lbcc-syms -lstdc++ -lelf -lz
THIRD_PARTY_DEPENDENCIES := $(THIRD_PARTY_DEPENDENCIES) build-profile-bpf build-bcc build-libbpf
endif

ifeq ("$(OS)", "Linux")
ifeq ("$(shell cat /etc/os-release | grep ^ID=)", "ID=alpine")
RUST_TARGET ?= "$(ARCH)-unknown-linux-musl"
Expand All @@ -56,7 +68,7 @@ else
endif

EMBEDDED_ASSETS_DEPS ?= "assets-release"
EXTRA_LDFLAGS ?= ""
EXTRA_LDFLAGS ?=

ifndef $(GOPATH)
GOPATH=$(shell go env GOPATH || true)
Expand Down Expand Up @@ -106,7 +118,9 @@ install-go-dependencies: ## installs golang dependencies

.PHONY: build
build: ## Builds the binary
$(GOBUILD) -tags "$(GO_TAGS)" -ldflags "$(EXTRA_LDFLAGS) $(shell scripts/generate-build-flags.sh)" -o ./bin/pyroscope ./cmd/pyroscope
CGO_CFLAGS="$(CGO_CFLAGS) $(EXTRA_CGO_CFLAGS)" \
CGO_LDFLAGS="$(CGO_LDFLAGS) $(EXTRA_CGO_LDFLAGS)" \
$(GOBUILD) -tags "$(GO_TAGS)" -ldflags "$(EXTRA_LDFLAGS) $(shell scripts/generate-build-flags.sh)" -o ./bin/pyroscope ./cmd/pyroscope

.PHONY: build-release
build-release: embedded-assets ## Builds the release build
Expand Down Expand Up @@ -136,6 +150,19 @@ build-phpspy-dependencies: ## Builds the PHP dependency
cd third_party/phpspy_src && USE_ZEND=1 make CFLAGS="-DUSE_DIRECT" || $(MAKE) print-deps-error-message
cp third_party/phpspy_src/libphpspy.a third_party/phpspy/libphpspy.a

.PHONY: build-libbpf
build-libbpf:
$(MAKE) -C third_party/libbpf

.PHONY: build-bcc
build-bcc:
$(MAKE) -C third_party/bcc

.PHONY: build-profile-bpf
build-profile-bpf: build-libbpf
CFLAGS="-I$(abspath ./third_party/libbpf/lib/include)" $(MAKE) -C pkg/agent/ebpfspy/bpf


.PHONY: build-third-party-dependencies
build-third-party-dependencies: $(shell echo $(THIRD_PARTY_DEPENDENCIES)) ## Builds third party dep

Expand Down Expand Up @@ -236,6 +263,9 @@ go-deps-graph: ## Generate the deps graph
.PHONY: clean
clean: ## Clean up storage
rm -rf tmp/pyroscope-storage
$(MAKE) -C third_party/bcc clean
$(MAKE) -C third_party/libbpf clean
$(MAKE) -C pkg/agent/ebpfspy/bpf clean

.PHONY: update-contributors
update-contributors: ## Update the contributors
Expand Down
27 changes: 27 additions & 0 deletions cmd/pyroscope/command/ebpf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//go:build ebpfspy

package command

import (
"github.com/spf13/cobra"

"github.com/pyroscope-io/pyroscope/pkg/cli"
"github.com/pyroscope-io/pyroscope/pkg/config"
"github.com/pyroscope-io/pyroscope/pkg/exec"
)

func newEBPFSpyCmd(cfg *config.EBPF) *cobra.Command {
vpr := newViper()
connectCmd := &cobra.Command{
Use: "ebpf [flags]",
Short: "Profile whole system using eBPF sampling profiler",
Args: cobra.NoArgs,

RunE: cli.CreateCmdRunFn(cfg, vpr, func(_ *cobra.Command, _ []string) error {
return exec.RunEBPF(cfg)
}),
}

cli.PopulateFlagSet(cfg, connectCmd.Flags(), vpr)
return connectCmd
}
13 changes: 13 additions & 0 deletions cmd/pyroscope/command/ebpf_stub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build !ebpfspy

package command

import (
"github.com/spf13/cobra"

"github.com/pyroscope-io/pyroscope/pkg/config"
)

func newEBPFSpyCmd(_ *config.EBPF) *cobra.Command {
return nil
}
4 changes: 4 additions & 0 deletions cmd/pyroscope/command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func Execute() error {
newAdminCmd(&cfg.Admin),
newAgentCmd(&cfg.Agent),
newConnectCmd(&cfg.Connect),
newEBPFSpyCmd(&cfg.EBPF),
newConvertCmd(&cfg.Convert),
newDbManagerCmd(&config.CombinedDbManager{DbManager: &cfg.DbManager, Server: &cfg.Server}),
newExecCmd(&cfg.Exec),
Expand All @@ -29,6 +30,9 @@ func Execute() error {
}

for _, c := range subcommands {
if c == nil {
continue
}
addHelpSubcommand(c)
c.HasHelpSubCommands()
rootCmd.AddCommand(c)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/pyroscope-io/pyroscope
go 1.18

require (
github.com/aquasecurity/libbpfgo v0.3.0-libbpf-0.8.0
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/aws/aws-sdk-go v1.44.37
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aquasecurity/libbpfgo v0.3.0-libbpf-0.8.0 h1:NQEf484vQOshZwZOLTE7kzo62TvYrM906gUjlVg4D2k=
github.com/aquasecurity/libbpfgo v0.3.0-libbpf-0.8.0/go.mod h1:qu0TVGRvtNMFkuKLscJkY1FwmageNBLqeImAFslqPPc=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
Expand Down Expand Up @@ -1015,6 +1017,7 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
5 changes: 5 additions & 0 deletions pkg/agent/ebpfspy/bpf/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
libbpf*
bcc-src
libs/
profile.bpf.o
vmlinux.h
14 changes: 14 additions & 0 deletions pkg/agent/ebpfspy/bpf/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
CLANG ?= clang

CFLAGS := $(CFLAGS) -ggdb -gdwarf -O2 -Wall -fpie -Wno-unused-variable -Wno-unused-function


profile.bpf.o: profile.bpf.c profile.bpf.h vmlinux.h
$(CLANG) $(CFLAGS) -target bpf -D__TARGET_ARCH_x86 -I. -c profile.bpf.c -o $@

vmlinux.h:
wget https://raw.githubusercontent.com/iovisor/bcc/798a1056903b57687fd9d30a43c64c7a4402934c/libbpf-tools/x86/vmlinux_518.h -O $@

.PHONY: clean
clean:
rm -rf profile.bpf.o vmlinux.h
65 changes: 65 additions & 0 deletions pkg/agent/ebpfspy/bpf/profile.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "profile.bpf.h"

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct profile_key_t);
__type(value, u32);
__uint(max_entries, PROFILE_MAPS_SIZE);
} counts SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_STACK_TRACE);
__uint(key_size, sizeof(u32));
__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
__uint(max_entries, PROFILE_MAPS_SIZE);
} stacks SEC(".maps");


struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, struct profile_bss_args_t);
__uint(max_entries, 1);
} args SEC(".maps");

#define KERN_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP)
#define USER_STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP | BPF_F_USER_STACK)



SEC("perf_event")
int do_perf_event(struct bpf_perf_event_data *ctx)
{
u64 id = bpf_get_current_pid_tgid();
u32 tgid = id >> 32;
u32 pid = id;
struct profile_key_t key = { .pid = tgid };
u32 *val, one = 1, zero = 0;
struct profile_bss_args_t *arg = bpf_map_lookup_elem(&args, &zero);
if (!arg) {
return 0;
}
if (pid == 0) {
return 0;
}
if (arg->tgid_filter != 0 && tgid != arg->tgid_filter) {
return 0;
}

bpf_get_current_comm(&key.comm, sizeof(key.comm));

key.kern_stack = bpf_get_stackid(ctx, &stacks, KERN_STACKID_FLAGS);
key.user_stack = bpf_get_stackid(ctx, &stacks, USER_STACKID_FLAGS);

val = bpf_map_lookup_elem(&counts, &key);
if (val)
(*val)++;
else
bpf_map_update_elem(&counts, &key, &one, BPF_NOEXIST);
return 0;
}

char _license[] SEC("license") = "GPL"; //todo
15 changes: 15 additions & 0 deletions pkg/agent/ebpfspy/bpf/profile.bpf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

#define PERF_MAX_STACK_DEPTH 127
#define PROFILE_MAPS_SIZE 16384


struct profile_key_t {
__u32 pid;
__s64 kern_stack;
__s64 user_stack;
char comm[16];
};

struct profile_bss_args_t {
__u32 tgid_filter; // 0 => profile everything
};
43 changes: 43 additions & 0 deletions pkg/agent/ebpfspy/cpuonline/cpuonline.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cpuonline

import (
"io/ioutil"
"strconv"
"strings"
)

const cpuOnline = "/sys/devices/system/cpu/online"

// Get returns a slice with the online CPUs, for example `[0, 2, 3]`
func Get() ([]uint, error) {
buf, err := ioutil.ReadFile(cpuOnline)
if err != nil {
return nil, err
}
return ReadCPURange(string(buf))
}

// loosely based on https://github.com/iovisor/bcc/blob/v0.3.0/src/python/bcc/utils.py#L15
func ReadCPURange(cpuRangeStr string) ([]uint, error) {
var cpus []uint
cpuRangeStr = strings.Trim(cpuRangeStr, "\n ")
for _, cpuRange := range strings.Split(cpuRangeStr, ",") {
rangeOp := strings.SplitN(cpuRange, "-", 2)
first, err := strconv.ParseUint(rangeOp[0], 10, 32)
if err != nil {
return nil, err
}
if len(rangeOp) == 1 {
cpus = append(cpus, uint(first))
continue
}
last, err := strconv.ParseUint(rangeOp[1], 10, 32)
if err != nil {
return nil, err
}
for n := first; n <= last; n++ {
cpus = append(cpus, uint(n))
}
}
return cpus, nil
}
Loading