1 package link
2
3 import (
4 "errors"
5 "fmt"
6 "os"
7 "testing"
8
9 qt "github.com/frankban/quicktest"
10
11 "github.com/cilium/ebpf"
12 "github.com/cilium/ebpf/asm"
13 "github.com/cilium/ebpf/internal/testutils"
14 "github.com/cilium/ebpf/internal/unix"
15 )
16
17
18 var ksym = "vprintk"
19
20
21
22 var symTests = []string{
23 "async_resume.cold",
24 "echo_char.isra.0",
25 "get_buffer.constprop.0",
26 "unregister_kprobes.part.0",
27 }
28
29 func TestKprobe(t *testing.T) {
30 prog := mustLoadProgram(t, ebpf.Kprobe, 0, "")
31
32 for _, tt := range symTests {
33 t.Run(tt, func(t *testing.T) {
34 k, err := Kprobe(tt, prog, nil)
35 if err != nil {
36 t.Fatal(err)
37 }
38 defer k.Close()
39 })
40 }
41
42 c := qt.New(t)
43
44 k, err := Kprobe("bogus", prog, nil)
45 c.Assert(err, qt.ErrorIs, os.ErrNotExist, qt.Commentf("got error: %s", err))
46 if k != nil {
47 k.Close()
48 }
49
50 k, err = Kprobe(ksym, prog, nil)
51 c.Assert(err, qt.IsNil)
52 defer k.Close()
53
54 testLink(t, k, prog)
55 }
56
57 func TestKretprobe(t *testing.T) {
58 prog := mustLoadProgram(t, ebpf.Kprobe, 0, "")
59
60 for _, tt := range symTests {
61 t.Run(tt, func(t *testing.T) {
62 k, err := Kretprobe(tt, prog, nil)
63 if err != nil {
64 t.Fatal(err)
65 }
66 defer k.Close()
67 })
68 }
69
70 c := qt.New(t)
71
72 k, err := Kretprobe("bogus", prog, nil)
73 c.Assert(err, qt.ErrorIs, os.ErrNotExist, qt.Commentf("got error: %s", err))
74 if k != nil {
75 k.Close()
76 }
77
78 k, err = Kretprobe(ksym, prog, nil)
79 c.Assert(err, qt.IsNil)
80 defer k.Close()
81
82 testLink(t, k, prog)
83 }
84
85 func TestKprobeErrors(t *testing.T) {
86 c := qt.New(t)
87
88
89
90 _, err := Kprobe("", nil, nil)
91 c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue)
92
93 _, err = Kprobe("_", nil, nil)
94 c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue)
95
96 _, err = Kprobe(".", &ebpf.Program{}, nil)
97 c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue)
98
99 _, err = Kprobe("foo", &ebpf.Program{}, nil)
100 c.Assert(errors.Is(err, errInvalidInput), qt.IsTrue)
101 }
102
103
104 func TestKprobeCreatePMU(t *testing.T) {
105
106 testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU")
107
108 c := qt.New(t)
109
110
111 pk, err := pmuKprobe(probeArgs{symbol: ksym})
112 c.Assert(err, qt.IsNil)
113 defer pk.Close()
114
115 c.Assert(pk.typ, qt.Equals, kprobeEvent)
116
117
118 pr, err := pmuKprobe(probeArgs{symbol: ksym, ret: true})
119 c.Assert(err, qt.IsNil)
120 defer pr.Close()
121
122 c.Assert(pr.typ, qt.Equals, kretprobeEvent)
123
124
125
126 _, err = pmuKprobe(probeArgs{symbol: "bogus"})
127 c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
128
129
130
131 _, err = pmuKprobe(probeArgs{symbol: "bogus", ret: true})
132 c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
133 }
134
135
136 func TestKprobePMUUnavailable(t *testing.T) {
137 c := qt.New(t)
138
139 pk, err := pmuKprobe(probeArgs{symbol: ksym})
140 if err == nil {
141 pk.Close()
142 t.Skipf("Kernel supports perf_kprobe PMU, not asserting error.")
143 }
144
145
146 c.Assert(errors.Is(err, ErrNotSupported), qt.IsTrue, qt.Commentf("got error: %s", err))
147 }
148
149 func BenchmarkKprobeCreatePMU(b *testing.B) {
150 for n := 0; n < b.N; n++ {
151 pr, err := pmuKprobe(probeArgs{symbol: ksym})
152 if err != nil {
153 b.Error("error creating perf_kprobe PMU:", err)
154 }
155
156 if err := pr.Close(); err != nil {
157 b.Error("error closing perf_kprobe PMU:", err)
158 }
159 }
160 }
161
162
163 func TestKprobeTraceFS(t *testing.T) {
164 c := qt.New(t)
165
166
167 kp, err := tracefsKprobe(probeArgs{symbol: ksym})
168 c.Assert(err, qt.IsNil)
169 c.Assert(kp.Close(), qt.IsNil)
170 c.Assert(kp.typ, qt.Equals, kprobeEvent)
171
172 kp, err = tracefsKprobe(probeArgs{symbol: ksym, ret: true})
173 c.Assert(err, qt.IsNil)
174 c.Assert(kp.Close(), qt.IsNil)
175 c.Assert(kp.typ, qt.Equals, kretprobeEvent)
176
177
178 k1, err := tracefsKprobe(probeArgs{symbol: ksym})
179 c.Assert(err, qt.IsNil)
180 defer k1.Close()
181 c.Assert(k1.tracefsID, qt.Not(qt.Equals), 0)
182
183 k2, err := tracefsKprobe(probeArgs{symbol: ksym})
184 c.Assert(err, qt.IsNil)
185 defer k2.Close()
186 c.Assert(k2.tracefsID, qt.Not(qt.Equals), 0)
187
188
189 c.Assert(k1.tracefsID, qt.Not(qt.CmpEquals()), k2.tracefsID)
190
191
192 args := probeArgs{group: "testgroup", symbol: "symbol"}
193
194
195 err = createTraceFSProbeEvent(kprobeType, args)
196 c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
197
198
199
200 args.ret = true
201 err = createTraceFSProbeEvent(kprobeType, args)
202 c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
203 }
204
205 func BenchmarkKprobeCreateTraceFS(b *testing.B) {
206 for n := 0; n < b.N; n++ {
207
208
209 pr, err := tracefsKprobe(probeArgs{symbol: ksym})
210 if err != nil {
211 b.Error("error creating tracefs perf event:", err)
212 }
213
214 if err := pr.Close(); err != nil {
215 b.Error("error closing tracefs perf event:", err)
216 }
217 }
218 }
219
220
221
222
223
224 func TestKprobeCreateTraceFS(t *testing.T) {
225 testutils.SkipOnOldKernel(t, "5.0", "<tracefs>/kprobe_events doesn't reject duplicate events")
226
227 c := qt.New(t)
228
229 pg, _ := randomGroup("ebpftest")
230 rg, _ := randomGroup("ebpftest")
231
232
233 defer func() {
234 _ = closeTraceFSProbeEvent(kprobeType, pg, ksym)
235 _ = closeTraceFSProbeEvent(kprobeType, rg, ksym)
236 }()
237
238
239 args := probeArgs{group: pg, symbol: ksym}
240
241
242 err := createTraceFSProbeEvent(kprobeType, args)
243 c.Assert(err, qt.IsNil)
244
245
246
247 err = createTraceFSProbeEvent(kprobeType, args)
248 c.Assert(errors.Is(err, os.ErrExist), qt.IsTrue,
249 qt.Commentf("expected consecutive kprobe creation to contain os.ErrExist, got: %v", err))
250
251
252 c.Assert(closeTraceFSProbeEvent(kprobeType, pg, ksym), qt.IsNil)
253
254 args.group = rg
255 args.ret = true
256
257
258 err = createTraceFSProbeEvent(kprobeType, args)
259 c.Assert(err, qt.IsNil)
260
261 err = createTraceFSProbeEvent(kprobeType, args)
262 c.Assert(os.IsExist(err), qt.IsFalse,
263 qt.Commentf("expected consecutive kretprobe creation to contain os.ErrExist, got: %v", err))
264
265
266 c.Assert(closeTraceFSProbeEvent(kprobeType, rg, ksym), qt.IsNil)
267 }
268
269 func TestKprobeTraceFSGroup(t *testing.T) {
270 c := qt.New(t)
271
272
273 g, err := randomGroup("ebpftest")
274 c.Assert(err, qt.IsNil)
275 c.Assert(g, qt.Matches, `ebpftest_[a-f0-9]{16}`)
276
277
278 p := make([]byte, 47)
279 for i := range p {
280 p[i] = byte('a')
281 }
282 _, err = randomGroup(string(p))
283 c.Assert(err, qt.Not(qt.IsNil))
284
285
286 _, err = randomGroup("/")
287 c.Assert(err, qt.Not(qt.IsNil))
288 }
289
290 func TestDetermineRetprobeBit(t *testing.T) {
291 testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU")
292 c := qt.New(t)
293
294 rpk, err := kretprobeBit()
295 c.Assert(err, qt.IsNil)
296 c.Assert(rpk, qt.Equals, uint64(0))
297
298 rpu, err := uretprobeBit()
299 c.Assert(err, qt.IsNil)
300 c.Assert(rpu, qt.Equals, uint64(0))
301 }
302
303 func TestKprobeProgramCall(t *testing.T) {
304 m, p := newUpdaterMapProg(t, ebpf.Kprobe)
305
306
307
308 k, err := Kprobe("sys_getpid", p, nil)
309 if err != nil {
310 t.Fatal(err)
311 }
312
313
314 unix.Getpid()
315
316
317 assertMapValue(t, m, 0, 1)
318
319
320 if err := k.Close(); err != nil {
321 t.Fatal(err)
322 }
323
324
325 if err := m.Update(uint32(0), uint32(0), ebpf.UpdateExist); err != nil {
326 t.Fatal(err)
327 }
328
329
330 unix.Getpid()
331
332
333 assertMapValue(t, m, 0, 0)
334 }
335
336 func newUpdaterMapProg(t *testing.T, typ ebpf.ProgramType) (*ebpf.Map, *ebpf.Program) {
337
338 m, err := ebpf.NewMap(&ebpf.MapSpec{
339 Type: ebpf.Array,
340 KeySize: 4,
341 ValueSize: 4,
342 MaxEntries: 1,
343 })
344 if err != nil {
345 t.Fatal(err)
346 }
347
348
349
350 p, err := ebpf.NewProgram(&ebpf.ProgramSpec{
351 Type: typ,
352 Instructions: asm.Instructions{
353
354 asm.Mov.Imm(asm.R1, 0),
355 asm.StoreMem(asm.RFP, -4, asm.R1, asm.Word),
356
357
358 asm.Mov.Imm(asm.R1, 1),
359 asm.StoreMem(asm.RFP, -8, asm.R1, asm.Word),
360
361
362 asm.Mov.Reg(asm.R2, asm.RFP),
363 asm.Add.Imm(asm.R2, -4),
364 asm.Mov.Reg(asm.R3, asm.RFP),
365 asm.Add.Imm(asm.R3, -8),
366 asm.LoadMapPtr(asm.R1, m.FD()),
367 asm.Mov.Imm(asm.R4, 0),
368 asm.FnMapUpdateElem.Call(),
369
370
371 asm.Mov.Imm(asm.R0, 0),
372 asm.Return(),
373 },
374 License: "Dual MIT/GPL",
375 })
376 if err != nil {
377 t.Fatal(err)
378 }
379
380
381 t.Cleanup(func() {
382 m.Close()
383 p.Close()
384 })
385
386 return m, p
387 }
388
389 func assertMapValue(t *testing.T, m *ebpf.Map, k, v uint32) {
390 var val uint32
391 if err := m.Lookup(k, &val); err != nil {
392 t.Fatal(err)
393 }
394 if val != v {
395 t.Fatalf("unexpected value: want '%d', got '%d'", v, val)
396 }
397 }
398
399 func TestKprobeCookie(t *testing.T) {
400 testutils.SkipOnOldKernel(t, "5.15", "bpf_perf_link")
401
402 prog := mustLoadProgram(t, ebpf.Kprobe, 0, "")
403 k, err := Kprobe(ksym, prog, &KprobeOptions{Cookie: 1000})
404 if err != nil {
405 t.Fatal(err)
406 }
407 k.Close()
408 }
409
410 func TestKprobeToken(t *testing.T) {
411 tests := []struct {
412 args probeArgs
413 expected string
414 }{
415 {probeArgs{symbol: "symbol"}, "symbol"},
416 {probeArgs{symbol: "symbol", offset: 1}, "symbol+0x1"},
417 {probeArgs{symbol: "symbol", offset: 65535}, "symbol+0xffff"},
418 {probeArgs{symbol: "symbol", offset: 65536}, "symbol+0x10000"},
419 }
420
421 for i, tt := range tests {
422 t.Run(fmt.Sprint(i), func(t *testing.T) {
423 po := kprobeToken(tt.args)
424 if tt.expected != po {
425 t.Errorf("Expected symbol+offset to be '%s', got '%s'", tt.expected, po)
426 }
427 })
428 }
429 }
430
View as plain text