Skip to content

Commit 6c76107

Browse files
btf: Refactor AnyTypeByName to accept a exact parameter
In the previous commit we added new methods for non-exact type matching, matches where the essential name matches but the suffixes may differ. This commit removes those new methods and instead adds an exact parameter to the existing methods. This is a breaking change be it results in cleaner code overall. Signed-off-by: Dylan Reimerink <dylan.reimerink@isovalent.com>
1 parent c5dd0c7 commit 6c76107

15 files changed

Lines changed: 67 additions & 151 deletions

File tree

btf/btf.go

Lines changed: 7 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -396,38 +396,20 @@ func (s *Spec) TypeID(typ Type) (TypeID, error) {
396396
// data structure.
397397
//
398398
// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.
399-
func (s *Spec) AnyTypesByName(name string) ([]Type, error) {
400-
types, err := s.AnyTypesByEssentialName(name)
399+
func (s *Spec) AnyTypesByName(name string, exact bool) ([]Type, error) {
400+
types, err := s.TypesByName(newEssentialName(name))
401401
if err != nil {
402402
return nil, err
403403
}
404404

405405
for i := 0; i < len(types); i++ {
406406
// Match against the full name, not just the essential one
407407
// in case the type being looked up is a struct flavor.
408-
if types[i].TypeName() != name {
408+
if exact && types[i].TypeName() != name {
409409
types = slices.Delete(types, i, i+1)
410410
continue
411411
}
412-
}
413-
414-
return types, nil
415-
}
416412

417-
// AnyTypesByEssentialName returns a list of BTF Types with the same essential name.
418-
//
419-
// If the BTF blob describes multiple compilation units like vmlinux, multiple
420-
// Types with the same name and kind can exist, but might not describe the same
421-
// data structure.
422-
//
423-
// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.
424-
func (s *Spec) AnyTypesByEssentialName(name string) ([]Type, error) {
425-
types, err := s.TypesByName(newEssentialName(name))
426-
if err != nil {
427-
return nil, err
428-
}
429-
430-
for i := 0; i < len(types); i++ {
431413
if err := s.elf.fixupDatasec(types[i]); err != nil {
432414
return nil, err
433415
}
@@ -439,8 +421,8 @@ func (s *Spec) AnyTypesByEssentialName(name string) ([]Type, error) {
439421
// AnyTypeByName returns a Type with the given name.
440422
//
441423
// Returns an error if multiple types of that name exist.
442-
func (s *Spec) AnyTypeByName(name string) (Type, error) {
443-
types, err := s.AnyTypesByName(name)
424+
func (s *Spec) AnyTypeByName(name string, exact bool) (Type, error) {
425+
types, err := s.AnyTypesByName(name, exact)
444426
if err != nil {
445427
return nil, err
446428
}
@@ -461,73 +443,7 @@ func (s *Spec) AnyTypeByName(name string) (Type, error) {
461443
//
462444
// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.
463445
// Returns an error wrapping ErrMultipleTypes if multiple candidates are found.
464-
func (s *Spec) TypeByEssentialName(name string, typ any) error {
465-
if err := internal.IsNilPointer(typ); err != nil {
466-
return fmt.Errorf("type argument: %w", err)
467-
}
468-
469-
typeInterface := reflect.TypeFor[Type]()
470-
471-
// typ may be **T or *Type
472-
typValue := reflect.ValueOf(typ)
473-
if typValue.Kind() != reflect.Pointer {
474-
return fmt.Errorf("%T is not a pointer", typ)
475-
}
476-
typPtr := typValue.Elem()
477-
if !typPtr.CanSet() {
478-
return fmt.Errorf("%T cannot be set", typ)
479-
}
480-
481-
wanted := typPtr.Type()
482-
if wanted == typeInterface {
483-
// This is *Type. Unwrap the value's type.
484-
if typPtr.IsNil() {
485-
return fmt.Errorf("%T points to a nil Type", typ)
486-
}
487-
wanted = typPtr.Elem().Type()
488-
}
489-
490-
if !wanted.AssignableTo(typeInterface) {
491-
return fmt.Errorf("%T does not satisfy Type interface", typ)
492-
}
493-
494-
types, err := s.AnyTypesByEssentialName(name)
495-
if err != nil {
496-
return err
497-
}
498-
499-
var candidate Type
500-
for _, typ := range types {
501-
if reflect.TypeOf(typ) != wanted {
502-
continue
503-
}
504-
505-
if candidate != nil {
506-
return fmt.Errorf("type %s(%T): %w", name, typ, ErrMultipleMatches)
507-
}
508-
509-
candidate = typ
510-
}
511-
512-
if candidate == nil {
513-
return fmt.Errorf("%s %s: %w", wanted, name, ErrNotFound)
514-
}
515-
516-
typPtr.Set(reflect.ValueOf(candidate))
517-
518-
return nil
519-
}
520-
521-
// TypeByName searches for a Type with a specific name. Since multiple Types
522-
// with the same name can exist, the parameter typ is taken to narrow down the
523-
// search in case of a clash.
524-
//
525-
// typ must be a non-nil pointer to an implementation of a Type. On success, the
526-
// address of the found Type will be copied to typ.
527-
//
528-
// Returns an error wrapping ErrNotFound if no matching Type exists in the Spec.
529-
// Returns an error wrapping ErrMultipleTypes if multiple candidates are found.
530-
func (s *Spec) TypeByName(name string, typ any) error {
446+
func (s *Spec) TypeByName(name string, exact bool, typ any) error {
531447
if err := internal.IsNilPointer(typ); err != nil {
532448
return fmt.Errorf("type argument: %w", err)
533449
}
@@ -557,7 +473,7 @@ func (s *Spec) TypeByName(name string, typ any) error {
557473
return fmt.Errorf("%T does not satisfy Type interface", typ)
558474
}
559475

560-
types, err := s.AnyTypesByName(name)
476+
types, err := s.AnyTypesByName(name, exact)
561477
if err != nil {
562478
return err
563479
}

btf/btf_test.go

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func TestAnyTypesByName(t *testing.T) {
9090
testutils.Files(t, testutils.Glob(t, "testdata/relocs-*.elf"), func(t *testing.T, file string) {
9191
spec := parseELFBTF(t, file)
9292

93-
types, err := spec.AnyTypesByName("ambiguous")
93+
types, err := spec.AnyTypesByName("ambiguous", true)
9494
if err != nil {
9595
t.Fatal(err)
9696
}
@@ -99,7 +99,7 @@ func TestAnyTypesByName(t *testing.T) {
9999
t.Fatalf("expected to receive exactly 1 types from querying ambiguous type, got: %v", types)
100100
}
101101

102-
types, err = spec.AnyTypesByName("ambiguous___flavour")
102+
types, err = spec.AnyTypesByName("ambiguous___flavour", true)
103103
if err != nil {
104104
t.Fatal(err)
105105
}
@@ -115,15 +115,15 @@ func TestTypeByNameAmbiguous(t *testing.T) {
115115
spec := parseELFBTF(t, file)
116116

117117
var typ *Struct
118-
if err := spec.TypeByName("ambiguous", &typ); err != nil {
118+
if err := spec.TypeByName("ambiguous", true, &typ); err != nil {
119119
t.Fatal(err)
120120
}
121121

122122
if name := typ.TypeName(); name != "ambiguous" {
123123
t.Fatal("expected type name 'ambiguous', got:", name)
124124
}
125125

126-
if err := spec.TypeByName("ambiguous___flavour", &typ); err != nil {
126+
if err := spec.TypeByName("ambiguous___flavour", true, &typ); err != nil {
127127
t.Fatal(err)
128128
}
129129

@@ -150,18 +150,18 @@ func TestTypeByName(t *testing.T) {
150150
} {
151151
t.Run(fmt.Sprintf("%T", typ), func(t *testing.T) {
152152
// spec.TypeByName MUST fail if typ is a nil btf.Type.
153-
if err := spec.TypeByName("iphdr", typ); err == nil {
153+
if err := spec.TypeByName("iphdr", true, typ); err == nil {
154154
t.Fatalf("TypeByName does not fail with type %T", typ)
155155
}
156156
})
157157
}
158158

159159
// spec.TypeByName MUST return the same address for multiple calls with the same type name.
160160
var iphdr1, iphdr2 *Struct
161-
if err := spec.TypeByName("iphdr", &iphdr1); err != nil {
161+
if err := spec.TypeByName("iphdr", true, &iphdr1); err != nil {
162162
t.Fatal(err)
163163
}
164-
if err := spec.TypeByName("iphdr", &iphdr2); err != nil {
164+
if err := spec.TypeByName("iphdr", true, &iphdr2); err != nil {
165165
t.Fatal(err)
166166
}
167167

@@ -171,12 +171,12 @@ func TestTypeByName(t *testing.T) {
171171

172172
// It's valid to pass a *Type to TypeByName.
173173
typ := Type(iphdr2)
174-
if err := spec.TypeByName("iphdr", &typ); err != nil {
174+
if err := spec.TypeByName("iphdr", true, &typ); err != nil {
175175
t.Fatal("Can't look up using *Type:", err)
176176
}
177177

178178
var nt Type
179-
qt.Assert(t, qt.IsNotNil(spec.TypeByName("a", &nt)))
179+
qt.Assert(t, qt.IsNotNil(spec.TypeByName("a", true, &nt)))
180180

181181
// Excerpt from linux/ip.h, https://elixir.bootlin.com/linux/latest/A/ident/iphdr
182182
//
@@ -278,17 +278,17 @@ func TestLoadSpecFromElf(t *testing.T) {
278278
}
279279

280280
var bpfMapDef *Struct
281-
if err := spec.TypeByName("bpf_map_def", &bpfMapDef); err != nil {
281+
if err := spec.TypeByName("bpf_map_def", true, &bpfMapDef); err != nil {
282282
t.Error("Can't find bpf_map_def:", err)
283283
}
284284

285285
var tmp *Void
286-
if err := spec.TypeByName("totally_bogus_type", &tmp); !errors.Is(err, ErrNotFound) {
286+
if err := spec.TypeByName("totally_bogus_type", true, &tmp); !errors.Is(err, ErrNotFound) {
287287
t.Error("TypeByName doesn't return ErrNotFound:", err)
288288
}
289289

290290
var fn *Func
291-
if err := spec.TypeByName("global_fn", &fn); err != nil {
291+
if err := spec.TypeByName("global_fn", true, &fn); err != nil {
292292
t.Error("Can't find global_fn():", err)
293293
} else {
294294
if fn.Linkage != GlobalFunc {
@@ -297,7 +297,7 @@ func TestLoadSpecFromElf(t *testing.T) {
297297
}
298298

299299
var v *Var
300-
if err := spec.TypeByName("key3", &v); err != nil {
300+
if err := spec.TypeByName("key3", true, &v); err != nil {
301301
t.Error("Can't find key3:", err)
302302
} else {
303303
if v.Linkage != GlobalVar {
@@ -383,7 +383,7 @@ func ExampleSpec_TypeByName() {
383383
// Declare a variable of the desired type
384384
var foo *Struct
385385

386-
if err := spec.TypeByName("foo", &foo); err != nil {
386+
if err := spec.TypeByName("foo", true, &foo); err != nil {
387387
// There is no struct with name foo, or there
388388
// are multiple possibilities.
389389
}
@@ -431,7 +431,7 @@ func TestLoadSplitSpec(t *testing.T) {
431431
}
432432

433433
var fnType *Func
434-
qt.Assert(t, qt.IsNil(splitSpec.TypeByName("bpf_testmod_init", &fnType)))
434+
qt.Assert(t, qt.IsNil(splitSpec.TypeByName("bpf_testmod_init", true, &fnType)))
435435
typeID, err := splitSpec.TypeID(fnType)
436436
qt.Assert(t, qt.IsNil(err))
437437

@@ -441,11 +441,11 @@ func TestLoadSplitSpec(t *testing.T) {
441441

442442
fnProto := fnType.Type.(*FuncProto)
443443
// 'int' is defined in the base BTF...
444-
intType, err := spec.AnyTypeByName("int")
444+
intType, err := spec.AnyTypeByName("int", true)
445445
qt.Assert(t, qt.IsNil(err))
446446

447447
// ... but not in the split BTF
448-
_, err = splitSpec.AnyTypeByName("int")
448+
_, err = splitSpec.AnyTypeByName("int", true)
449449
qt.Assert(t, qt.ErrorIs(err, ErrNotFound))
450450

451451
qt.Assert(t, qt.Equals(fnProto.Return, intType),
@@ -466,13 +466,13 @@ func TestLoadSplitSpec(t *testing.T) {
466466
splitSpecCopy := splitSpec.Copy()
467467

468468
var fnCopyType *Func
469-
qt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName("bpf_testmod_init", &fnCopyType)))
469+
qt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName("bpf_testmod_init", true, &fnCopyType)))
470470
qt.Assert(t, testutils.IsDeepCopy(fnCopyType, fnType))
471471

472472
// Pull out a second type which refers to "int" in the base, but which hasn't
473473
// been inflated yet. This forces inflating int from the base.
474474
var str *Struct
475-
qt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName("bpf_testmod_struct_arg_1", &str)))
475+
qt.Assert(t, qt.IsNil(splitSpecCopy.TypeByName("bpf_testmod_struct_arg_1", true, &str)))
476476

477477
// Ensure that the int types are indeed the same.
478478
qt.Assert(t, qt.Equals(str.Members[0].Type, fnCopyType.Type.(*FuncProto).Return))
@@ -525,7 +525,7 @@ func TestSpecConcurrentAccess(t *testing.T) {
525525
}
526526

527527
if n%2 == 0 {
528-
_, _ = spec.AnyTypeByName("gov_update_cpu_data")
528+
_, _ = spec.AnyTypeByName("gov_update_cpu_data", true)
529529
} else {
530530
_ = spec.Copy()
531531
}
@@ -619,7 +619,7 @@ func BenchmarkInspektorGadget(b *testing.B) {
619619

620620
var s *Struct
621621
for _, name := range types {
622-
if err := spec.TypeByName(name, &s); err != nil {
622+
if err := spec.TypeByName(name, true, &s); err != nil {
623623
b.Fatal(name, err)
624624
}
625625
}

btf/core_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ func BenchmarkCORESkBuff(b *testing.B) {
660660
spec := vmlinuxTestdataSpec(b)
661661

662662
var skb *Struct
663-
err := spec.TypeByName("sk_buff", &skb)
663+
err := spec.TypeByName("sk_buff", true, &skb)
664664
qt.Assert(b, qt.IsNil(err))
665665

666666
skbID, err := spec.TypeID(skb)
@@ -672,7 +672,7 @@ func BenchmarkCORESkBuff(b *testing.B) {
672672
qt.Assert(b, qt.Not(qt.Equals(lenIndex, -1)))
673673

674674
var pktHashTypes *Enum
675-
err = spec.TypeByName("pkt_hash_types", &pktHashTypes)
675+
err = spec.TypeByName("pkt_hash_types", true, &pktHashTypes)
676676
qt.Assert(b, qt.IsNil(err))
677677

678678
pktHashTypesID, err := spec.TypeID(pktHashTypes)

btf/dedup_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ func TestDedupSKBuff(t *testing.T) {
2323
qt.Assert(t, qt.IsNil(err))
2424

2525
var skBuffOne *Struct
26-
qt.Assert(t, qt.IsNil(spec.TypeByName("sk_buff", &skBuffOne)))
26+
qt.Assert(t, qt.IsNil(spec.TypeByName("sk_buff", true, &skBuffOne)))
2727

2828
skbCount := countTypes(skBuffOne)
2929

3030
spec = spec.Copy()
3131
var skBuffTwo *Struct
32-
qt.Assert(t, qt.IsNil(spec.TypeByName("sk_buff", &skBuffTwo)))
32+
qt.Assert(t, qt.IsNil(spec.TypeByName("sk_buff", true, &skBuffTwo)))
3333

3434
deduper := newDeduper()
3535

@@ -54,7 +54,7 @@ func BenchmarkDeduplicateSKBuff(b *testing.B) {
5454
types := make([]Type, 0, b.N)
5555
for range b.N {
5656
var skb *Struct
57-
if err := base.Copy().TypeByName("sk_buff", &skb); err != nil {
57+
if err := base.Copy().TypeByName("sk_buff", true, &skb); err != nil {
5858
b.Fatal(err)
5959
}
6060
types = append(types, skb)

btf/marshal_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ func TestBuilderSpec(t *testing.T) {
8888

8989
// With deduplication enabled, both ints should be merged into one,
9090
// allowing queries with AnyTypeByName.
91-
_, err = spec.AnyTypeByName("foo")
91+
_, err = spec.AnyTypeByName("foo", true)
9292
qt.Assert(t, qt.IsNil(err))
9393
}
9494

@@ -156,7 +156,7 @@ func TestMarshalEnum64(t *testing.T) {
156156
qt.Assert(t, qt.IsNil(err))
157157

158158
var have *Union
159-
err = spec.TypeByName("enum64", &have)
159+
err = spec.TypeByName("enum64", true, &have)
160160
qt.Assert(t, qt.IsNil(err))
161161

162162
placeholder := &Int{Name: "enum64_placeholder", Size: 8, Encoding: Signed}
@@ -192,9 +192,9 @@ func TestMarshalDeclTags(t *testing.T) {
192192
qt.Assert(t, qt.IsNil(err))
193193

194194
var td *Typedef
195-
qt.Assert(t, qt.IsNil(spec.TypeByName("decl tag typedef", &td)))
195+
qt.Assert(t, qt.IsNil(spec.TypeByName("decl tag typedef", true, &td)))
196196
var ti *Int
197-
qt.Assert(t, qt.IsNil(spec.TypeByName("decl_tag_placeholder", &ti)))
197+
qt.Assert(t, qt.IsNil(spec.TypeByName("decl_tag_placeholder", true, &ti)))
198198
}
199199

200200
func TestMarshalTypeTags(t *testing.T) {
@@ -223,7 +223,7 @@ func TestMarshalTypeTags(t *testing.T) {
223223
qt.Assert(t, qt.IsNil(err))
224224

225225
var td *Typedef
226-
qt.Assert(t, qt.IsNil(spec.TypeByName("type tag typedef", &td)))
226+
qt.Assert(t, qt.IsNil(spec.TypeByName("type tag typedef", true, &td)))
227227
qt.Assert(t, qt.Satisfies(td.Type, func(typ Type) bool {
228228
_, ok := typ.(*Const)
229229
return ok

0 commit comments

Comments
 (0)