Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kprobepin example showing how to re-use a pinned map in a BPF prog #364

Merged
merged 2 commits into from
Aug 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# eBPF Examples

- [kprobe](kprobe/) - Attach a program to the entry or exit of an arbitrary kernel symbol (function).
- [kprobepin](kprobepin/) - Reuse a pinned map for the kprobe example. It assumes the BPF FS is mounted at `/sys/fs/bpf`.
- [uretprobe](uretprobe/) - 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!
Expand Down
27 changes: 27 additions & 0 deletions examples/kprobepin/bpf/kprobe_pin_example.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include "common.h"
#include "bpf_helpers.h"

char __license[] SEC("license") = "Dual MIT/GPL";

struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, u64);
__uint(max_entries, 1);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} kprobe_map SEC(".maps");

SEC("kprobe/sys_execve")
int kprobe_execve() {
u32 key = 0;
u64 initval = 1, *valp;

valp = bpf_map_lookup_elem(&kprobe_map, &key);
if (!valp) {
bpf_map_update_elem(&kprobe_map, &key, &initval, BPF_ANY);
return 0;
}
__sync_fetch_and_add(valp, 1);

return 0;
}
116 changes: 116 additions & 0 deletions examples/kprobepin/kprobepinexample_bpfeb.go

Large diffs are not rendered by default.

116 changes: 116 additions & 0 deletions examples/kprobepin/kprobepinexample_bpfel.go

Large diffs are not rendered by default.

94 changes: 94 additions & 0 deletions examples/kprobepin/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// +build linux

// This program demonstrates attaching an eBPF program to a kernel symbol.
// The eBPF program will be attached to the start of the sys_execve
// kernel function and prints out the number of times it has been called
// every second.
package main

import (
"log"
"os"
"os/signal"
"path"
"syscall"
"time"

"github.com/cilium/ebpf"
"github.com/cilium/ebpf/link"
"golang.org/x/sys/unix"
)

//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang-11 KProbePinExample ./bpf/kprobe_pin_example.c -- -I../headers

const (
mapKey uint32 = 0
bpfFSPath = "/sys/fs/bpf"
)

func main() {

// Name of the kernel function to trace.
fn := "sys_execve"

// Subscribe to signals for terminating the program.
stopper := make(chan os.Signal, 1)
signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)

// Increase the rlimit of the current process to provide sufficient space
// for locking memory for the eBPF map.
if err := unix.Setrlimit(unix.RLIMIT_MEMLOCK, &unix.Rlimit{
Cur: unix.RLIM_INFINITY,
Max: unix.RLIM_INFINITY,
}); err != nil {
log.Fatalf("failed to set temporary rlimit: %v", err)
}

pinPath := path.Join(bpfFSPath, fn)
if err := os.MkdirAll(pinPath, os.ModePerm); err != nil {
log.Fatalf("failed to create bpf fs subpath: %+v", err)
}

var kProbeObj KProbePinExampleObjects
ungureanuvladvictor marked this conversation as resolved.
Show resolved Hide resolved
if err := LoadKProbePinExampleObjects(&kProbeObj, &ebpf.CollectionOptions{
Maps: ebpf.MapOptions{
// Pin the map to the BPF filesystem and configure the
// library to automatically re-write it in the BPF
// program so it can be re-used if it already exists or
// create it if not
PinPath: pinPath,
},
}); err != nil {
log.Fatalf("loading objects: %v", err)
}
defer kProbeObj.Close()

// Open a Kprobe at the entry point of the kernel function and attach the
// pre-compiled program. Each time the kernel function enters, the program
// will increment the execution counter by 1. The read loop below polls this
// map value once per second.
kp, err := link.Kprobe(fn, kProbeObj.KprobeExecve)
if err != nil {
log.Fatalf("opening kprobe: %s", err)
}
defer kp.Close()

// Read loop reporting the total amount of times the kernel
// function was entered, once per second.
ticker := time.NewTicker(1 * time.Second)

log.Println("Waiting for events..")

for {
select {
case <-ticker.C:
var value uint64
if err := kProbeObj.KprobeMap.Lookup(mapKey, &value); err != nil {
log.Fatalf("reading map: %v", err)
}
log.Printf("%s called %d times\n", fn, value)
case <-stopper:
return
}
}
}