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 }