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  }