github.com/dylandreimerink/gobpfld@v0.6.1-0.20220205171531-e79c330ad608/prog_info.go (about) 1 package gobpfld 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "strings" 8 "syscall" 9 "time" 10 "unsafe" 11 12 "github.com/dylandreimerink/gobpfld/bpfsys" 13 "github.com/dylandreimerink/gobpfld/bpftypes" 14 "github.com/dylandreimerink/gobpfld/ebpf" 15 bpfSyscall "github.com/dylandreimerink/gobpfld/internal/syscall" 16 ) 17 18 // BPFProgInfo is a more easy to use version of the bpftypes.BPFProgInfo 19 // the main difference being that this struct contains the actual from the kernel 20 // not just pointers to them 21 type BPFProgInfo struct { 22 Type bpftypes.BPFProgType 23 ID uint32 24 Tag [bpftypes.BPF_TAG_SIZE]byte 25 JitedProgInsns []ebpf.RawInstruction 26 XlatedProgInsns []ebpf.RawInstruction 27 LoadTime time.Time 28 CreatedByUID uint32 29 MapIDs []uint32 30 Name ObjName 31 IfIndex uint32 32 Flags bpftypes.BPFProgInfoFlags 33 NetNSDev uint64 34 NetNSIno uint64 35 JitedKsyms []uint64 36 JitedFuncLens []uint32 37 BTFID uint32 38 FuncInfo []bpftypes.BPFFuncInfo 39 LineInfo []bpftypes.BPFLineInfo 40 JitedLineInfo []bpftypes.BPFLineInfo 41 ProgTags [][bpftypes.BPF_TAG_SIZE]byte 42 RunTimeNs uint64 43 RunCnt uint64 44 RecursionMisses uint64 45 } 46 47 // GetLoadedPrograms returns a slice of info object about all loaded bpf programs 48 func GetLoadedPrograms() ([]BPFProgInfo, error) { 49 programs := []BPFProgInfo{} 50 51 attr := &bpfsys.BPFAttrGetID{} 52 for { 53 err := bpfsys.ProgramGetNextID(attr) 54 if syserr, ok := err.(*bpfSyscall.Error); ok { 55 // if the "next" id could not be found, we have scanned them all 56 if syserr.Errno == syscall.ENOENT { 57 return programs, nil 58 } 59 60 return nil, fmt.Errorf("bpf prog_get_next_id returned error: %w", syserr) 61 } 62 63 fd, syserr := bpfsys.ProgramGetFDByID(&bpfsys.BPFAttrGetID{ 64 ID: attr.NextID, 65 }) 66 if syserr != nil { 67 return nil, fmt.Errorf("bpf prog_get_fd_by_id returned error: %w", syserr) 68 } 69 70 progInfo, err := GetProgramInfo(fd) 71 if err != nil { 72 return nil, fmt.Errorf("get program info: %w", syserr) 73 } 74 75 programs = append(programs, *progInfo) 76 77 attr.ID = attr.NextID 78 } 79 } 80 81 func getSystemBootTime() (time.Time, error) { 82 procUptime, err := os.Open("/proc/uptime") 83 if err != nil { 84 return time.Time{}, fmt.Errorf("can't open /proc/uptime which we need to calculate the program loadtime") 85 } 86 87 uptimeBytes, err := ioutil.ReadAll(procUptime) 88 if err != nil { 89 return time.Time{}, fmt.Errorf("can't read /proc/uptime which we need to calculate the program loadtime") 90 } 91 92 uptimeString := string(uptimeBytes) 93 uptimeString = uptimeString[:strings.Index(uptimeString, " ")] 94 uptimeString = "-" + strings.ReplaceAll(uptimeString, ".", "s") + "0ms" 95 uptime, err := time.ParseDuration(uptimeString) 96 if err != nil { 97 return time.Time{}, err 98 } 99 100 return time.Now().Add(uptime), nil 101 } 102 103 var systemBootTime *time.Time 104 105 func GetProgramInfo(fd bpfsys.BPFfd) (*BPFProgInfo, error) { 106 if systemBootTime == nil { 107 bt, err := getSystemBootTime() 108 if err != nil { 109 return nil, fmt.Errorf("get system time: %w", err) 110 } 111 112 systemBootTime = &bt 113 } 114 115 // At first we will call the function without passing any buffers since we don't know the 116 // sizes to allocate yet 117 info := bpftypes.BPFProgInfo{} 118 err := bpfsys.ObjectGetInfoByFD(&bpfsys.BPFAttrGetInfoFD{ 119 BPFFD: fd, 120 InfoLen: uint32(bpftypes.BPFProgInfoSize), 121 Info: uintptr(unsafe.Pointer(&info)), 122 }) 123 if err != nil { 124 return nil, fmt.Errorf("bpf obj_get_info_by_fd returned error: %w", err) 125 } 126 127 progInfo := BPFProgInfo{ 128 Type: info.Type, 129 ID: info.ID, 130 Tag: info.Tag, 131 JitedProgInsns: make([]ebpf.RawInstruction, info.JitedProgLen/uint32(ebpf.BPFInstSize)), 132 XlatedProgInsns: make([]ebpf.RawInstruction, info.XlatedProgLen/uint32(ebpf.BPFInstSize)), 133 LoadTime: systemBootTime.Add(time.Duration(info.LoadTime)), 134 CreatedByUID: info.CreatedByUID, 135 MapIDs: make([]uint32, info.NumMapIDs), 136 Name: ObjName{ 137 str: string(info.Name[:]), 138 cname: info.Name, 139 }, 140 IfIndex: info.IfIndex, 141 Flags: info.Flags, 142 NetNSDev: info.NetNSDev, 143 NetNSIno: info.NetNSIno, 144 JitedKsyms: make([]uint64, info.NumJitedKSyms), // TODO make custom type for ksyms 145 JitedFuncLens: make([]uint32, info.NumJitedFuncLens), // TODO make custom type for func lens 146 BTFID: info.BTFID, 147 FuncInfo: make([]bpftypes.BPFFuncInfo, info.NumFuncInfo), 148 LineInfo: make([]bpftypes.BPFLineInfo, info.NumLineInfo), 149 JitedLineInfo: make([]bpftypes.BPFLineInfo, info.NumJitedLineInfo), 150 ProgTags: make([][8]byte, info.NumProgTags), 151 RunTimeNs: info.RunTimeNs, 152 RunCnt: info.RunCnt, 153 RecursionMisses: info.RecursionMisses, 154 } 155 156 if len(progInfo.JitedProgInsns) > 0 { 157 info.JitedProgInsns = uintptr(unsafe.Pointer(&progInfo.JitedProgInsns[0])) 158 } 159 160 if len(progInfo.XlatedProgInsns) > 0 { 161 info.XlatedProgInsns = uintptr(unsafe.Pointer(&progInfo.XlatedProgInsns[0])) 162 } 163 164 if len(progInfo.MapIDs) > 0 { 165 info.MapIDs = uintptr(unsafe.Pointer(&progInfo.MapIDs[0])) 166 } 167 168 if len(progInfo.JitedKsyms) > 0 { 169 info.JitedKsyms = uintptr(unsafe.Pointer(&progInfo.JitedKsyms[0])) 170 } 171 172 if len(progInfo.JitedFuncLens) > 0 { 173 info.JitedFuncLens = uintptr(unsafe.Pointer(&progInfo.JitedFuncLens[0])) 174 } 175 176 if len(progInfo.FuncInfo) > 0 { 177 info.FuncInfo = uintptr(unsafe.Pointer(&progInfo.FuncInfo[0])) 178 } 179 180 if len(progInfo.LineInfo) > 0 { 181 info.LineInfo = uintptr(unsafe.Pointer(&progInfo.LineInfo[0])) 182 } 183 184 if len(progInfo.JitedLineInfo) > 0 { 185 info.JitedLineInfo = uintptr(unsafe.Pointer(&progInfo.JitedLineInfo[0])) 186 } 187 188 progTags := make([]byte, info.NumProgTags*bpftypes.BPF_TAG_SIZE) 189 if len(progTags) > 0 { 190 info.ProgTags = uintptr(unsafe.Pointer(&progTags[0])) 191 } 192 193 err = bpfsys.ObjectGetInfoByFD(&bpfsys.BPFAttrGetInfoFD{ 194 BPFFD: fd, 195 InfoLen: uint32(bpftypes.BPFProgInfoSize), 196 Info: uintptr(unsafe.Pointer(&info)), 197 }) 198 if err != nil { 199 return nil, fmt.Errorf("bpf obj_get_info_by_fd returned error: %w", err) 200 } 201 202 for i := 0; i < len(progTags); i += bpftypes.BPF_TAG_SIZE { 203 copy(progInfo.ProgTags[i/bpftypes.BPF_TAG_SIZE][:], progTags[i:i+bpftypes.BPF_TAG_SIZE]) 204 } 205 206 return &progInfo, nil 207 }