pkg/proc: fix ebpf probe tracing backen uprobe handling (#3417)

Uprobes get automatically cleaned and removed when the reference to
the Link object is lost. Hold a reference to any active Uprobe Link
for duration of Delve execution and ensure they are cleaned up
at exit.

Fixes ebpf probes don't work after time.Sleep() #3227
This commit is contained in:
Derek Parker 2023-06-15 03:07:32 -07:00 committed by GitHub
parent ccf17a6f42
commit 1d28ceccdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 78 additions and 1 deletions

22
_fixtures/ebpf_trace2.go Normal file

@ -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)
}

@ -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)

@ -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
}