info.go 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. package ebpf
  2. import (
  3. "bufio"
  4. "bytes"
  5. "encoding/hex"
  6. "errors"
  7. "fmt"
  8. "io"
  9. "os"
  10. "strings"
  11. "syscall"
  12. "time"
  13. "unsafe"
  14. "github.com/cilium/ebpf/asm"
  15. "github.com/cilium/ebpf/btf"
  16. "github.com/cilium/ebpf/internal"
  17. "github.com/cilium/ebpf/internal/sys"
  18. "github.com/cilium/ebpf/internal/unix"
  19. )
  20. // MapInfo describes a map.
  21. type MapInfo struct {
  22. Type MapType
  23. id MapID
  24. KeySize uint32
  25. ValueSize uint32
  26. MaxEntries uint32
  27. Flags uint32
  28. // Name as supplied by user space at load time. Available from 4.15.
  29. Name string
  30. }
  31. func newMapInfoFromFd(fd *sys.FD) (*MapInfo, error) {
  32. var info sys.MapInfo
  33. err := sys.ObjInfo(fd, &info)
  34. if errors.Is(err, syscall.EINVAL) {
  35. return newMapInfoFromProc(fd)
  36. }
  37. if err != nil {
  38. return nil, err
  39. }
  40. return &MapInfo{
  41. MapType(info.Type),
  42. MapID(info.Id),
  43. info.KeySize,
  44. info.ValueSize,
  45. info.MaxEntries,
  46. info.MapFlags,
  47. unix.ByteSliceToString(info.Name[:]),
  48. }, nil
  49. }
  50. func newMapInfoFromProc(fd *sys.FD) (*MapInfo, error) {
  51. var mi MapInfo
  52. err := scanFdInfo(fd, map[string]interface{}{
  53. "map_type": &mi.Type,
  54. "key_size": &mi.KeySize,
  55. "value_size": &mi.ValueSize,
  56. "max_entries": &mi.MaxEntries,
  57. "map_flags": &mi.Flags,
  58. })
  59. if err != nil {
  60. return nil, err
  61. }
  62. return &mi, nil
  63. }
  64. // ID returns the map ID.
  65. //
  66. // Available from 4.13.
  67. //
  68. // The bool return value indicates whether this optional field is available.
  69. func (mi *MapInfo) ID() (MapID, bool) {
  70. return mi.id, mi.id > 0
  71. }
  72. // programStats holds statistics of a program.
  73. type programStats struct {
  74. // Total accumulated runtime of the program ins ns.
  75. runtime time.Duration
  76. // Total number of times the program was called.
  77. runCount uint64
  78. }
  79. // ProgramInfo describes a program.
  80. type ProgramInfo struct {
  81. Type ProgramType
  82. id ProgramID
  83. // Truncated hash of the BPF bytecode. Available from 4.13.
  84. Tag string
  85. // Name as supplied by user space at load time. Available from 4.15.
  86. Name string
  87. btf btf.ID
  88. stats *programStats
  89. maps []MapID
  90. insns []byte
  91. }
  92. func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) {
  93. var info sys.ProgInfo
  94. err := sys.ObjInfo(fd, &info)
  95. if errors.Is(err, syscall.EINVAL) {
  96. return newProgramInfoFromProc(fd)
  97. }
  98. if err != nil {
  99. return nil, err
  100. }
  101. pi := ProgramInfo{
  102. Type: ProgramType(info.Type),
  103. id: ProgramID(info.Id),
  104. Tag: hex.EncodeToString(info.Tag[:]),
  105. Name: unix.ByteSliceToString(info.Name[:]),
  106. btf: btf.ID(info.BtfId),
  107. stats: &programStats{
  108. runtime: time.Duration(info.RunTimeNs),
  109. runCount: info.RunCnt,
  110. },
  111. }
  112. // Start with a clean struct for the second call, otherwise we may get EFAULT.
  113. var info2 sys.ProgInfo
  114. if info.NrMapIds > 0 {
  115. pi.maps = make([]MapID, info.NrMapIds)
  116. info2.NrMapIds = info.NrMapIds
  117. info2.MapIds = sys.NewPointer(unsafe.Pointer(&pi.maps[0]))
  118. }
  119. if info.XlatedProgLen > 0 {
  120. pi.insns = make([]byte, info.XlatedProgLen)
  121. info2.XlatedProgLen = info.XlatedProgLen
  122. info2.XlatedProgInsns = sys.NewSlicePointer(pi.insns)
  123. }
  124. if info.NrMapIds > 0 || info.XlatedProgLen > 0 {
  125. if err := sys.ObjInfo(fd, &info2); err != nil {
  126. return nil, err
  127. }
  128. }
  129. return &pi, nil
  130. }
  131. func newProgramInfoFromProc(fd *sys.FD) (*ProgramInfo, error) {
  132. var info ProgramInfo
  133. err := scanFdInfo(fd, map[string]interface{}{
  134. "prog_type": &info.Type,
  135. "prog_tag": &info.Tag,
  136. })
  137. if errors.Is(err, errMissingFields) {
  138. return nil, &internal.UnsupportedFeatureError{
  139. Name: "reading program info from /proc/self/fdinfo",
  140. MinimumVersion: internal.Version{4, 10, 0},
  141. }
  142. }
  143. if err != nil {
  144. return nil, err
  145. }
  146. return &info, nil
  147. }
  148. // ID returns the program ID.
  149. //
  150. // Available from 4.13.
  151. //
  152. // The bool return value indicates whether this optional field is available.
  153. func (pi *ProgramInfo) ID() (ProgramID, bool) {
  154. return pi.id, pi.id > 0
  155. }
  156. // BTFID returns the BTF ID associated with the program.
  157. //
  158. // The ID is only valid as long as the associated program is kept alive.
  159. // Available from 5.0.
  160. //
  161. // The bool return value indicates whether this optional field is available and
  162. // populated. (The field may be available but not populated if the kernel
  163. // supports the field but the program was loaded without BTF information.)
  164. func (pi *ProgramInfo) BTFID() (btf.ID, bool) {
  165. return pi.btf, pi.btf > 0
  166. }
  167. // RunCount returns the total number of times the program was called.
  168. //
  169. // Can return 0 if the collection of statistics is not enabled. See EnableStats().
  170. // The bool return value indicates whether this optional field is available.
  171. func (pi *ProgramInfo) RunCount() (uint64, bool) {
  172. if pi.stats != nil {
  173. return pi.stats.runCount, true
  174. }
  175. return 0, false
  176. }
  177. // Runtime returns the total accumulated runtime of the program.
  178. //
  179. // Can return 0 if the collection of statistics is not enabled. See EnableStats().
  180. // The bool return value indicates whether this optional field is available.
  181. func (pi *ProgramInfo) Runtime() (time.Duration, bool) {
  182. if pi.stats != nil {
  183. return pi.stats.runtime, true
  184. }
  185. return time.Duration(0), false
  186. }
  187. // Instructions returns the 'xlated' instruction stream of the program
  188. // after it has been verified and rewritten by the kernel. These instructions
  189. // cannot be loaded back into the kernel as-is, this is mainly used for
  190. // inspecting loaded programs for troubleshooting, dumping, etc.
  191. //
  192. // For example, map accesses are made to reference their kernel map IDs,
  193. // not the FDs they had when the program was inserted. Note that before
  194. // the introduction of bpf_insn_prepare_dump in kernel 4.16, xlated
  195. // instructions were not sanitized, making the output even less reusable
  196. // and less likely to round-trip or evaluate to the same program Tag.
  197. //
  198. // The first instruction is marked as a symbol using the Program's name.
  199. //
  200. // Available from 4.13. Requires CAP_BPF or equivalent.
  201. func (pi *ProgramInfo) Instructions() (asm.Instructions, error) {
  202. // If the calling process is not BPF-capable or if the kernel doesn't
  203. // support getting xlated instructions, the field will be zero.
  204. if len(pi.insns) == 0 {
  205. return nil, fmt.Errorf("insufficient permissions or unsupported kernel: %w", ErrNotSupported)
  206. }
  207. r := bytes.NewReader(pi.insns)
  208. var insns asm.Instructions
  209. if err := insns.Unmarshal(r, internal.NativeEndian); err != nil {
  210. return nil, fmt.Errorf("unmarshaling instructions: %w", err)
  211. }
  212. // Tag the first instruction with the name of the program, if available.
  213. insns[0] = insns[0].WithSymbol(pi.Name)
  214. return insns, nil
  215. }
  216. // MapIDs returns the maps related to the program.
  217. //
  218. // Available from 4.15.
  219. //
  220. // The bool return value indicates whether this optional field is available.
  221. func (pi *ProgramInfo) MapIDs() ([]MapID, bool) {
  222. return pi.maps, pi.maps != nil
  223. }
  224. func scanFdInfo(fd *sys.FD, fields map[string]interface{}) error {
  225. fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", fd.Int()))
  226. if err != nil {
  227. return err
  228. }
  229. defer fh.Close()
  230. if err := scanFdInfoReader(fh, fields); err != nil {
  231. return fmt.Errorf("%s: %w", fh.Name(), err)
  232. }
  233. return nil
  234. }
  235. var errMissingFields = errors.New("missing fields")
  236. func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
  237. var (
  238. scanner = bufio.NewScanner(r)
  239. scanned int
  240. )
  241. for scanner.Scan() {
  242. parts := strings.SplitN(scanner.Text(), "\t", 2)
  243. if len(parts) != 2 {
  244. continue
  245. }
  246. name := strings.TrimSuffix(parts[0], ":")
  247. field, ok := fields[string(name)]
  248. if !ok {
  249. continue
  250. }
  251. if n, err := fmt.Sscanln(parts[1], field); err != nil || n != 1 {
  252. return fmt.Errorf("can't parse field %s: %v", name, err)
  253. }
  254. scanned++
  255. }
  256. if err := scanner.Err(); err != nil {
  257. return err
  258. }
  259. if len(fields) > 0 && scanned == 0 {
  260. return ErrNotSupported
  261. }
  262. if scanned != len(fields) {
  263. return errMissingFields
  264. }
  265. return nil
  266. }
  267. // EnableStats starts the measuring of the runtime
  268. // and run counts of eBPF programs.
  269. //
  270. // Collecting statistics can have an impact on the performance.
  271. //
  272. // Requires at least 5.8.
  273. func EnableStats(which uint32) (io.Closer, error) {
  274. fd, err := sys.EnableStats(&sys.EnableStatsAttr{
  275. Type: which,
  276. })
  277. if err != nil {
  278. return nil, err
  279. }
  280. return fd, nil
  281. }