-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathattach.go
More file actions
125 lines (112 loc) · 3.34 KB
/
attach.go
File metadata and controls
125 lines (112 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Copyright The Parca Authors
// SPDX-License-Identifier: Apache-2.0
//go:build linux
package usdt
import (
"errors"
"fmt"
"unsafe"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
)
// ProbeLinks manages the lifecycle of attached USDT uprobes and their
// corresponding spec map entries.
type ProbeLinks struct {
Links []link.Link
SpecIDs []uint32
SpecMap *ebpf.Map
}
// Close detaches all probes and removes their spec map entries.
func (pl *ProbeLinks) Close() error { return pl.Unload() }
// Unload detaches all probes and removes their spec map entries.
func (pl *ProbeLinks) Unload() error {
var errs []error
for _, l := range pl.Links {
if err := l.Close(); err != nil {
errs = append(errs, err)
}
}
if pl.SpecMap != nil {
for _, id := range pl.SpecIDs {
if id != 0 {
if err := pl.SpecMap.Delete(unsafe.Pointer(&id)); err != nil {
errs = append(errs, err)
}
}
}
}
return errors.Join(errs...)
}
// PopulateSpecMap parses USDT probe arguments and stores the resulting specs
// in the BPF map. Each probe with parseable arguments is assigned a spec ID
// starting from startID. Probes with empty or unparseable arguments receive
// spec ID 0 (the BPF side treats 0 as "no spec").
func PopulateSpecMap(specMap *ebpf.Map, probes []Probe, startID uint32) ([]uint32, error) {
specIDs := make([]uint32, len(probes))
nextID := startID
for i, p := range probes {
if p.Arguments == "" {
continue
}
spec, err := ParseUSDTArguments(p.Arguments)
if err != nil {
// Non-fatal: probe just won't have argument extraction.
continue
}
id := nextID
nextID++
specIDs[i] = id
if err := specMap.Put(unsafe.Pointer(&id), SpecToBytes(spec)); err != nil {
return nil, fmt.Errorf("store spec for %s:%s: %w", p.Provider, p.Name, err)
}
}
return specIDs, nil
}
// MergeCookies combines spec IDs (high 32 bits) with user cookies (low 32
// bits) into the format expected by the BPF USDT argument extraction code.
func MergeCookies(specIDs []uint32, userCookies []uint64) []uint64 {
n := len(specIDs)
if len(userCookies) > n {
n = len(userCookies)
}
out := make([]uint64, n)
for i := range out {
if i < len(specIDs) {
out[i] = uint64(specIDs[i]) << 32
}
if i < len(userCookies) {
out[i] |= userCookies[i] & 0xFFFFFFFF
}
}
return out
}
// AttachUprobes attaches one BPF program per probe as a uprobe. The caller
// provides a slice of programs parallel to probes (one program per probe) and
// optional cookies. Returns ProbeLinks that must be closed to detach and
// clean up spec map entries.
func AttachUprobes(exe *link.Executable, specMap *ebpf.Map, probes []Probe,
progs []*ebpf.Program, cookies []uint64) (*ProbeLinks, error) {
if len(progs) != len(probes) {
return nil, fmt.Errorf("len(progs)=%d != len(probes)=%d", len(progs), len(probes))
}
var links []link.Link
for i, p := range probes {
opts := &link.UprobeOptions{
Address: p.Location,
RefCtrOffset: p.SemaphoreOffset,
}
if cookies != nil && i < len(cookies) {
opts.Cookie = cookies[i]
}
l, err := exe.Uprobe(p.Name, progs[i], opts)
if err != nil {
// Clean up already-attached links.
for _, lnk := range links {
lnk.Close()
}
return nil, fmt.Errorf("attach probe %s at 0x%x: %w", p.Name, p.Location, err)
}
links = append(links, l)
}
return &ProbeLinks{Links: links, SpecMap: specMap}, nil
}