Skip to content

Commit

Permalink
Refactor key format for attach/detach functions
Browse files Browse the repository at this point in the history
* Return multi-error for perf-event

Signed-off-by: Viet Anh Duong <[email protected]>
  • Loading branch information
vietanhduong committed Feb 14, 2024
1 parent 525bd3b commit f8ea316
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 55 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/avvmoto/buf-readerat v0.0.0-20171115124131-a17c8cb89270
github.com/cilium/ebpf v0.12.3
github.com/google/go-cmp v0.5.9
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/ianlancetaylor/demangle v0.0.0-20231023195312-e2daf7ba7156
github.com/orcaman/concurrent-map/v2 v2.0.1
Expand All @@ -18,6 +19,7 @@ require (

require (
github.com/frankban/quicktest v1.14.6 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/ianlancetaylor/demangle v0.0.0-20231023195312-e2daf7ba7156 h1:XaXfcSUnkTV/iujizC1//N5IrJA1v6KQHwMDbsZesoM=
Expand Down
125 changes: 77 additions & 48 deletions module.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func NewModule(opts ...ModuleOption) (*Module, error) {
}

// GetTable returns the table with the given name.
// Otherwise, an error will be returned.
func (m *Module) GetTable(name string) (*Table, error) {
tbl, ok := m.collection.Maps[name]
if !ok || tbl == nil {
Expand All @@ -86,12 +87,15 @@ func (m *Module) AttachKprobe(sysname, prog string) error {
return m.attachKprobe(sysname, prog, false)
}

func (m *Module) DetachKprobe(sysname string) {
// DetachKprobe detaches the kprobe with the given name. If the input prog is empty,
// all kprobes with the given name will be detached.
func (m *Module) DetachKprobe(sysname, prog string) {
detach := func(name string) {
if kprobe, _ := m.kprobes.Get(name); !utils.IsNil(kprobe) {
kprobe.Close()
if prog == "" {
detachPrefix(fmt.Sprintf("%s~", name), m.kprobes)
return
}
m.kprobes.Remove(name)
detach(fmt.Sprintf("%s~%s", name, prog), m.kprobes)
}
detach(sysname)
detach(FixSyscallName(sysname))
Expand All @@ -113,7 +117,8 @@ func (m *Module) AttachKretprobe(sysname, prog string) error {
// AttachTracepoint attaches a tracepoint to the input prog.
// The input name must be in the format 'group:name'
func (m *Module) AttachTracepoint(name, prog string) error {
if m.tracepoints.Has(name) {
key := fmt.Sprintf("%s~%s", name, prog)
if m.tracepoints.Has(key) {
return nil
}
parts := strings.SplitN(name, ":", 2)
Expand All @@ -130,23 +135,26 @@ func (m *Module) AttachTracepoint(name, prog string) error {
if err != nil {
return fmt.Errorf("link tracepoint: %w", err)
}
m.tracepoints.Set(name, tp)
m.tracepoints.Set(key, tp)
return nil
}

// DetachTracepoint detaches the tracepoint with the given name.
// The input name must be in the format 'group:name'
func (m *Module) DetachTracepoint(name string) {
if tp, _ := m.tracepoints.Get(name); !utils.IsNil(tp) {
tp.Close()
// The input name must be in the format 'group:name'. If the input prog is empty,
// all tracepoints with the given name will be detached.
func (m *Module) DetachTracepoint(name, prog string) {
if prog == "" {
detachPrefix(fmt.Sprintf("%s~", name), m.tracepoints)
return
}
m.tracepoints.Remove(name)
detach(fmt.Sprintf("%s~%s", name, prog), m.tracepoints)
}

// AttachRawTracepoint attaches a raw tracepoint to the input prog.
// The input name is in the format 'name', there is no group.
func (m *Module) AttachRawTracepoint(name, prog string) error {
if m.rawtps.Has(name) {
key := fmt.Sprintf("%s~%s", name, prog)
if m.rawtps.Has(key) {
return nil
}

Expand All @@ -159,17 +167,19 @@ func (m *Module) AttachRawTracepoint(name, prog string) error {
if err != nil {
return fmt.Errorf("link attach raw tracepoint %s: %w", name, err)
}
m.rawtps.Set(name, rawtp)
m.rawtps.Set(key, rawtp)
return nil
}

// DetachRawTracepoint detaches the raw tracepoint with the given name.
// The input name is in the format 'name', there is no group.
func (m *Module) DetachRawTracepoint(name string) {
if rawtp, _ := m.rawtps.Get(name); !utils.IsNil(rawtp) {
rawtp.Close()
// DetachRawTracepoint detaches the raw tracepoint with the given name and prog.
// The input name is in the format 'name', there is no group. If the input prog
// is empty, all raw tracepoints with the given name will be detached.
func (m *Module) DetachRawTracepoint(name, prog string) {
if prog == "" {
detachPrefix(fmt.Sprintf("%s~", name), m.rawtps)
return
}
m.rawtps.Remove(name)
detach(fmt.Sprintf("%s~%s", name, prog), m.rawtps)
}

// AttachPerfEvent attaches the given eBPF program to a perf event that fires
Expand All @@ -191,17 +201,12 @@ func (m *Module) AttachPerfEvent(prog string, opts PerfEventOptions) error {
return nil
}

func (m *Module) DetachPerfEvent(prog string) {
if event, _ := m.perfEvents.Get(prog); event != nil {
event.Close()
}
m.perfEvents.Remove(prog)
}
func (m *Module) DetachPerfEvent(prog string) { detach(prog, m.perfEvents) }

func (m *Module) attachKprobe(sysname, prog string, ret bool) error {
key := sysname
key := fmt.Sprintf("%s~%s", sysname, prog)
if ret {
key = fmt.Sprintf("%s_ret", sysname)
key = fmt.Sprintf("%s~ret", sysname)
}
if m.kprobes.Has(key) {
return nil
Expand Down Expand Up @@ -317,6 +322,10 @@ func (m *Module) attachUprobe(module, prog string, ret bool, opts *UprobeOptions
// the network interface to which you want to attach the input program.
// The input flags must conform to the link.XDPAttachFlags enum.
func (m *Module) AttachXDP(ifname, prog string, flags uint64) error {
key := fmt.Sprintf("%s~%s", ifname, prog)
if m.xdps.Has(key) {
return nil
}
p, err := m.GetProg(prog)
if err != nil {
return err
Expand All @@ -336,15 +345,18 @@ func (m *Module) AttachXDP(ifname, prog string, flags uint64) error {
if err != nil {
return fmt.Errorf("link attach xdp: %w", err)
}
m.xdps.Set(ifname, l)
m.xdps.Set(key, l)
return nil
}

func (m *Module) DetachXDP(ifname string) {
if l, _ := m.xdps.Get(ifname); !utils.IsNil(l) {
l.Close()
// DetachXDP detaches the XDP program from the given interface. If the input prog is empty,
// all XDP programs attached to the given interface will be detached.
func (m *Module) DetachXDP(ifname, prog string) {
if prog == "" { // remove all with the given ifname
detachPrefix(fmt.Sprintf("%s~", ifname), m.xdps)
return
}
m.xdps.Remove(ifname)
detach(fmt.Sprintf("%s~%s", ifname, prog), m.xdps)
}

// OpenPerfBuffer opens a perf buffer for the given table. The input opts is optional.
Expand Down Expand Up @@ -453,7 +465,8 @@ func (m *Module) AttachModifyReturn(prog string) error {
// a BTF-powered raw tracepoint (tp_btf) BPF Program to a BPF hook defined
// in kernel modules.
func (m *Module) AttachTracing(prog string, typ ebpf.AttachType) error {
if m.tracings.Has(prog) {
key := fmt.Sprintf("%s~%s", prog, typ.String())
if m.tracings.Has(key) {
return nil
}
p, err := m.GetProg(prog)
Expand All @@ -468,15 +481,16 @@ func (m *Module) AttachTracing(prog string, typ ebpf.AttachType) error {
if err != nil {
return fmt.Errorf("link tracing (type=%s): %w", typ.String(), err)
}
m.tracings.Set(prog, tp)
m.tracings.Set(key, tp)
return nil
}

func (m *Module) DetachTracing(prog string) {
if tp, _ := m.tracings.Get(prog); !utils.IsNil(tp) {
tp.Close()
func (m *Module) DetachTracing(prog string, typ ebpf.AttachType) {
if typ == ebpf.AttachNone {
detachPrefix(fmt.Sprintf("%s~", prog), m.tracings)
return
}
m.tracings.Remove(prog)
detach(fmt.Sprintf("%s~%s", prog, typ.String()), m.tracings)
}

func (m *Module) GetProg(name string) (*ebpf.Program, error) {
Expand Down Expand Up @@ -569,8 +583,8 @@ func (m *Module) Close() {
defer m.collection.Close()
}
// Detach Kprobes
for _, name := range m.kprobes.Keys() {
m.DetachKprobe(name)
for _, key := range m.kprobes.Keys() {
detach(key, m.kprobes)
}
// Close Ring Buffers
for _, name := range m.ringbufs.Keys() {
Expand All @@ -581,12 +595,12 @@ func (m *Module) Close() {
m.ClosePerfBuffer(name)
}
// Detach Raw Tracepoints
for _, name := range m.rawtps.Keys() {
m.DetachRawTracepoint(name)
for _, key := range m.rawtps.Keys() {
detach(key, m.rawtps)
}
// Detach Tracepoints
for _, name := range m.tracepoints.Keys() {
m.DetachTracepoint(name)
for _, key := range m.tracepoints.Keys() {
detach(key, m.tracepoints)
}
// Detach PerfEvents
for _, name := range m.perfEvents.Keys() {
Expand All @@ -598,12 +612,12 @@ func (m *Module) Close() {
m.uprobes.Remove(entry.Key)
}
// Detach XDPs
for _, name := range m.xdps.Keys() {
m.DetachXDP(name)
for _, key := range m.xdps.Keys() {
detach(key, m.xdps)
}
// Detach Tracings
for _, name := range m.tracings.Keys() {
m.DetachTracing(name)
for _, key := range m.tracings.Keys() {
detach(key, m.tracings)
}

// Purge Sym caches
Expand Down Expand Up @@ -650,3 +664,18 @@ func newModule(opts *moduleOptions) (*Module, error) {
}
return mod, nil
}

func detach[T interface{ Close() error }](key string, m cmap.ConcurrentMap[string, T]) {
if v, _ := m.Get(key); !utils.IsNil(v) {
v.Close()
}
m.Remove(key)
}

func detachPrefix[T interface{ Close() error }](prefix string, m cmap.ConcurrentMap[string, T]) {
for _, k := range m.Keys() {
if strings.HasPrefix(k, prefix) {
detach(k, m)
}
}
}
19 changes: 12 additions & 7 deletions perf_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"github.com/hashicorp/go-multierror"
"github.com/vietanhduong/wbpf/pkg/cpu"
"golang.org/x/sys/unix"
)
Expand Down Expand Up @@ -48,25 +49,29 @@ func NewPerfEvent(prog *ebpf.Program, opts PerfEventOptions) (*PerfEvent, error)
return this, nil
}

func (pe *PerfEvent) Close() {
func (pe *PerfEvent) Close() error {
if pe == nil {
return
return nil
}

var err error
for _, entry := range pe.cpus {
entry.Close()
if err2 := entry.Close(); err != nil {
err = multierror.Append(err, err2)
}
}
clear(pe.cpus)
return err
}

func (p *perfEventEntry) Close() {
func (p *perfEventEntry) Close() error {
if p == nil {
return
return nil
}
_ = syscall.Close(p.perfFd)
if p.rawlink != nil {
_ = p.rawlink.Close()
return p.rawlink.Close()
}
return nil
}

func (pe *PerfEvent) attachPerfEventOnCpu(cpu int, opts PerfEventOptions) (*perfEventEntry, error) {
Expand Down

0 comments on commit f8ea316

Please sign in to comment.