Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

btf: Optimizing BTF parsing by merging readTypes and inflateRawTypes #1211

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions btf/btf.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,7 @@ func loadRawSpec(btf io.ReaderAt, bo binary.ByteOrder, base *Spec) (*Spec, error
}
}

rawTypes, rawStrings, err := parseBTF(btf, bo, baseStrings)
if err != nil {
return nil, err
}

types, err := inflateRawTypes(rawTypes, rawStrings, base)
types, rawStrings, err := parseBTF(btf, bo, baseStrings, base)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -373,7 +368,7 @@ func guessRawBTFByteOrder(r io.ReaderAt) binary.ByteOrder {

// parseBTF reads a .BTF section into memory and parses it into a list of
// raw types and a string table.
func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([]rawType, *stringTable, error) {
func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable, base *Spec) ([]Type, *stringTable, error) {
buf := internal.NewBufferedSectionReader(btf, 0, math.MaxInt64)
header, err := parseBTFHeader(buf, bo)
if err != nil {
Expand All @@ -387,12 +382,12 @@ func parseBTF(btf io.ReaderAt, bo binary.ByteOrder, baseStrings *stringTable) ([
}

buf.Reset(io.NewSectionReader(btf, header.typeStart(), int64(header.TypeLen)))
rawTypes, err := readTypes(buf, bo, header.TypeLen)
types, err := readAndInflateTypes(buf, bo, header.TypeLen, rawStrings, base)
if err != nil {
return nil, nil, fmt.Errorf("can't read types: %w", err)
return nil, nil, err
}

return rawTypes, rawStrings, nil
return types, rawStrings, nil
}

type symbol struct {
Expand Down
206 changes: 147 additions & 59 deletions btf/btf_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,19 @@ type btfType struct {
SizeType uint32
}

var btfTypeSize = int(unsafe.Sizeof(btfType{}))

func unmarshalBtfType(bt *btfType, b []byte, bo binary.ByteOrder) (int, error) {
if len(b) < btfTypeSize {
return 0, fmt.Errorf("not enough bytes to unmarshal btfType")
}

bt.NameOff = bo.Uint32(b[0:])
bt.Info = bo.Uint32(b[4:])
bt.SizeType = bo.Uint32(b[8:])
return btfTypeSize, nil
}

func mask(len uint32) uint32 {
return (1 << len) - 1
}
Expand Down Expand Up @@ -300,6 +313,17 @@ const (
btfIntBitsShift = 0
)

var btfIntLen = int(unsafe.Sizeof(btfInt{}))

func unmarshalBtfInt(bi *btfInt, b []byte, bo binary.ByteOrder) (int, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not make these methods on btfInt and so on?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works for the single types, but for the types where we have slices such as []btfMember and []btfParam its nice to have a function to marshal the whole slice and do the bounds checks ect.

I thought this worked just as well.

We can also go the method route but we would have to declare a type alias for the slices to be able to add methods to them.

type btfMembers []btfMember

func (bm btfMembers) Marshal(b []byte, bo binary.ByteOrder) (int, error) { ....

But that seemed like to much added code for the same effect.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough. Medium term we need to get rid of all of these manual marshalers anyways.

if len(b) < btfIntLen {
return 0, fmt.Errorf("not enough bytes to unmarshal btfInt")
}

bi.Raw = bo.Uint32(b[0:])
return btfIntLen, nil
}

func (bi btfInt) Encoding() IntEncoding {
return IntEncoding(readBits(bi.Raw, btfIntEncodingLen, btfIntEncodingShift))
}
Expand Down Expand Up @@ -330,102 +354,166 @@ type btfArray struct {
Nelems uint32
}

var btfArrayLen = int(unsafe.Sizeof(btfArray{}))

func unmarshalBtfArray(ba *btfArray, b []byte, bo binary.ByteOrder) (int, error) {
if len(b) < btfArrayLen {
return 0, fmt.Errorf("not enough bytes to unmarshal btfArray")
}

ba.Type = TypeID(bo.Uint32(b[0:]))
ba.IndexType = TypeID(bo.Uint32(b[4:]))
ba.Nelems = bo.Uint32(b[8:])
return btfArrayLen, nil
}

type btfMember struct {
NameOff uint32
Type TypeID
Offset uint32
}

var btfMemberLen = int(unsafe.Sizeof(btfMember{}))

func unmarshalBtfMembers(members []btfMember, b []byte, bo binary.ByteOrder) (int, error) {
off := 0
for i := range members {
if off+btfMemberLen > len(b) {
return 0, fmt.Errorf("not enough bytes to unmarshal btfMember %d", i)
}

members[i].NameOff = bo.Uint32(b[off+0:])
members[i].Type = TypeID(bo.Uint32(b[off+4:]))
members[i].Offset = bo.Uint32(b[off+8:])

off += btfMemberLen
}

return off, nil
}

type btfVarSecinfo struct {
Type TypeID
Offset uint32
Size uint32
}

var btfVarSecinfoLen = int(unsafe.Sizeof(btfVarSecinfo{}))

func unmarshalBtfVarSecInfos(secinfos []btfVarSecinfo, b []byte, bo binary.ByteOrder) (int, error) {
off := 0
for i := range secinfos {
if off+btfVarSecinfoLen > len(b) {
return 0, fmt.Errorf("not enough bytes to unmarshal btfVarSecinfo %d", i)
}

secinfos[i].Type = TypeID(bo.Uint32(b[off+0:]))
secinfos[i].Offset = bo.Uint32(b[off+4:])
secinfos[i].Size = bo.Uint32(b[off+8:])

off += btfVarSecinfoLen
}

return off, nil
}

type btfVariable struct {
Linkage uint32
}

var btfVariableLen = int(unsafe.Sizeof(btfVariable{}))

func unmarshalBtfVariable(bv *btfVariable, b []byte, bo binary.ByteOrder) (int, error) {
if len(b) < btfVariableLen {
return 0, fmt.Errorf("not enough bytes to unmarshal btfVariable")
}

bv.Linkage = bo.Uint32(b[0:])
return btfVariableLen, nil
}

type btfEnum struct {
NameOff uint32
Val uint32
}

var btfEnumLen = int(unsafe.Sizeof(btfEnum{}))

func unmarshalBtfEnums(enums []btfEnum, b []byte, bo binary.ByteOrder) (int, error) {
off := 0
for i := range enums {
if off+btfEnumLen > len(b) {
return 0, fmt.Errorf("not enough bytes to unmarshal btfEnum %d", i)
}

enums[i].NameOff = bo.Uint32(b[off+0:])
enums[i].Val = bo.Uint32(b[off+4:])

off += btfEnumLen
}

return off, nil
}

type btfEnum64 struct {
NameOff uint32
ValLo32 uint32
ValHi32 uint32
}

var btfEnum64Len = int(unsafe.Sizeof(btfEnum64{}))

func unmarshalBtfEnums64(enums []btfEnum64, b []byte, bo binary.ByteOrder) (int, error) {
off := 0
for i := range enums {
if off+btfEnum64Len > len(b) {
return 0, fmt.Errorf("not enough bytes to unmarshal btfEnum64 %d", i)
}

enums[i].NameOff = bo.Uint32(b[off+0:])
enums[i].ValLo32 = bo.Uint32(b[off+4:])
enums[i].ValHi32 = bo.Uint32(b[off+8:])

off += btfEnum64Len
}

return off, nil
}

type btfParam struct {
NameOff uint32
Type TypeID
}

type btfDeclTag struct {
ComponentIdx uint32
}
var btfParamLen = int(unsafe.Sizeof(btfParam{}))

func readTypes(r io.Reader, bo binary.ByteOrder, typeLen uint32) ([]rawType, error) {
var header btfType
// because of the interleaving between types and struct members it is difficult to
// precompute the numbers of raw types this will parse
// this "guess" is a good first estimation
sizeOfbtfType := uintptr(btfTypeLen)
tyMaxCount := uintptr(typeLen) / sizeOfbtfType / 2
types := make([]rawType, 0, tyMaxCount)

for id := TypeID(1); ; id++ {
if err := binary.Read(r, bo, &header); err == io.EOF {
return types, nil
} else if err != nil {
return nil, fmt.Errorf("can't read type info for id %v: %v", id, err)
func unmarshalBtfParams(params []btfParam, b []byte, bo binary.ByteOrder) (int, error) {
off := 0
for i := range params {
if off+btfParamLen > len(b) {
return 0, fmt.Errorf("not enough bytes to unmarshal btfParam %d", i)
}

var data interface{}
switch header.Kind() {
case kindInt:
data = new(btfInt)
case kindPointer:
case kindArray:
data = new(btfArray)
case kindStruct:
fallthrough
case kindUnion:
data = make([]btfMember, header.Vlen())
case kindEnum:
data = make([]btfEnum, header.Vlen())
case kindForward:
case kindTypedef:
case kindVolatile:
case kindConst:
case kindRestrict:
case kindFunc:
case kindFuncProto:
data = make([]btfParam, header.Vlen())
case kindVar:
data = new(btfVariable)
case kindDatasec:
data = make([]btfVarSecinfo, header.Vlen())
case kindFloat:
case kindDeclTag:
data = new(btfDeclTag)
case kindTypeTag:
case kindEnum64:
data = make([]btfEnum64, header.Vlen())
default:
return nil, fmt.Errorf("type id %v: unknown kind: %v", id, header.Kind())
}
params[i].NameOff = bo.Uint32(b[off+0:])
params[i].Type = TypeID(bo.Uint32(b[off+4:]))

if data == nil {
types = append(types, rawType{header, nil})
continue
}
off += btfParamLen
}

if err := binary.Read(r, bo, data); err != nil {
return nil, fmt.Errorf("type id %d: kind %v: can't read %T: %v", id, header.Kind(), data, err)
}
return off, nil
}

type btfDeclTag struct {
ComponentIdx uint32
}

types = append(types, rawType{header, data})
var btfDeclTagLen = int(unsafe.Sizeof(btfDeclTag{}))

func unmarshalBtfDeclTag(bdt *btfDeclTag, b []byte, bo binary.ByteOrder) (int, error) {
if len(b) < btfDeclTagLen {
return 0, fmt.Errorf("not enough bytes to unmarshal btfDeclTag")
}

bdt.ComponentIdx = bo.Uint32(b[0:])
return btfDeclTagLen, nil
}
Loading