github.com/cilium/ebpf@v0.16.0/info.go (about) 1 package ebpf 2 3 import ( 4 "bufio" 5 "bytes" 6 "encoding/hex" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "strings" 12 "syscall" 13 "time" 14 "unsafe" 15 16 "github.com/cilium/ebpf/asm" 17 "github.com/cilium/ebpf/btf" 18 "github.com/cilium/ebpf/internal" 19 "github.com/cilium/ebpf/internal/sys" 20 "github.com/cilium/ebpf/internal/unix" 21 ) 22 23 // The *Info structs expose metadata about a program or map. Most 24 // fields are exposed via a getter: 25 // 26 // func (*MapInfo) ID() (MapID, bool) 27 // 28 // This is because the metadata available changes based on kernel version. 29 // The second boolean return value indicates whether a particular field is 30 // available on the current kernel. 31 // 32 // Always add new metadata as such a getter, unless you can somehow get the 33 // value of the field on all supported kernels. Also document which version 34 // a particular field first appeared in. 35 // 36 // Some metadata is a buffer which needs additional parsing. In this case, 37 // store the undecoded data in the Info struct and provide a getter which 38 // decodes it when necessary. See ProgramInfo.Instructions for an example. 39 40 // MapInfo describes a map. 41 type MapInfo struct { 42 Type MapType 43 id MapID 44 KeySize uint32 45 ValueSize uint32 46 MaxEntries uint32 47 Flags uint32 48 // Name as supplied by user space at load time. Available from 4.15. 49 Name string 50 51 btf btf.ID 52 } 53 54 func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) { 55 var info sys.MapInfo 56 err := sys.ObjInfo(fd, &info) 57 if errors.Is(err, syscall.EINVAL) { 58 return newMapInfoFromProc(fd) 59 } 60 if err != nil { 61 return nil, err 62 } 63 64 return &MapInfo{ 65 MapType(info.Type), 66 MapID(info.Id), 67 info.KeySize, 68 info.ValueSize, 69 info.MaxEntries, 70 uint32(info.MapFlags), 71 unix.ByteSliceToString(info.Name[:]), 72 btf.ID(info.BtfId), 73 }, nil 74 } 75 76 func newMapInfoFromProc(fd *sys.FD) (*MapInfo, error) { 77 var mi MapInfo 78 err := scanFdInfo(fd, map[string]interface{}{ 79 "map_type": &mi.Type, 80 "key_size": &mi.KeySize, 81 "value_size": &mi.ValueSize, 82 "max_entries": &mi.MaxEntries, 83 "map_flags": &mi.Flags, 84 }) 85 if err != nil { 86 return nil, err 87 } 88 return &mi, nil 89 } 90 91 // ID returns the map ID. 92 // 93 // Available from 4.13. 94 // 95 // The bool return value indicates whether this optional field is available. 96 func (mi *MapInfo) ID() (MapID, bool) { 97 return mi.id, mi.id > 0 98 } 99 100 // BTFID returns the BTF ID associated with the Map. 101 // 102 // The ID is only valid as long as the associated Map is kept alive. 103 // Available from 4.18. 104 // 105 // The bool return value indicates whether this optional field is available and 106 // populated. (The field may be available but not populated if the kernel 107 // supports the field but the Map was loaded without BTF information.) 108 func (mi *MapInfo) BTFID() (btf.ID, bool) { 109 return mi.btf, mi.btf > 0 110 } 111 112 // programStats holds statistics of a program. 113 type programStats struct { 114 // Total accumulated runtime of the program ins ns. 115 runtime time.Duration 116 // Total number of times the program was called. 117 runCount uint64 118 // Total number of times the programm was NOT called. 119 // Added in commit 9ed9e9ba2337 ("bpf: Count the number of times recursion was prevented"). 120 recursionMisses uint64 121 } 122 123 // ProgramInfo describes a program. 124 type ProgramInfo struct { 125 Type ProgramType 126 id ProgramID 127 // Truncated hash of the BPF bytecode. Available from 4.13. 128 Tag string 129 // Name as supplied by user space at load time. Available from 4.15. 130 Name string 131 132 createdByUID uint32 133 haveCreatedByUID bool 134 btf btf.ID 135 stats *programStats 136 137 maps []MapID 138 insns []byte 139 140 lineInfos []byte 141 numLineInfos uint32 142 funcInfos []byte 143 numFuncInfos uint32 144 } 145 146 func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { 147 var info sys.ProgInfo 148 err := sys.ObjInfo(fd, &info) 149 if errors.Is(err, syscall.EINVAL) { 150 return newProgramInfoFromProc(fd) 151 } 152 if err != nil { 153 return nil, err 154 } 155 156 pi := ProgramInfo{ 157 Type: ProgramType(info.Type), 158 id: ProgramID(info.Id), 159 Tag: hex.EncodeToString(info.Tag[:]), 160 Name: unix.ByteSliceToString(info.Name[:]), 161 btf: btf.ID(info.BtfId), 162 stats: &programStats{ 163 runtime: time.Duration(info.RunTimeNs), 164 runCount: info.RunCnt, 165 recursionMisses: info.RecursionMisses, 166 }, 167 } 168 169 // Start with a clean struct for the second call, otherwise we may get EFAULT. 170 var info2 sys.ProgInfo 171 172 makeSecondCall := false 173 174 if info.NrMapIds > 0 { 175 pi.maps = make([]MapID, info.NrMapIds) 176 info2.NrMapIds = info.NrMapIds 177 info2.MapIds = sys.NewPointer(unsafe.Pointer(&pi.maps[0])) 178 makeSecondCall = true 179 } else if haveProgramInfoMapIDs() == nil { 180 // This program really has no associated maps. 181 pi.maps = make([]MapID, 0) 182 } else { 183 // The kernel doesn't report associated maps. 184 pi.maps = nil 185 } 186 187 // createdByUID and NrMapIds were introduced in the same kernel version. 188 if pi.maps != nil { 189 pi.createdByUID = info.CreatedByUid 190 pi.haveCreatedByUID = true 191 } 192 193 if info.XlatedProgLen > 0 { 194 pi.insns = make([]byte, info.XlatedProgLen) 195 info2.XlatedProgLen = info.XlatedProgLen 196 info2.XlatedProgInsns = sys.NewSlicePointer(pi.insns) 197 makeSecondCall = true 198 } 199 200 if info.NrLineInfo > 0 { 201 pi.lineInfos = make([]byte, btf.LineInfoSize*info.NrLineInfo) 202 info2.LineInfo = sys.NewSlicePointer(pi.lineInfos) 203 info2.LineInfoRecSize = btf.LineInfoSize 204 info2.NrLineInfo = info.NrLineInfo 205 pi.numLineInfos = info.NrLineInfo 206 makeSecondCall = true 207 } 208 209 if info.NrFuncInfo > 0 { 210 pi.funcInfos = make([]byte, btf.FuncInfoSize*info.NrFuncInfo) 211 info2.FuncInfo = sys.NewSlicePointer(pi.funcInfos) 212 info2.FuncInfoRecSize = btf.FuncInfoSize 213 info2.NrFuncInfo = info.NrFuncInfo 214 pi.numFuncInfos = info.NrFuncInfo 215 makeSecondCall = true 216 } 217 218 if makeSecondCall { 219 if err := sys.ObjInfo(fd, &info2); err != nil { 220 return nil, err 221 } 222 } 223 224 return &pi, nil 225 } 226 227 func newProgramInfoFromProc(fd *sys.FD) (*ProgramInfo, error) { 228 var info ProgramInfo 229 err := scanFdInfo(fd, map[string]interface{}{ 230 "prog_type": &info.Type, 231 "prog_tag": &info.Tag, 232 }) 233 if errors.Is(err, errMissingFields) { 234 return nil, &internal.UnsupportedFeatureError{ 235 Name: "reading program info from /proc/self/fdinfo", 236 MinimumVersion: internal.Version{4, 10, 0}, 237 } 238 } 239 if err != nil { 240 return nil, err 241 } 242 243 return &info, nil 244 } 245 246 // ID returns the program ID. 247 // 248 // Available from 4.13. 249 // 250 // The bool return value indicates whether this optional field is available. 251 func (pi *ProgramInfo) ID() (ProgramID, bool) { 252 return pi.id, pi.id > 0 253 } 254 255 // CreatedByUID returns the Uid that created the program. 256 // 257 // Available from 4.15. 258 // 259 // The bool return value indicates whether this optional field is available. 260 func (pi *ProgramInfo) CreatedByUID() (uint32, bool) { 261 return pi.createdByUID, pi.haveCreatedByUID 262 } 263 264 // BTFID returns the BTF ID associated with the program. 265 // 266 // The ID is only valid as long as the associated program is kept alive. 267 // Available from 5.0. 268 // 269 // The bool return value indicates whether this optional field is available and 270 // populated. (The field may be available but not populated if the kernel 271 // supports the field but the program was loaded without BTF information.) 272 func (pi *ProgramInfo) BTFID() (btf.ID, bool) { 273 return pi.btf, pi.btf > 0 274 } 275 276 // RunCount returns the total number of times the program was called. 277 // 278 // Can return 0 if the collection of statistics is not enabled. See EnableStats(). 279 // The bool return value indicates whether this optional field is available. 280 func (pi *ProgramInfo) RunCount() (uint64, bool) { 281 if pi.stats != nil { 282 return pi.stats.runCount, true 283 } 284 return 0, false 285 } 286 287 // Runtime returns the total accumulated runtime of the program. 288 // 289 // Can return 0 if the collection of statistics is not enabled. See EnableStats(). 290 // The bool return value indicates whether this optional field is available. 291 func (pi *ProgramInfo) Runtime() (time.Duration, bool) { 292 if pi.stats != nil { 293 return pi.stats.runtime, true 294 } 295 return time.Duration(0), false 296 } 297 298 // RecursionMisses returns the total number of times the program was NOT called. 299 // This can happen when another bpf program is already running on the cpu, which 300 // is likely to happen for example when you interrupt bpf program execution. 301 func (pi *ProgramInfo) RecursionMisses() (uint64, bool) { 302 if pi.stats != nil { 303 return pi.stats.recursionMisses, true 304 } 305 return 0, false 306 } 307 308 // Instructions returns the 'xlated' instruction stream of the program 309 // after it has been verified and rewritten by the kernel. These instructions 310 // cannot be loaded back into the kernel as-is, this is mainly used for 311 // inspecting loaded programs for troubleshooting, dumping, etc. 312 // 313 // For example, map accesses are made to reference their kernel map IDs, 314 // not the FDs they had when the program was inserted. Note that before 315 // the introduction of bpf_insn_prepare_dump in kernel 4.16, xlated 316 // instructions were not sanitized, making the output even less reusable 317 // and less likely to round-trip or evaluate to the same program Tag. 318 // 319 // The first instruction is marked as a symbol using the Program's name. 320 // 321 // If available, the instructions will be annotated with metadata from the 322 // BTF. This includes line information and function information. Reading 323 // this metadata requires CAP_SYS_ADMIN or equivalent. If capability is 324 // unavailable, the instructions will be returned without metadata. 325 // 326 // Available from 4.13. Requires CAP_BPF or equivalent for plain instructions. 327 // Requires CAP_SYS_ADMIN for instructions with metadata. 328 func (pi *ProgramInfo) Instructions() (asm.Instructions, error) { 329 // If the calling process is not BPF-capable or if the kernel doesn't 330 // support getting xlated instructions, the field will be zero. 331 if len(pi.insns) == 0 { 332 return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported) 333 } 334 335 r := bytes.NewReader(pi.insns) 336 var insns asm.Instructions 337 if err := insns.Unmarshal(r, internal.NativeEndian); err != nil { 338 return nil, fmt.Errorf("unmarshaling instructions: %w", err) 339 } 340 341 if pi.btf != 0 { 342 btfh, err := btf.NewHandleFromID(pi.btf) 343 if err != nil { 344 // Getting a BTF handle requires CAP_SYS_ADMIN, if not available we get an -EPERM. 345 // Ignore it and fall back to instructions without metadata. 346 if !errors.Is(err, unix.EPERM) { 347 return nil, fmt.Errorf("unable to get BTF handle: %w", err) 348 } 349 } 350 351 // If we have a BTF handle, we can use it to assign metadata to the instructions. 352 if btfh != nil { 353 defer btfh.Close() 354 355 spec, err := btfh.Spec(nil) 356 if err != nil { 357 return nil, fmt.Errorf("unable to get BTF spec: %w", err) 358 } 359 360 lineInfos, err := btf.LoadLineInfos( 361 bytes.NewReader(pi.lineInfos), 362 internal.NativeEndian, 363 pi.numLineInfos, 364 spec, 365 ) 366 if err != nil { 367 return nil, fmt.Errorf("parse line info: %w", err) 368 } 369 370 funcInfos, err := btf.LoadFuncInfos( 371 bytes.NewReader(pi.funcInfos), 372 internal.NativeEndian, 373 pi.numFuncInfos, 374 spec, 375 ) 376 if err != nil { 377 return nil, fmt.Errorf("parse func info: %w", err) 378 } 379 380 btf.AssignMetadataToInstructions(insns, funcInfos, lineInfos, btf.CORERelocationInfos{}) 381 } 382 } 383 384 fn := btf.FuncMetadata(&insns[0]) 385 name := pi.Name 386 if fn != nil { 387 name = fn.Name 388 } 389 insns[0] = insns[0].WithSymbol(name) 390 391 return insns, nil 392 } 393 394 // MapIDs returns the maps related to the program. 395 // 396 // Available from 4.15. 397 // 398 // The bool return value indicates whether this optional field is available. 399 func (pi *ProgramInfo) MapIDs() ([]MapID, bool) { 400 return pi.maps, pi.maps != nil 401 } 402 403 func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error { 404 fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int())) 405 if err != nil { 406 return err 407 } 408 defer fh.Close() 409 410 if err := scanFdInfoReader(fh, fields); err != nil { 411 return fmt.Errorf("%s: %w", fh.Name(), err) 412 } 413 return nil 414 } 415 416 var errMissingFields = errors.New("missing fields") 417 418 func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error { 419 var ( 420 scanner = bufio.NewScanner(r) 421 scanned int 422 ) 423 424 for scanner.Scan() { 425 parts := strings.SplitN(scanner.Text(), "\t", 2) 426 if len(parts) != 2 { 427 continue 428 } 429 430 name := strings.TrimSuffix(parts[0], ":") 431 field, ok := fields[string(name)] 432 if !ok { 433 continue 434 } 435 436 if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 { 437 return fmt.Errorf("can't parse field %s: %v", name, err) 438 } 439 440 scanned++ 441 } 442 443 if err := scanner.Err(); err != nil { 444 return err 445 } 446 447 if len(fields) > 0 && scanned == 0 { 448 return ErrNotSupported 449 } 450 451 if scanned != len(fields) { 452 return errMissingFields 453 } 454 455 return nil 456 } 457 458 // EnableStats starts the measuring of the runtime 459 // and run counts of eBPF programs. 460 // 461 // Collecting statistics can have an impact on the performance. 462 // 463 // Requires at least 5.8. 464 func EnableStats(which uint32) (io.Closer, error) { 465 fd, err := sys.EnableStats(&sys.EnableStatsAttr{ 466 Type: which, 467 }) 468 if err != nil { 469 return nil, err 470 } 471 return fd, nil 472 } 473 474 var haveProgramInfoMapIDs = internal.NewFeatureTest("map IDs in program info", "4.15", func() error { 475 prog, err := progLoad(asm.Instructions{ 476 asm.LoadImm(asm.R0, 0, asm.DWord), 477 asm.Return(), 478 }, SocketFilter, "MIT") 479 if err != nil { 480 return err 481 } 482 defer prog.Close() 483 484 err = sys.ObjInfo(prog, &sys.ProgInfo{ 485 // NB: Don't need to allocate MapIds since the program isn't using 486 // any maps. 487 NrMapIds: 1, 488 }) 489 if errors.Is(err, unix.EINVAL) { 490 // Most likely the syscall doesn't exist. 491 return internal.ErrNotSupported 492 } 493 if errors.Is(err, unix.E2BIG) { 494 // We've hit check_uarg_tail_zero on older kernels. 495 return internal.ErrNotSupported 496 } 497 498 return err 499 })