github.com/cilium/ebpf@v0.15.0/examples/tracepoint_in_go/main.go (about)

     1  // This program demonstrates how to attach an eBPF program to a tracepoint.
     2  // The program is attached to the syscall/sys_enter_openat tracepoint and
     3  // prints out the integer 123 every time the syscall is entered.
     4  package main
     5  
     6  import (
     7  	"errors"
     8  	"log"
     9  	"os"
    10  	"os/signal"
    11  	"syscall"
    12  
    13  	"github.com/cilium/ebpf"
    14  	"github.com/cilium/ebpf/asm"
    15  	"github.com/cilium/ebpf/link"
    16  	"github.com/cilium/ebpf/perf"
    17  	"github.com/cilium/ebpf/rlimit"
    18  )
    19  
    20  // Metadata for the eBPF program used in this example.
    21  var progSpec = &ebpf.ProgramSpec{
    22  	Name:    "my_trace_prog", // non-unique name, will appear in `bpftool prog list` while attached
    23  	Type:    ebpf.TracePoint, // only TracePoint programs can be attached to trace events created by link.Tracepoint()
    24  	License: "GPL",           // license must be GPL for calling kernel helpers like perf_event_output
    25  }
    26  
    27  func main() {
    28  
    29  	// Subscribe to signals for terminating the program.
    30  	stopper := make(chan os.Signal, 1)
    31  	signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
    32  
    33  	// Allow the current process to lock memory for eBPF resources.
    34  	if err := rlimit.RemoveMemlock(); err != nil {
    35  		log.Fatal(err)
    36  	}
    37  
    38  	// Create a perf event array for the kernel to write perf records to.
    39  	// These records will be read by userspace below.
    40  	events, err := ebpf.NewMap(&ebpf.MapSpec{
    41  		Type: ebpf.PerfEventArray,
    42  		Name: "my_perf_array",
    43  	})
    44  	if err != nil {
    45  		log.Fatalf("creating perf event array: %s", err)
    46  	}
    47  	defer events.Close()
    48  
    49  	// Open a perf reader from userspace into the perf event array
    50  	// created earlier.
    51  	rd, err := perf.NewReader(events, os.Getpagesize())
    52  	if err != nil {
    53  		log.Fatalf("creating event reader: %s", err)
    54  	}
    55  	defer rd.Close()
    56  
    57  	// Close the reader when the process receives a signal, which will exit
    58  	// the read loop.
    59  	go func() {
    60  		<-stopper
    61  		rd.Close()
    62  	}()
    63  
    64  	// Minimal program that writes the static value '123' to the perf ring on
    65  	// each event. Note that this program refers to the file descriptor of
    66  	// the perf event array created above, which needs to be created prior to the
    67  	// program being verified by and inserted into the kernel.
    68  	progSpec.Instructions = asm.Instructions{
    69  		// store the integer 123 at FP[-8]
    70  		asm.Mov.Imm(asm.R2, 123),
    71  		asm.StoreMem(asm.RFP, -8, asm.R2, asm.Word),
    72  
    73  		// load registers with arguments for call of FnPerfEventOutput
    74  		asm.LoadMapPtr(asm.R2, events.FD()), // file descriptor of the perf event array
    75  		asm.LoadImm(asm.R3, 0xffffffff, asm.DWord),
    76  		asm.Mov.Reg(asm.R4, asm.RFP),
    77  		asm.Add.Imm(asm.R4, -8),
    78  		asm.Mov.Imm(asm.R5, 4),
    79  
    80  		// call FnPerfEventOutput, an eBPF kernel helper
    81  		asm.FnPerfEventOutput.Call(),
    82  
    83  		// set exit code to 0
    84  		asm.Mov.Imm(asm.R0, 0),
    85  		asm.Return(),
    86  	}
    87  
    88  	// Instantiate and insert the program into the kernel.
    89  	prog, err := ebpf.NewProgram(progSpec)
    90  	if err != nil {
    91  		log.Fatalf("creating ebpf program: %s", err)
    92  	}
    93  	defer prog.Close()
    94  
    95  	// Open a trace event based on a pre-existing kernel hook (tracepoint).
    96  	// Each time a userspace program uses the 'openat()' syscall, the eBPF
    97  	// program specified above will be executed and a '123' value will appear
    98  	// in the perf ring.
    99  	tp, err := link.Tracepoint("syscalls", "sys_enter_openat", prog, nil)
   100  	if err != nil {
   101  		log.Fatalf("opening tracepoint: %s", err)
   102  	}
   103  	defer tp.Close()
   104  
   105  	log.Println("Waiting for events..")
   106  
   107  	for {
   108  		record, err := rd.Read()
   109  		if err != nil {
   110  			if errors.Is(err, perf.ErrClosed) {
   111  				log.Println("Received signal, exiting..")
   112  				return
   113  			}
   114  			log.Printf("reading from reader: %s", err)
   115  			continue
   116  		}
   117  
   118  		log.Println("Record:", record)
   119  	}
   120  }