github.com/inspektor-gadget/inspektor-gadget@v0.28.1/pkg/operators/ebpf/struct.go (about)

     1  // Copyright 2024 The Inspektor Gadget authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package ebpfoperator
    16  
    17  import (
    18  	"fmt"
    19  	"reflect"
    20  
    21  	"github.com/cilium/ebpf/btf"
    22  	"gopkg.in/yaml.v3"
    23  
    24  	"github.com/inspektor-gadget/inspektor-gadget/pkg/btfhelpers"
    25  	"github.com/inspektor-gadget/inspektor-gadget/pkg/columns"
    26  	"github.com/inspektor-gadget/inspektor-gadget/pkg/gadget-service/api"
    27  	metadatav1 "github.com/inspektor-gadget/inspektor-gadget/pkg/metadata/v1"
    28  )
    29  
    30  type Field struct {
    31  	metadatav1.Field
    32  	Tags   []string
    33  	Type   reflect.Type
    34  	Offset uint32
    35  	Size   uint32
    36  	parent int
    37  	name   string
    38  }
    39  
    40  type Struct struct {
    41  	Fields []*Field `yaml:"fields"`
    42  	Size   uint32
    43  }
    44  
    45  func (f *Field) FieldName() string {
    46  	return f.name
    47  }
    48  
    49  func (f *Field) FieldSize() uint32 {
    50  	return f.Size
    51  }
    52  
    53  func (f *Field) FieldOffset() uint32 {
    54  	return f.Offset
    55  }
    56  
    57  func (f *Field) FieldTags() []string {
    58  	return f.Tags
    59  }
    60  
    61  func (f *Field) FieldType() api.Kind {
    62  	if f.Type == nil {
    63  		return api.Kind_Invalid
    64  	}
    65  	switch f.Type.Kind() {
    66  	case reflect.Bool:
    67  		return api.Kind_Bool
    68  	case reflect.Int8:
    69  		return api.Kind_Int8
    70  	case reflect.Int16:
    71  		return api.Kind_Int16
    72  	case reflect.Int32:
    73  		return api.Kind_Int32
    74  	case reflect.Int64:
    75  		return api.Kind_Int64
    76  	case reflect.Uint8:
    77  		return api.Kind_Uint8
    78  	case reflect.Uint16:
    79  		return api.Kind_Uint16
    80  	case reflect.Uint32:
    81  		return api.Kind_Uint32
    82  	case reflect.Uint64:
    83  		return api.Kind_Uint64
    84  	case reflect.Float32:
    85  		return api.Kind_Float32
    86  	case reflect.Float64:
    87  		return api.Kind_Float64
    88  	}
    89  	return api.Kind_Invalid
    90  }
    91  
    92  func (f *Field) FieldAnnotations() map[string]string {
    93  	out := make(map[string]string)
    94  
    95  	for k, v := range f.Annotations {
    96  		if s, ok := v.(string); ok {
    97  			out[k] = s
    98  		} else {
    99  			// try to copy rest
   100  			out[k] = fmt.Sprintf("%v", v)
   101  		}
   102  	}
   103  
   104  	if val := f.Description; val != "" {
   105  		out["description"] = val
   106  	}
   107  
   108  	// Rewrite attributes as annotations; TODO: tbd
   109  	if val := f.Attributes.Alignment; val != "" {
   110  		out["columns.alignment"] = string(val)
   111  	}
   112  	if val := f.Attributes.Ellipsis; val != "" {
   113  		out["columns.ellipsis"] = string(val)
   114  	}
   115  	if val := f.Attributes.Width; val != 0 {
   116  		out["columns.width"] = fmt.Sprintf("%d", val)
   117  	}
   118  	if val := f.Attributes.MinWidth; val != 0 {
   119  		out["columns.minWidth"] = fmt.Sprintf("%d", val)
   120  	}
   121  	if val := f.Attributes.MaxWidth; val != 0 {
   122  		out["columns.maxWidth"] = fmt.Sprintf("%d", val)
   123  	}
   124  	if val := f.Attributes.Template; val != "" {
   125  		out["columns.template"] = val
   126  	}
   127  	if val := f.Attributes.Hidden; val {
   128  		out["hidden"] = "true"
   129  	}
   130  	return out
   131  }
   132  
   133  func (f *Field) FieldParent() int {
   134  	return f.parent
   135  }
   136  
   137  func (i *ebpfInstance) populateStructDirect(btfStruct *btf.Struct) error {
   138  	gadgetStruct := i.structs[btfStruct.Name]
   139  	existingFields := make(map[string]*Field)
   140  
   141  	if gadgetStruct == nil {
   142  		gadgetStruct = &Struct{}
   143  		i.logger.Debugf("adding struct %q", btfStruct.Name)
   144  	}
   145  
   146  	// TODO: make this validate the struct
   147  	for _, field := range gadgetStruct.Fields {
   148  		existingFields[field.Name] = field
   149  	}
   150  
   151  	i.getFieldsFromStruct(btfStruct, &gadgetStruct.Fields, "", 0, -1)
   152  
   153  	var configStruct *metadatav1.Struct
   154  	fields := i.config.Sub("structs." + btfStruct.Name)
   155  	if fields != nil {
   156  		// This feels ugly, maybe optimize
   157  		d, _ := yaml.Marshal(fields.AllSettings())
   158  		err := yaml.Unmarshal(d, &configStruct)
   159  		if err != nil {
   160  			return fmt.Errorf("invalid metadata for struct %q", btfStruct.Name)
   161  		}
   162  
   163  		// Build lookup
   164  		lookup := make(map[string]metadatav1.Field)
   165  		for _, field := range configStruct.Fields {
   166  			lookup[field.Name] = field
   167  		}
   168  
   169  		// Only handling topmost layer for now // TODO
   170  		for _, field := range gadgetStruct.Fields {
   171  			cfgField, ok := lookup[field.Name]
   172  			if !ok {
   173  				continue
   174  			}
   175  			i.logger.Debugf(" found field config for %q", field.Name)
   176  
   177  			// Fill in blanks from metadata
   178  			field.Description = cfgField.Description
   179  			field.Attributes = cfgField.Attributes
   180  			field.Annotations = cfgField.Annotations
   181  		}
   182  	}
   183  
   184  	gadgetStruct.Size = btfStruct.Size
   185  
   186  	i.structs[btfStruct.Name] = gadgetStruct
   187  	return nil
   188  }
   189  
   190  func (i *ebpfInstance) getFieldsFromMember(member btf.Member, fields *[]*Field, prefix string, offset uint32, parent int) {
   191  	refType, tags := btfhelpers.GetType(member.Type)
   192  	for i := range tags {
   193  		tags[i] = "type:" + tags[i]
   194  	}
   195  
   196  	tags = append(tags, "name:"+member.Name, api.TagSrcEbpf)
   197  
   198  	defaultAttributes := metadatav1.FieldAttributes{
   199  		Alignment: metadatav1.AlignmentLeft,
   200  		Ellipsis:  metadatav1.EllipsisEnd,
   201  	}
   202  
   203  	newField := func(size uint32, reflectType reflect.Type) *Field {
   204  		return &Field{
   205  			Field: metadatav1.Field{
   206  				Name:       prefix + member.Name,
   207  				Attributes: defaultAttributes,
   208  			},
   209  			Size:   size,
   210  			Tags:   tags,
   211  			Type:   reflectType,
   212  			Offset: offset + member.Offset.Bytes(),
   213  			parent: parent,
   214  			name:   member.Name,
   215  		}
   216  	}
   217  
   218  	// Flatten embedded structs
   219  	if t, ok := member.Type.(*btf.Struct); ok {
   220  		// Add outer struct as well
   221  		field := newField(t.Size, reflect.ArrayOf(int(t.Size), reflect.TypeOf(uint8(0))))
   222  		newParent := len(*fields)
   223  		*fields = append(*fields, field)
   224  
   225  		i.logger.Debugf(" adding field %q (%s) at %d (%v)", field.Name, "struct", field.Offset, tags)
   226  		i.getFieldsFromStruct(t, fields, prefix+member.Name+".", offset+member.Offset.Bytes(), newParent)
   227  		return
   228  	}
   229  
   230  	if t, ok := member.Type.(*btf.Union); ok {
   231  		// Add outer struct as well
   232  		field := newField(t.Size, reflect.ArrayOf(int(t.Size), reflect.TypeOf(uint8(0))))
   233  		newParent := len(*fields)
   234  		*fields = append(*fields, field)
   235  
   236  		i.logger.Debugf(" adding field %q (%s) at %d", field.Name, "union", field.Offset)
   237  		i.getFieldsFromUnion(t, fields, prefix+member.Name+".", offset+member.Offset.Bytes(), newParent)
   238  		return
   239  	}
   240  
   241  	fsize := uint32(0)
   242  	fieldType := "raw bytes"
   243  	if refType != nil {
   244  		fsize = uint32(refType.Size())
   245  		fieldType = refType.String()
   246  	}
   247  
   248  	// handling arrays as raw data
   249  	if arr, ok := member.Type.(*btf.Array); ok {
   250  		// make sure type
   251  		arrType := arr.Type
   252  
   253  		// Resolve
   254  		if typedef, ok := arrType.(*btf.Typedef); ok {
   255  			arrType = btfhelpers.GetUnderlyingType(typedef)
   256  		}
   257  
   258  		intType, ok := arrType.(*btf.Int)
   259  		if !ok {
   260  			i.logger.Debugf(" skipping field %q (%T) (array of non-int %T)", prefix+member.Name, member.Type, arr.Type)
   261  			return
   262  		}
   263  		if intType.Size != 1 {
   264  			i.logger.Debugf(" skipping field %q (%T) (array of elements with size != 1)", prefix+member.Name, member.Type)
   265  			return
   266  		}
   267  		fsize = intType.Size * arr.Nelems
   268  	}
   269  
   270  	if fsize == 0 {
   271  		i.logger.Debugf(" skipping field %q (%T)", prefix+member.Name, member.Type)
   272  		return
   273  	}
   274  
   275  	// Keep enums to convert them to strings
   276  	if enum, ok := member.Type.(*btf.Enum); ok {
   277  		i.enums[member.Name] = enum
   278  	}
   279  
   280  	field := newField(fsize, refType)
   281  	if refType != nil {
   282  		field.Field.Attributes.Width = uint(columns.GetWidthFromType(refType.Kind()))
   283  	}
   284  
   285  	i.logger.Debugf(" adding field %q (%s) at %d (parent %d) (%v)", field.Name, fieldType, field.Offset, parent, tags)
   286  	*fields = append(*fields, field)
   287  }
   288  
   289  func (i *ebpfInstance) getFieldsFromStruct(btfStruct *btf.Struct, fields *[]*Field, prefix string, offset uint32, parent int) {
   290  	for _, member := range btfStruct.Members {
   291  		i.getFieldsFromMember(member, fields, prefix, offset, parent)
   292  	}
   293  }
   294  
   295  func (i *ebpfInstance) getFieldsFromUnion(btfStruct *btf.Union, fields *[]*Field, prefix string, offset uint32, parent int) {
   296  	for _, member := range btfStruct.Members {
   297  		i.getFieldsFromMember(member, fields, prefix, offset, parent)
   298  	}
   299  }