Repository: cilium/ebpf
Pull request #281 (2021-04-11 13:22:35)

Update README.md for how to run examples
Hi all,

The commit is to provide the helpful guidelines on how to run the examples.

Signed-off-by: Wen-Quan Li <legendarilylwq@gmail.com>


lmb: > Why don't we just add a `make examples` target to the main Makefile instead, and document that here? Not sure if this repo is the place to teach a user about `go build`... thinking

I'd rather not bloat the Makefile more. Users are probably interested in individual examples anyways, how do you run those via the makefile?

Documenting the `go run -exec sudo` trick is worth it IMO.
ti-mo: > Users are probably interested in individual examples anyways, how do you run those via the makefile?

Oh, I would just suggest `make examples` to build them, not run them. That would leave runnable binaries in the example dirs.

I hadn't used `go run` that way before, I thought it had to be called on a single file. So what about `go run -exec sudo .` in the individual example dirs then instead? That doesn't leave a binary behind in the directory.

`go build .` is functionally equivalent to `go build`, so I would omit the dot in case we go for that.
nevermosby: Just updated based on your suggestions, pls help review.

Patch:
diff --git a/examples/README.md b/examples/README.md
index f7ceef6..2aa6dce 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -4,3 +4,11 @@
 - [uprobe](uprobe/) - Like a kprobe, but for symbols in userspace binaries (e.g. `bash`).
 - [tracepoint](tracepoint/) - Attach a program to predetermined kernel tracepoints.
 - Add your use case(s) here!
+
+## How to run
+
+```bash
+cd ebpf/examples/
+go run -exec sudo [./kprobe,./uprobe,./tracepoint...]
+
+```
\ No newline at end of file
diff --git a/link/kprobe.go b/link/kprobe.go
index 9366628..9e519c2 100644
--- a/link/kprobe.go
+++ b/link/kprobe.go
@@ -1,12 +1,15 @@
 package link
 
 import (
+	"bytes"
 	"crypto/rand"
 	"errors"
 	"fmt"
+	"io/ioutil"
 	"os"
 	"path/filepath"
 	"runtime"
+	"sync"
 
 	"github.com/cilium/ebpf"
 	"github.com/cilium/ebpf/internal"
@@ -15,6 +18,12 @@ import (
 
 var (
 	kprobeEventsPath = filepath.Join(tracefsPath, "kprobe_events")
+
+	kprobeRetprobeBit = struct {
+		once  sync.Once
+		value uint64
+		err   error
+	}{}
 )
 
 // Kprobe attaches the given eBPF program to a perf event that fires when the
@@ -117,16 +126,19 @@ func pmuKprobe(symbol string, ret bool) (*perfEvent, error) {
 		return nil, err
 	}
 
-	// TODO: Parse the position of the bit from /sys/bus/event_source/devices/%s/format/retprobe.
-	config := 0
+	var config uint64
 	if ret {
-		config = 1
+		retprobeBit, err := kretprobeBit()
+		if err != nil {
+			return nil, fmt.Errorf("determine kretprobe bit: %w", err)
+		}
+		config |= 1 << retprobeBit
 	}
 
 	attr := unix.PerfEventAttr{
 		Type:   uint32(et),          // PMU event type read from sysfs
 		Ext1:   uint64(uintptr(sp)), // Kernel symbol to trace
-		Config: uint64(config),      // perf_kprobe PMU treats config as flags
+		Config: config,              // Set retprobe for k,uretprobes
 	}
 
 	fd, err := unix.PerfEventOpen(&attr, perfAllThreads, 0, -1, unix.PERF_FLAG_FD_CLOEXEC)
@@ -294,3 +306,32 @@ func kprobePrefix(ret bool) string {
 	}
 	return "p"
 }
+
+// determineRetprobeBit reads a Performance Monitoring Unit's retprobe bit
+// from /sys/bus/event_source/devices/<pmu>/format/retprobe.
+func determineRetprobeBit(pmu string) (uint64, error) {
+	p := filepath.Join("/sys/bus/event_source/devices/", pmu, "/format/retprobe")
+
+	data, err := ioutil.ReadFile(p)
+	if err != nil {
+		return 0, err
+	}
+
+	var rp uint64
+	n, err := fmt.Sscanf(string(bytes.TrimSpace(data)), "config:%d", &rp)
+	if err != nil {
+		return 0, fmt.Errorf("parse retprobe bit: %w", err)
+	}
+	if n != 1 {
+		return 0, fmt.Errorf("parse retprobe bit: expected 1 item, got %d", n)
+	}
+
+	return rp, nil
+}
+
+func kretprobeBit() (uint64, error) {
+	kprobeRetprobeBit.once.Do(func() {
+		kprobeRetprobeBit.value, kprobeRetprobeBit.err = determineRetprobeBit("kprobe")
+	})
+	return kprobeRetprobeBit.value, kprobeRetprobeBit.err
+}


Test patch:
diff --git a/link/kprobe_test.go b/link/kprobe_test.go
index 472a9f6..e02a141 100644
--- a/link/kprobe_test.go
+++ b/link/kprobe_test.go
@@ -270,3 +270,15 @@ func TestKprobeTraceFSGroup(t *testing.T) {
 	_, err = randomGroup("/")
 	c.Assert(err, qt.Not(qt.IsNil))
 }
+
+func TestDetermineRetprobeBit(t *testing.T) {
+	testutils.SkipOnOldKernel(t, "4.17", "perf_kprobe PMU")
+	c := qt.New(t)
+
+	rp, err := kretprobeBit()
+	c.Assert(err, qt.IsNil)
+	c.Assert(rp, qt.Equals, uint64(0))
+
+	_, err = determineRetprobeBit("bogus")
+	c.Assert(errors.Is(err, os.ErrNotExist), qt.IsTrue, qt.Commentf("got error: %s", err))
+}