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

Extract lookupcgroupv2 out of the otlp reporter #227

Merged
merged 4 commits into from
Nov 20, 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
59 changes: 59 additions & 0 deletions libpf/cgroupv2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package libpf // import "go.opentelemetry.io/ebpf-profiler/libpf"

import (
"bufio"
"fmt"
"os"
"regexp"

lru "github.com/elastic/go-freelru"
log "github.com/sirupsen/logrus"
)

var (
cgroupv2PathPattern = regexp.MustCompile(`0:.*?:(.*)`)
)

// LookupCgroupv2 returns the cgroupv2 ID for pid.
func LookupCgroupv2(cgrouplru *lru.SyncedLRU[PID, string], pid PID) (string, error) {
id, ok := cgrouplru.Get(pid)
if ok {
return id, nil
}

// Slow path
f, err := os.Open(fmt.Sprintf("/proc/%d/cgroup", pid))
if err != nil {
return "", err
}
defer f.Close()

var genericCgroupv2 string
scanner := bufio.NewScanner(f)
buf := make([]byte, 512)
// Providing a predefined buffer overrides the internal buffer that Scanner uses (4096 bytes).
// We can do that and also set a maximum allocation size on the following call.
// With a maximum of 4096 characters path in the kernel, 8192 should be fine here. We don't
// expect lines in /proc/<PID>/cgroup to be longer than that.
scanner.Buffer(buf, 8192)
var pathParts []string
for scanner.Scan() {
line := scanner.Text()
pathParts = cgroupv2PathPattern.FindStringSubmatch(line)
if pathParts == nil {
log.Debugf("Could not extract cgroupv2 path from line: %s", line)
continue
}
genericCgroupv2 = pathParts[1]
break
}

// Cache the cgroupv2 information.
// To avoid busy lookups, also empty cgroupv2 information is cached.
cgrouplru.Add(pid, genericCgroupv2)

return genericCgroupv2, nil
}
51 changes: 1 addition & 50 deletions reporter/otlp_reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@
package reporter // import "go.opentelemetry.io/ebpf-profiler/reporter"

import (
"bufio"
"context"
"crypto/rand"
"crypto/tls"
"fmt"
"maps"
"os"
"regexp"
"slices"
"strconv"
"time"
Expand All @@ -33,10 +29,6 @@ import (
"go.opentelemetry.io/ebpf-profiler/libpf/xsync"
)

var (
cgroupv2PathPattern = regexp.MustCompile(`0:.*?:(.*)`)
)

// Assert that we implement the full Reporter interface.
var _ Reporter = (*OTLPReporter)(nil)

Expand Down Expand Up @@ -216,7 +208,7 @@ func (r *OTLPReporter) ReportTraceEvent(trace *libpf.Trace, meta *TraceEventMeta
traceEventsMap := r.traceEvents.WLock()
defer r.traceEvents.WUnlock(&traceEventsMap)

containerID, err := r.lookupCgroupv2(meta.PID)
containerID, err := libpf.LookupCgroupv2(r.cgroupv2ID, meta.PID)
if err != nil {
log.Debugf("Failed to get a cgroupv2 ID as container ID for PID %d: %v",
meta.PID, err)
Expand Down Expand Up @@ -871,44 +863,3 @@ func setupGrpcConnection(parent context.Context, cfg *Config,
//nolint:staticcheck
return grpc.DialContext(ctx, cfg.CollAgentAddr, opts...)
}

// lookupCgroupv2 returns the cgroupv2 ID for pid.
func (r *OTLPReporter) lookupCgroupv2(pid libpf.PID) (string, error) {
id, ok := r.cgroupv2ID.Get(pid)
if ok {
return id, nil
}

// Slow path
f, err := os.Open(fmt.Sprintf("/proc/%d/cgroup", pid))
if err != nil {
return "", err
}
defer f.Close()

var genericCgroupv2 string
scanner := bufio.NewScanner(f)
buf := make([]byte, 512)
// Providing a predefined buffer overrides the internal buffer that Scanner uses (4096 bytes).
// We can do that and also set a maximum allocation size on the following call.
// With a maximum of 4096 characters path in the kernel, 8192 should be fine here. We don't
// expect lines in /proc/<PID>/cgroup to be longer than that.
scanner.Buffer(buf, 8192)
var pathParts []string
for scanner.Scan() {
line := scanner.Text()
pathParts = cgroupv2PathPattern.FindStringSubmatch(line)
if pathParts == nil {
log.Debugf("Could not extract cgroupv2 path from line: %s", line)
continue
}
genericCgroupv2 = pathParts[1]
break
}

// Cache the cgroupv2 information.
// To avoid busy lookups, also empty cgroupv2 information is cached.
r.cgroupv2ID.Add(pid, genericCgroupv2)

return genericCgroupv2, nil
}