Skip to content

Commit

Permalink
Attach tracing of tc-bpf concurrently
Browse files Browse the repository at this point in the history
When there are many tc-bpf progs to trace, it'll be very slow to attach
tracing one by one.

At this commit, I change the attaching way from serial to concurrent in
order to accelerate the attaching progress for tracing tc-bpf progs.

Signed-off-by: Leon Hwang <[email protected]>
  • Loading branch information
Asphaltt authored and brb committed Mar 4, 2024
1 parent 47a823e commit 9ea2ba5
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 86 deletions.
87 changes: 1 addition & 86 deletions internal/pwru/bpf_prog.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ package pwru
import (
"errors"
"fmt"
"log"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/btf"
"github.com/cilium/ebpf/link"
"golang.org/x/sys/unix"
)

Expand All @@ -36,7 +34,7 @@ func listBpfProgs(typ ebpf.ProgramType) ([]*ebpf.Program, error) {
}
}

if err != nil && !errors.Is(err, unix.ENOENT) {
if !errors.Is(err, unix.ENOENT) { // Surely err != nil
return nil, err
}

Expand Down Expand Up @@ -74,86 +72,3 @@ func getEntryFuncName(prog *ebpf.Program) (string, string, error) {

return "", "", fmt.Errorf("no function found in %s bpf prog", info.Name)
}

func TraceTC(prevColl *ebpf.Collection, spec *ebpf.CollectionSpec,
opts *ebpf.CollectionOptions, outputSkb bool, name2addr BpfProgName2Addr,
) func() {
progs, err := listBpfProgs(ebpf.SchedCLS)
if err != nil {
log.Fatalf("Failed to list TC bpf progs: %v", err)
}

// Reusing maps from previous collection is to handle the events together
// with the kprobes.
replacedMaps := map[string]*ebpf.Map{
"events": prevColl.Maps["events"],
"print_stack_map": prevColl.Maps["print_stack_map"],
}
if outputSkb {
replacedMaps["print_skb_map"] = prevColl.Maps["print_skb_map"]
}
opts.MapReplacements = replacedMaps

tracings := make([]link.Link, 0, len(progs))
for _, prog := range progs {
entryFn, name, err := getEntryFuncName(prog)
if err != nil {
log.Fatalf("Failed to get entry function name: %v", err)
}

// The addr may hold the wrong rip value, because two addresses could
// have one same symbol. As discussed before, that doesn't affect the
// symbol resolution because even a "wrong" rip can be matched to the
// right symbol. However, this could make a difference when we want to
// distinguish which exact bpf prog is called.
// -- @jschwinger233

addr, ok := name2addr[entryFn]
if !ok {
addr, ok = name2addr[name]
if !ok {
log.Fatalf("Failed to find address for tag %s of bpf prog %s", name, prog)
}
}

spec := spec.Copy()
if err := spec.RewriteConstants(map[string]any{
"BPF_PROG_ADDR": addr,
}); err != nil {
log.Fatalf("Failed to rewrite bpf prog addr: %v", err)
}

spec.Programs["fentry_tc"].AttachTarget = prog
spec.Programs["fentry_tc"].AttachTo = entryFn
coll, err := ebpf.NewCollectionWithOptions(spec, *opts)
if err != nil {
var (
ve *ebpf.VerifierError
verifierLog string
)
if errors.As(err, &ve) {
verifierLog = fmt.Sprintf("Verifier error: %+v\n", ve)
}

log.Fatalf("Failed to load objects: %s\n%+v", verifierLog, err)
}
defer coll.Close()

tracing, err := link.AttachTracing(link.TracingOptions{
Program: coll.Programs["fentry_tc"],
})
if err != nil {
log.Fatalf("Failed to attach tracing: %v", err)
}
tracings = append(tracings, tracing)
}

return func() {
for _, tracing := range tracings {
_ = tracing.Close()
}
for _, prog := range progs {
_ = prog.Close()
}
}
}
138 changes: 138 additions & 0 deletions internal/pwru/tc_tracer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// SPDX-License-Identifier: Apache-2.0
/* Copyright 2024 Authors of Cilium */

package pwru

import (
"errors"
"fmt"
"log"
"sync"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"golang.org/x/sync/errgroup"
)

type tcTracer struct {
sync.Mutex
links []link.Link
}

func (t *tcTracer) close() {
t.Lock()
defer t.Unlock()

for _, l := range t.links {
_ = l.Close()
}
}

func (t *tcTracer) addLink(l link.Link) {
t.Lock()
defer t.Unlock()

t.links = append(t.links, l)
}

func (t *tcTracer) trace(spec *ebpf.CollectionSpec,
opts *ebpf.CollectionOptions, prog *ebpf.Program, n2a BpfProgName2Addr,
) error {
entryFn, name, err := getEntryFuncName(prog)
if err != nil {
return fmt.Errorf("failed to get entry function name: %w", err)
}

// The addr may hold the wrong rip value, because two addresses could
// have one same symbol. As discussed before, that doesn't affect the
// symbol resolution because even a "wrong" rip can be matched to the
// right symbol. However, this could make a difference when we want to
// distinguish which exact bpf prog is called.
// -- @jschwinger233

addr, ok := n2a[entryFn]
if !ok {
addr, ok = n2a[name]
if !ok {
return fmt.Errorf("failed to find address for function %s of bpf prog %s", name, prog)
}
}

spec = spec.Copy()
if err := spec.RewriteConstants(map[string]any{
"BPF_PROG_ADDR": addr,
}); err != nil {
return fmt.Errorf("failed to rewrite bpf prog addr: %w", err)
}

spec.Programs["fentry_tc"].AttachTarget = prog
spec.Programs["fentry_tc"].AttachTo = entryFn
coll, err := ebpf.NewCollectionWithOptions(spec, *opts)
if err != nil {
var (
ve *ebpf.VerifierError
verifierLog string
)
if errors.As(err, &ve) {
verifierLog = fmt.Sprintf("Verifier error: %+v\n", ve)
}

return fmt.Errorf("failed to load objects: %s\n%w", verifierLog, err)
}
defer coll.Close()

tracing, err := link.AttachTracing(link.TracingOptions{
Program: coll.Programs["fentry_tc"],
})
if err != nil {
return fmt.Errorf("failed to attach tracing: %w", err)
}

t.addLink(tracing)

return nil
}

func TraceTC(coll *ebpf.Collection, spec *ebpf.CollectionSpec,
opts *ebpf.CollectionOptions, outputSkb bool, n2a BpfProgName2Addr,
) func() {
progs, err := listBpfProgs(ebpf.SchedCLS)
if err != nil {
log.Fatalf("Failed to list TC bpf progs: %v", err)
}

// Reusing maps from previous collection is to handle the events together
// with the kprobes.
replacedMaps := map[string]*ebpf.Map{
"events": coll.Maps["events"],
"print_stack_map": coll.Maps["print_stack_map"],
}
if outputSkb {
replacedMaps["print_skb_map"] = coll.Maps["print_skb_map"]
}
opts.MapReplacements = replacedMaps

var tt tcTracer
tt.links = make([]link.Link, 0, len(progs))

var errg errgroup.Group

for _, prog := range progs {
prog := prog
errg.Go(func() error {
return tt.trace(spec, opts, prog, n2a)
})
}

if err := errg.Wait(); err != nil {
log.Fatalf("Failed to trace TC: %v", err)
}

return func() {
tt.close()

for _, prog := range progs {
_ = prog.Close()
}
}
}

0 comments on commit 9ea2ba5

Please sign in to comment.