Skip to content

Commit

Permalink
Merge pull request inspektor-gadget#2521 from inspektor-gadget/michae…
Browse files Browse the repository at this point in the history
…l/refactoring-helpers-2

add btfhelpers package
  • Loading branch information
flyth authored Mar 25, 2024
2 parents 22bee32 + 6c5f29c commit d0b0822
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 27 deletions.
271 changes: 271 additions & 0 deletions pkg/btfhelpers/bpfhelpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package btfhelpers

import (
"reflect"
"testing"

"github.com/cilium/ebpf/btf"
"github.com/stretchr/testify/assert"
)

var int32Type = &btf.Int{
Encoding: btf.Signed,
Size: 4,
Name: "int32",
}

func TestGetType(t *testing.T) {
t.Parallel()

tests := []struct {
name string
typ btf.Type
expectedType reflect.Type
expectedNames []string
}{
{
name: "int8",
typ: &btf.Int{
Encoding: btf.Signed,
Size: 1,
Name: "int8",
},
expectedType: reflect.TypeOf(int8(0)),
expectedNames: []string{"int8"},
},
{
name: "int16",
typ: &btf.Int{
Encoding: btf.Signed,
Size: 2,
Name: "int16",
},
expectedType: reflect.TypeOf(int16(0)),
expectedNames: []string{"int16"},
},
{
name: "int32",
typ: &btf.Int{
Encoding: btf.Signed,
Size: 4,
Name: "int32",
},
expectedType: reflect.TypeOf(int32(0)),
expectedNames: []string{"int32"},
},
{
name: "int64",
typ: &btf.Int{
Encoding: btf.Signed,
Size: 8,
Name: "int64",
},
expectedType: reflect.TypeOf(int64(0)),
expectedNames: []string{"int64"},
},
{
name: "uint8",
typ: &btf.Int{
Encoding: btf.Unsigned,
Size: 1,
Name: "uint8",
},
expectedType: reflect.TypeOf(uint8(0)),
expectedNames: []string{"uint8"},
},
{
name: "uint16",
typ: &btf.Int{
Encoding: btf.Unsigned,
Size: 2,
Name: "uint16",
},
expectedType: reflect.TypeOf(uint16(0)),
expectedNames: []string{"uint16"},
},
{
name: "uint32",
typ: &btf.Int{
Encoding: btf.Unsigned,
Size: 4,
Name: "uint32",
},
expectedType: reflect.TypeOf(uint32(0)),
expectedNames: []string{"uint32"},
},
{
name: "uint64",
typ: &btf.Int{
Encoding: btf.Unsigned,
Size: 8,
Name: "uint64",
},
expectedType: reflect.TypeOf(uint64(0)),
expectedNames: []string{"uint64"},
},
{
name: "bool",
typ: &btf.Int{
Encoding: btf.Bool,
Size: 1,
Name: "bool",
},
expectedType: reflect.TypeOf(false),
expectedNames: []string{"bool"},
},
{
name: "char",
typ: &btf.Int{
Encoding: btf.Char,
Size: 1,
Name: "char",
},
expectedType: reflect.TypeOf(uint8(0)),
expectedNames: []string{"char"},
},
{
name: "float32",
typ: &btf.Float{
Size: 4,
Name: "float32",
},
expectedType: reflect.TypeOf(float32(0)),
expectedNames: []string{"float32"},
},
{
name: "float64",
typ: &btf.Float{
Size: 8,
Name: "float64",
},
expectedType: reflect.TypeOf(float64(0)),
expectedNames: []string{"float64"},
},
{
name: "typedef",
typ: &btf.Typedef{
Type: &btf.Int{
Encoding: btf.Signed,
Size: 4,
Name: "int32",
},
Name: "typedef",
},
expectedType: reflect.TypeOf(int32(0)),
expectedNames: []string{"typedef", "int32"},
},
{
name: "typedef typedef",
typ: &btf.Typedef{
Type: &btf.Typedef{
Type: int32Type,
Name: "typedef2",
},
Name: "typedef1",
},
expectedType: reflect.TypeOf(int32(0)),
expectedNames: []string{"typedef1", "typedef2", "int32"},
},
{
name: "array",
typ: &btf.Array{
Type: int32Type,
Nelems: 10,
},
expectedType: reflect.ArrayOf(10, reflect.TypeOf(int32(0))),
expectedNames: []string{"int32"},
},
{
name: "array of arrays",
typ: &btf.Array{
Type: &btf.Array{
Type: int32Type,
Nelems: 10,
},
Nelems: 10,
},
expectedType: nil,
expectedNames: nil,
},
{
name: "unknown",
typ: &btf.Void{},
expectedNames: []string{},
},
{
name: "unnamed",
typ: &btf.Int{
Encoding: btf.Unsigned,
Size: 2,
},
expectedType: reflect.TypeOf(uint16(0)),
expectedNames: []string{},
},
// TODO: checks structures
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
t.Parallel()
retTyp, retNames := GetType(tt.typ)
assert.Equal(t, tt.expectedType, retTyp)
assert.Equal(t, tt.expectedNames, retNames)
})
}
}

func TestGetUnderlyingType(t *testing.T) {
t.Parallel()

tests := []struct {
name string
typDef *btf.Typedef
expectedType btf.Type
}{
{
name: "typedef",
typDef: &btf.Typedef{
Type: int32Type,
Name: "typedef",
},
expectedType: int32Type,
},
{
name: "typedef typedef",
typDef: &btf.Typedef{
Type: &btf.Typedef{
Type: int32Type,
Name: "typedef",
},
Name: "typedef",
},
expectedType: int32Type,
},
}

for _, tt := range tests {
tt := tt

t.Run(tt.name, func(t *testing.T) {
t.Parallel()
retTyp := GetUnderlyingType(tt.typDef)
assert.Equal(t, tt.expectedType, retTyp)
})
}
}
111 changes: 111 additions & 0 deletions pkg/btfhelpers/btfhelpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright 2024 The Inspektor Gadget authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package btfhelpers provides a couple of helper functions to bridge Go's reflection system with
// types from BTF
package btfhelpers

import (
"reflect"

"github.com/cilium/ebpf/btf"
)

// GetType returns the reflect.Type for a given BTF type and the list of type names found while
// resolving it.
func GetType(typ btf.Type) (reflect.Type, []string) {
var refType reflect.Type
typeNames := []string{}

if typ.TypeName() != "" {
typeNames = append(typeNames, typ.TypeName())
}

switch typed := typ.(type) {
case *btf.Array:
arrType := getSimpleType(typed.Type)
if arrType == nil {
return nil, nil
}
if typed.Type.TypeName() != "" {
typeNames = append(typeNames, typed.Type.TypeName())
}
refType = reflect.ArrayOf(int(typed.Nelems), arrType)
case *btf.Typedef:
switch typed := typ.(type) {
case *btf.Typedef:
refType, typeNames2 := GetType(typed.Type)
typeNames = append(typeNames, typeNames2...)
return refType, typeNames
default:
return GetType(typed)
}
default:
refType = getSimpleType(typ)
}

return refType, typeNames
}

// GetUnderlyingType returns the underlying type of a typedef
func GetUnderlyingType(tf *btf.Typedef) btf.Type {
switch typed := tf.Type.(type) {
case *btf.Typedef:
return GetUnderlyingType(typed)
default:
return typed
}
}

func getSimpleType(typ btf.Type) reflect.Type {
switch typed := typ.(type) {
case *btf.Int:
switch typed.Encoding {
case btf.Signed:
switch typed.Size {
case 1:
return reflect.TypeOf(int8(0))
case 2:
return reflect.TypeOf(int16(0))
case 4:
return reflect.TypeOf(int32(0))
case 8:
return reflect.TypeOf(int64(0))
}
case btf.Unsigned:
switch typed.Size {
case 1:
return reflect.TypeOf(uint8(0))
case 2:
return reflect.TypeOf(uint16(0))
case 4:
return reflect.TypeOf(uint32(0))
case 8:
return reflect.TypeOf(uint64(0))
}
case btf.Bool:
return reflect.TypeOf(false)
case btf.Char:
return reflect.TypeOf(uint8(0))
}
case *btf.Float:
switch typed.Size {
case 4:
return reflect.TypeOf(float32(0))
case 8:
return reflect.TypeOf(float64(0))
}
}
return nil
}
Loading

0 comments on commit d0b0822

Please sign in to comment.