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