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 }