...

Source file src/github.com/cilium/ebpf/info.go

Documentation: github.com/cilium/ebpf

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

View as plain text