diff --git a/_fixtures/ebpf_trace2.go b/_fixtures/ebpf_trace2.go new file mode 100644 index 00000000..6aff358d --- /dev/null +++ b/_fixtures/ebpf_trace2.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "time" +) + +func main() { + i := int64(0) + for i = 0; i < 5; i++ { + tracedFunction(i) + } + for i = 5; i < 10; i++ { + tracedFunction(i) + time.Sleep(time.Second) + } +} + +//go:noinline +func tracedFunction(x int64) { + fmt.Println(x) +} diff --git a/cmd/dlv/dlv_test.go b/cmd/dlv/dlv_test.go index aa1b1001..153cc962 100644 --- a/cmd/dlv/dlv_test.go +++ b/cmd/dlv/dlv_test.go @@ -1157,6 +1157,54 @@ func TestTraceEBPF2(t *testing.T) { cmd.Wait() } +func TestTraceEBPF3(t *testing.T) { + if os.Getenv("CI") == "true" { + t.Skip("cannot run test in CI, requires kernel compiled with btf support") + } + if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" { + t.Skip("not implemented on non linux/amd64 systems") + } + if !goversion.VersionAfterOrEqual(runtime.Version(), 1, 16) { + t.Skip("requires at least Go 1.16 to run test") + } + usr, err := user.Current() + if err != nil { + t.Fatal(err) + } + if usr.Uid != "0" { + t.Skip("test must be run as root") + } + + dlvbin := getDlvBinEBPF(t) + + expected := []byte(`> (1) main.tracedFunction(0) +> (1) main.tracedFunction(1) +> (1) main.tracedFunction(2) +> (1) main.tracedFunction(3) +> (1) main.tracedFunction(4) +> (1) main.tracedFunction(5) +> (1) main.tracedFunction(6) +> (1) main.tracedFunction(7) +> (1) main.tracedFunction(8) +> (1) main.tracedFunction(9)`) + + fixtures := protest.FindFixturesDir() + cmd := exec.Command(dlvbin, "trace", "--ebpf", "--output", filepath.Join(t.TempDir(), "__debug"), filepath.Join(fixtures, "ebpf_trace2.go"), "main.traced") + rdr, err := cmd.StderrPipe() + assertNoError(err, t, "stderr pipe") + defer rdr.Close() + + assertNoError(cmd.Start(), t, "running trace") + + output, err := ioutil.ReadAll(rdr) + assertNoError(err, t, "ReadAll") + + if !bytes.Contains(output, expected) { + t.Fatalf("expected:\n%s\ngot:\n%s", string(expected), string(output)) + } + cmd.Wait() +} + func TestDlvTestChdir(t *testing.T) { dlvbin := getDlvBin(t) diff --git a/pkg/proc/internal/ebpf/helpers.go b/pkg/proc/internal/ebpf/helpers.go index b856cef1..fbef79e9 100644 --- a/pkg/proc/internal/ebpf/helpers.go +++ b/pkg/proc/internal/ebpf/helpers.go @@ -61,6 +61,7 @@ type EBPFContext struct { bpfRingBuf *ringbuf.Reader executable *link.Executable bpfArgMap *ebpf.Map + links []link.Link parsedBpfEvents []RawUProbeParams m sync.Mutex @@ -70,13 +71,19 @@ func (ctx *EBPFContext) Close() { if ctx.objs != nil { ctx.objs.Close() } + if ctx.links != nil { + for _, l := range ctx.links { + l.Close() + } + } } func (ctx *EBPFContext) AttachUprobe(pid int, name string, offset uint64) error { if ctx.executable == nil { return errors.New("no eBPF program loaded") } - _, err := ctx.executable.Uprobe(name, ctx.objs.tracePrograms.UprobeDlvTrace, &link.UprobeOptions{PID: pid, Offset: offset}) + l, err := ctx.executable.Uprobe(name, ctx.objs.tracePrograms.UprobeDlvTrace, &link.UprobeOptions{PID: pid, Offset: offset}) + ctx.links = append(ctx.links, l) return err }