github.com/cilium/ebpf@v0.10.0/examples/uretprobe/main.go (about)

     1  // This program demonstrates how to attach an eBPF program to a uretprobe.
     2  // The program will be attached to the 'readline' symbol in the binary '/bin/bash' and print out
     3  // the line which 'readline' functions returns to the caller.
     4  
     5  //go:build amd64
     6  
     7  package main
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/binary"
    12  	"errors"
    13  	"log"
    14  	"os"
    15  	"os/signal"
    16  	"syscall"
    17  
    18  	"github.com/cilium/ebpf/link"
    19  	"github.com/cilium/ebpf/perf"
    20  	"github.com/cilium/ebpf/rlimit"
    21  	"golang.org/x/sys/unix"
    22  )
    23  
    24  // $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
    25  //go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64 -type event bpf uretprobe.c -- -I../headers
    26  
    27  const (
    28  	// The path to the ELF binary containing the function to trace.
    29  	// On some distributions, the 'readline' function is provided by a
    30  	// dynamically-linked library, so the path of the library will need
    31  	// to be specified instead, e.g. /usr/lib/libreadline.so.8.
    32  	// Use `ldd /bin/bash` to find these paths.
    33  	binPath = "/bin/bash"
    34  	symbol  = "readline"
    35  )
    36  
    37  func main() {
    38  	stopper := make(chan os.Signal, 1)
    39  	signal.Notify(stopper, os.Interrupt, syscall.SIGTERM)
    40  
    41  	// Allow the current process to lock memory for eBPF resources.
    42  	if err := rlimit.RemoveMemlock(); err != nil {
    43  		log.Fatal(err)
    44  	}
    45  
    46  	// Load pre-compiled programs and maps into the kernel.
    47  	objs := bpfObjects{}
    48  	if err := loadBpfObjects(&objs, nil); err != nil {
    49  		log.Fatalf("loading objects: %s", err)
    50  	}
    51  	defer objs.Close()
    52  
    53  	// Open an ELF binary and read its symbols.
    54  	ex, err := link.OpenExecutable(binPath)
    55  	if err != nil {
    56  		log.Fatalf("opening executable: %s", err)
    57  	}
    58  
    59  	// Open a Uretprobe at the exit point of the symbol and attach
    60  	// the pre-compiled eBPF program to it.
    61  	up, err := ex.Uretprobe(symbol, objs.UretprobeBashReadline, nil)
    62  	if err != nil {
    63  		log.Fatalf("creating uretprobe: %s", err)
    64  	}
    65  	defer up.Close()
    66  
    67  	// Open a perf event reader from userspace on the PERF_EVENT_ARRAY map
    68  	// described in the eBPF C program.
    69  	rd, err := perf.NewReader(objs.Events, os.Getpagesize())
    70  	if err != nil {
    71  		log.Fatalf("creating perf event reader: %s", err)
    72  	}
    73  	defer rd.Close()
    74  
    75  	go func() {
    76  		// Wait for a signal and close the perf reader,
    77  		// which will interrupt rd.Read() and make the program exit.
    78  		<-stopper
    79  		log.Println("Received signal, exiting program..")
    80  
    81  		if err := rd.Close(); err != nil {
    82  			log.Fatalf("closing perf event reader: %s", err)
    83  		}
    84  	}()
    85  
    86  	log.Printf("Listening for events..")
    87  
    88  	// bpfEvent is generated by bpf2go.
    89  	var event bpfEvent
    90  	for {
    91  		record, err := rd.Read()
    92  		if err != nil {
    93  			if errors.Is(err, perf.ErrClosed) {
    94  				return
    95  			}
    96  			log.Printf("reading from perf event reader: %s", err)
    97  			continue
    98  		}
    99  
   100  		if record.LostSamples != 0 {
   101  			log.Printf("perf event ring buffer full, dropped %d samples", record.LostSamples)
   102  			continue
   103  		}
   104  
   105  		// Parse the perf event entry into a bpfEvent structure.
   106  		if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.LittleEndian, &event); err != nil {
   107  			log.Printf("parsing perf event: %s", err)
   108  			continue
   109  		}
   110  
   111  		log.Printf("%s:%s return value: %s", binPath, symbol, unix.ByteSliceToString(event.Line[:]))
   112  	}
   113  }