github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/cmd/examples/per_cpu_map/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"embed"
     6  	"fmt"
     7  	"os"
     8  	"os/signal"
     9  	"runtime"
    10  	"time"
    11  
    12  	"github.com/dylandreimerink/gobpfld"
    13  	"github.com/dylandreimerink/gobpfld/bpfsys"
    14  	"github.com/dylandreimerink/gobpfld/bpftypes"
    15  	"golang.org/x/sys/unix"
    16  
    17  	_ "embed"
    18  )
    19  
    20  //go:embed bpf/percpu
    21  var f embed.FS
    22  
    23  // This example command demonstrates how to read from and write to a "per cpu" map.
    24  // Per CPU maps values are always arrays with the same number of elements as the CPU count of the host.
    25  // Therefor arrays or slices need to be used to read from them or read to them.
    26  
    27  func main() {
    28  	elfFileBytes, err := f.ReadFile("bpf/percpu")
    29  	if err != nil {
    30  		fmt.Fprintf(os.Stderr, "error opening ELF file: %s\n", err.Error())
    31  		os.Exit(1)
    32  	}
    33  
    34  	elf, err := gobpfld.LoadProgramFromELF(bytes.NewReader(elfFileBytes), gobpfld.ELFParseSettings{})
    35  	if err != nil {
    36  		fmt.Fprintf(os.Stderr, "error while reading ELF file: %s\n", err.Error())
    37  		os.Exit(1)
    38  	}
    39  
    40  	program := elf.Programs["percpumap_prog"].(*gobpfld.ProgramXDP)
    41  
    42  	// Since the map type is an per cpu array type, the elf package will return a generic map
    43  	counterMap := program.Maps["cnt_map"].(*gobpfld.PerCPUArrayMap)
    44  
    45  	log, err := program.Load(gobpfld.ProgXDPLoadOpts{
    46  		VerifierLogLevel: bpftypes.BPFLogLevelBasic,
    47  	})
    48  
    49  	fmt.Printf("BPF Verifier log:\n%s\n", log)
    50  
    51  	if err != nil {
    52  		fmt.Fprintf(os.Stderr, "error while loading program: %s\n", err.Error())
    53  		os.Exit(1)
    54  	}
    55  
    56  	sigChan := make(chan os.Signal, 1)
    57  	signal.Notify(sigChan, os.Interrupt, unix.SIGTERM, unix.SIGINT)
    58  
    59  	err = program.Attach(gobpfld.ProgXDPAttachOpts{
    60  		InterfaceName: "lo",
    61  		Replace:       true,
    62  	})
    63  
    64  	if err != nil {
    65  		fmt.Fprintf(os.Stderr, "error while attaching program to loopback device: %s\n", err.Error())
    66  		os.Exit(1)
    67  	}
    68  
    69  	// We need to know how many CPU's this machine has to size our value buffers correctly
    70  	// runtime.NumCPU returns the usable number of CPU's for this process.
    71  	// This number can be different from the number available to the kernel if this process
    72  	// has custom CPU affinity / scheduling. To avoid this the /proc/cpuinfo "file" should be
    73  	// parsed which seems the most reliable method for CPU count detection.
    74  	// But this is not (yet) included in gobpfld.
    75  	numCPUs := runtime.NumCPU()
    76  
    77  	ticker := time.Tick(1 * time.Second)
    78  	i := 0
    79  	for {
    80  		select {
    81  		case <-ticker:
    82  			// Alternate between using a slice and an array, just for fun
    83  			i += 1
    84  			if i%2 == 0 {
    85  				key := uint32(0)
    86  				valueSlice := make([]uint64, numCPUs)
    87  
    88  				err := counterMap.Get(key, &valueSlice)
    89  				if err != nil {
    90  					fmt.Fprintf(os.Stderr, "error while getting data from per-cpu map: %s\n", err.Error())
    91  					// Close sigchan to trigger a shutdown
    92  					close(sigChan)
    93  					break
    94  				}
    95  
    96  				fmt.Println("-----------------")
    97  				for i := 0; i < numCPUs; i++ {
    98  					fmt.Printf("CPU %d: %d packets processed\n", i, valueSlice[i])
    99  				}
   100  
   101  				// Every 10 seconds write the current counts to index 1
   102  				if i%10 == 0 {
   103  					key := uint32(1)
   104  
   105  					err := counterMap.Set(key, &valueSlice, bpfsys.BPFMapElemAny)
   106  					if err != nil {
   107  						fmt.Fprintf(os.Stderr, "error while setting data to per-cpu map: %s\n", err.Error())
   108  						// Close sigchan to trigger a shutdown
   109  						close(sigChan)
   110  						break
   111  					}
   112  				}
   113  
   114  			} else {
   115  				key := uint32(0)
   116  				// Array must have a static size, so we pick a number of CPUs we will not exceed
   117  				valueArray := [1024]uint64{}
   118  
   119  				err := counterMap.Get(key, &valueArray)
   120  				if err != nil {
   121  					fmt.Fprintf(os.Stderr, "error while getting data from per-cpu map: %s\n", err.Error())
   122  					// Close sigchan to trigger a shutdown
   123  					close(sigChan)
   124  					break
   125  				}
   126  
   127  				fmt.Println("-----------------")
   128  				for i := 0; i < numCPUs; i++ {
   129  					fmt.Printf("CPU %d: %d packets processed\n", i, valueArray[i])
   130  				}
   131  			}
   132  		case <-sigChan:
   133  			fmt.Println("Detaching XPD program and stopping")
   134  
   135  			err = program.XDPLinkDetach(gobpfld.BPFProgramXDPLinkDetachSettings{
   136  				All: true,
   137  			})
   138  			if err != nil {
   139  				fmt.Fprintf(os.Stderr, "error while detaching program: %s\n", err.Error())
   140  				os.Exit(1)
   141  			}
   142  
   143  			os.Exit(0)
   144  		}
   145  	}
   146  }