Skip to content
This repository was archived by the owner on Jul 19, 2023. It is now read-only.

fix/ebpf arm64 unwind #862

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
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
Next Next commit
fix: vendor rlimit, to avoid bpf syscalls during init
  • Loading branch information
korniltsev committed Jul 14, 2023
commit 8458bd7c8687d1c9db38149c7d9cacd1da906aea
70 changes: 70 additions & 0 deletions ebpf/cmd/playground/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"fmt"
"os"
"strings"
"time"

"github.com/go-kit/log"
ebpfspy "github.com/grafana/phlare/ebpf"
"github.com/grafana/phlare/ebpf/sd"
"github.com/grafana/phlare/ebpf/symtab"
"github.com/prometheus/client_golang/prometheus"
)

func main() {
l := log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr))

targetFinder, err := sd.NewTargetFinder(os.DirFS("/"), l, sd.TargetsOptions{
TargetsOnly: false,
DefaultTarget: map[string]string{"service_name": "playground"},
ContainerCacheSize: 239,
})
if err != nil {
panic(fmt.Errorf("ebpf target finder create: %w", err))
}
session, err := ebpfspy.NewSession(
l,
targetFinder,
convertSessionOptions(),
)
_ = session
err = session.Start()
if err != nil {
panic(err)
}
for {
time.Sleep(5 * time.Second)
err := session.CollectProfiles(func(target *sd.Target, stack []string, value uint64, pid uint32) {
fmt.Printf("%s %d\n", strings.Join(stack, ";"), value)
})
if err != nil {
panic(err)
}
}
}

func convertSessionOptions() ebpfspy.SessionOptions {
ms := symtab.NewMetrics(prometheus.DefaultRegisterer)
return ebpfspy.SessionOptions{
CollectUser: true,
CollectKernel: true,
SampleRate: 11,
CacheOptions: symtab.CacheOptions{
PidCacheOptions: symtab.GCacheOptions{
Size: 239,
KeepRounds: 3,
},
BuildIDCacheOptions: symtab.GCacheOptions{
Size: 239,
KeepRounds: 3,
},
SameFileCacheOptions: symtab.GCacheOptions{
Size: 239,
KeepRounds: 3,
},
Metrics: ms,
},
}
}
2 changes: 1 addition & 1 deletion ebpf/go.mod
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ go 1.19

require (
github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270
github.com/cilium/ebpf v0.10.0
github.com/cilium/ebpf v0.11.0
github.com/go-kit/log v0.2.1
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751
github.com/hashicorp/golang-lru/v2 v2.0.3
21 changes: 18 additions & 3 deletions ebpf/go.sum
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270 h1:JIxGEMs4E5Zb6R7z2C5IgecI0mkqS97WAEF31wUbYTM=
github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270/go.mod h1:2XtVRGCw/HthOLxU0Qw6o6jSJrcEoOb2OCCl8gQYvGw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cilium/ebpf v0.10.0 h1:nk5HPMeoBXtOzbkZBWym+ZWq1GIiHUsBFXxwewXAHLQ=
github.com/cilium/ebpf v0.10.0/go.mod h1:DPiVdY/kT534dgc9ERmvP8mWA+9gvwgKfRvk4nNWnoE=
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
@@ -23,24 +28,34 @@ github.com/klauspost/compress v1.16.6/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY=
github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.10.0 h1:UkG7GPYkO4UZyLnyXjaWYcgOSONqwdBqFUT95ugmt6I=
github.com/prometheus/procfs v0.10.0/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw=
github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
45 changes: 45 additions & 0 deletions ebpf/rlimit/internal/internal_mirror.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package internal

import (
"runtime"
"unsafe"

"golang.org/x/sys/unix"
)

type MapType uint32
type TypeID uint32
type MapFlags uint32
type ObjName [unix.BPF_OBJ_NAME_LEN]byte
type Cmd uint32

const (
BPF_MAP_CREATE Cmd = 0
)

type MapCreateAttr struct {
MapType MapType
KeySize uint32
ValueSize uint32
MaxEntries uint32
MapFlags MapFlags
InnerMapFd uint32
NumaNode uint32
MapName ObjName
MapIfindex uint32
BtfFd uint32
BtfKeyTypeId TypeID
BtfValueTypeId TypeID
BtfVmlinuxValueTypeId TypeID
MapExtra uint64
}

func MapCreate(attr *MapCreateAttr) (uintptr, error) {
var attr2 = unsafe.Pointer(attr)
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(BPF_MAP_CREATE), uintptr(attr2), unsafe.Sizeof(*attr))
runtime.KeepAlive(attr2)
if errNo == 0 {
return r1, nil
}
return r1, errNo
}
113 changes: 113 additions & 0 deletions ebpf/rlimit/rlimit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//go:build linux

// Package rlimit allows raising RLIMIT_MEMLOCK if necessary for the use of BPF.

// this is a copied version of https://github.com/cilium/ebpf/blob/main/rlimit/rlimit.go
// with the following changes:
// 1. unsupportedMemcgAccounting = &internal.UnsupportedFeatureError replaced with unsupportedMemcgAccounting = errors.New
// to avoid importing internal package
// 2. global haveMemcgAccounting removed, it is checked locally under rlimitMu lock in RemoveMemlock
package rlimit

import (
"errors"
"fmt"
"sync"

"github.com/grafana/phlare/ebpf/rlimit/internal"
"golang.org/x/sys/unix"
)

var (
unsupportedMemcgAccounting = errors.New("unsupported memcg-based accounting for BPF memory. Minimum version: 5.11.0")
haveMemcgAccounting error
haveMemcgAccountingInit sync.Once

rlimitMu sync.Mutex
)

func detectMemcgAccounting() error {
// Retrieve the original limit to prevent lowering Max, since
// doing so is a permanent operation when running unprivileged.
var oldLimit unix.Rlimit
if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, nil, &oldLimit); err != nil {
return fmt.Errorf("getting original memlock rlimit: %s", err)
}

// Drop the current limit to zero, maintaining the old Max value.
// This is always permitted by the kernel for unprivileged users.
// Retrieve a new copy of the old limit tuple to minimize the chances
// of failing the restore operation below.
zeroLimit := unix.Rlimit{Cur: 0, Max: oldLimit.Max}
if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &zeroLimit, &oldLimit); err != nil {
return fmt.Errorf("lowering memlock rlimit: %s", err)
}

attr := internal.MapCreateAttr{
MapType: 2, /* Array */
KeySize: 4,
ValueSize: 4,
MaxEntries: 1,
}

// Creating a map allocates shared (and locked) memory that counts against
// the rlimit on pre-5.11 kernels, but against the memory cgroup budget on
// kernels 5.11 and over. If this call succeeds with the process' memlock
// rlimit set to 0, we can reasonably assume memcg accounting is supported.
fd, mapErr := internal.MapCreate(&attr)
defer unix.Close(int(fd))

// Restore old limits regardless of what happened.
if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &oldLimit, nil); err != nil {
return fmt.Errorf("restoring old memlock rlimit: %s", err)
}

// Map creation successful, memcg accounting supported.
if mapErr == nil {
return nil
}

// EPERM shows up when map creation would exceed the memory budget.
if errors.Is(mapErr, unix.EPERM) {
return unsupportedMemcgAccounting
}

// This shouldn't happen really.
return fmt.Errorf("unexpected error detecting memory cgroup accounting: %s", mapErr)
}

// RemoveMemlock removes the limit on the amount of memory the current
// process can lock into RAM, if necessary.
//
// This is not required to load eBPF resources on kernel versions 5.11+
// due to the introduction of cgroup-based memory accounting. On such kernels
// the function is a no-op.
//
// Since the function may change global per-process limits it should be invoked
// at program start up, in main() or init().
//
// This function exists as a convenience and should only be used when
// permanently raising RLIMIT_MEMLOCK to infinite is appropriate. Consider
// invoking prlimit(2) directly with a more reasonable limit if desired.
//
// Requires CAP_SYS_RESOURCE on kernels < 5.11.
func RemoveMemlock() error {
haveMemcgAccountingInit.Do(func() {
haveMemcgAccounting = detectMemcgAccounting()
})

if haveMemcgAccounting == nil {
return nil
}
if !errors.Is(haveMemcgAccounting, unsupportedMemcgAccounting) {
return haveMemcgAccounting
}
rlimitMu.Lock()
defer rlimitMu.Unlock()
// pid 0 affects the current process. Requires CAP_SYS_RESOURCE.
newLimit := unix.Rlimit{Cur: unix.RLIM_INFINITY, Max: unix.RLIM_INFINITY}
if err := unix.Prlimit(0, unix.RLIMIT_MEMLOCK, &newLimit, nil); err != nil {
return fmt.Errorf("failed to set memlock rlimit: %w", err)
}
return nil
}
2 changes: 1 addition & 1 deletion ebpf/session.go
Original file line number Diff line number Diff line change
@@ -14,10 +14,10 @@ import (
"unsafe"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/rlimit"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/grafana/phlare/ebpf/cpuonline"
"github.com/grafana/phlare/ebpf/rlimit"
"github.com/grafana/phlare/ebpf/sd"
"github.com/grafana/phlare/ebpf/symtab"
"github.com/samber/lo"
3 changes: 3 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -343,6 +343,7 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY=
github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA=
github.com/clarkduvall/hyperloglog v0.0.0-20171127014514-a0107a5d8004 h1:mK6JroY6bLiPS3s6QCYOSjRyErFc2iHNkhhmRfF0nHo=
@@ -370,6 +371,7 @@ github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fp
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ=
@@ -863,6 +865,7 @@ go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=