github.com/cilium/ebpf@v0.15.0/collection_test.go (about)

     1  package ebpf
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/go-quicktest/qt"
    12  
    13  	"github.com/cilium/ebpf/asm"
    14  	"github.com/cilium/ebpf/btf"
    15  	"github.com/cilium/ebpf/internal"
    16  	"github.com/cilium/ebpf/internal/testutils"
    17  	"github.com/cilium/ebpf/internal/testutils/fdtrace"
    18  )
    19  
    20  func TestMain(m *testing.M) {
    21  	fdtrace.TestMain(m)
    22  }
    23  
    24  func TestCollectionSpecNotModified(t *testing.T) {
    25  	cs := CollectionSpec{
    26  		Maps: map[string]*MapSpec{
    27  			"my-map": {
    28  				Type:       Array,
    29  				KeySize:    4,
    30  				ValueSize:  4,
    31  				MaxEntries: 1,
    32  			},
    33  		},
    34  		Programs: map[string]*ProgramSpec{
    35  			"test": {
    36  				Type: SocketFilter,
    37  				Instructions: asm.Instructions{
    38  					asm.LoadImm(asm.R1, 0, asm.DWord).WithReference("my-map"),
    39  					asm.LoadImm(asm.R0, 0, asm.DWord),
    40  					asm.Return(),
    41  				},
    42  				License: "MIT",
    43  			},
    44  		},
    45  	}
    46  
    47  	coll, err := NewCollection(&cs)
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	coll.Close()
    52  
    53  	if cs.Programs["test"].Instructions[0].Constant != 0 {
    54  		t.Error("Creating a collection modifies input spec")
    55  	}
    56  }
    57  
    58  func TestCollectionSpecCopy(t *testing.T) {
    59  	cs := &CollectionSpec{
    60  		Maps: map[string]*MapSpec{
    61  			"my-map": {
    62  				Type:       Array,
    63  				KeySize:    4,
    64  				ValueSize:  4,
    65  				MaxEntries: 1,
    66  			},
    67  		},
    68  		Programs: map[string]*ProgramSpec{
    69  			"test": {
    70  				Type: SocketFilter,
    71  				Instructions: asm.Instructions{
    72  					asm.LoadMapPtr(asm.R1, 0),
    73  					asm.LoadImm(asm.R0, 0, asm.DWord),
    74  					asm.Return(),
    75  				},
    76  				License: "MIT",
    77  			},
    78  		},
    79  		Types: &btf.Spec{},
    80  	}
    81  	cpy := cs.Copy()
    82  
    83  	if cpy == cs {
    84  		t.Error("Copy returned the same pointer")
    85  	}
    86  
    87  	if cpy.Maps["my-map"] == cs.Maps["my-map"] {
    88  		t.Error("Copy returned same Maps")
    89  	}
    90  
    91  	if cpy.Programs["test"] == cs.Programs["test"] {
    92  		t.Error("Copy returned same Programs")
    93  	}
    94  
    95  	if cpy.Types != cs.Types {
    96  		t.Error("Copy returned different Types")
    97  	}
    98  }
    99  
   100  func TestCollectionSpecLoadCopy(t *testing.T) {
   101  	file := testutils.NativeFile(t, "testdata/loader-%s.elf")
   102  	spec, err := LoadCollectionSpec(file)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	spec2 := spec.Copy()
   108  
   109  	var objs struct {
   110  		Prog *Program `ebpf:"xdp_prog"`
   111  	}
   112  
   113  	err = spec.LoadAndAssign(&objs, nil)
   114  	testutils.SkipIfNotSupported(t, err)
   115  	if err != nil {
   116  		t.Fatal("Loading original spec:", err)
   117  	}
   118  	defer objs.Prog.Close()
   119  
   120  	if err := spec2.LoadAndAssign(&objs, nil); err != nil {
   121  		t.Fatal("Loading copied spec:", err)
   122  	}
   123  	defer objs.Prog.Close()
   124  }
   125  
   126  func TestCollectionSpecRewriteMaps(t *testing.T) {
   127  	insns := asm.Instructions{
   128  		// R1 map
   129  		asm.LoadMapPtr(asm.R1, 0).WithReference("test-map"),
   130  		// R2 key
   131  		asm.Mov.Reg(asm.R2, asm.R10),
   132  		asm.Add.Imm(asm.R2, -4),
   133  		asm.StoreImm(asm.R2, 0, 0, asm.Word),
   134  		// Lookup map[0]
   135  		asm.FnMapLookupElem.Call(),
   136  		asm.JEq.Imm(asm.R0, 0, "ret"),
   137  		asm.LoadMem(asm.R0, asm.R0, 0, asm.Word),
   138  		asm.Return().WithSymbol("ret"),
   139  	}
   140  
   141  	cs := &CollectionSpec{
   142  		Maps: map[string]*MapSpec{
   143  			"test-map": {
   144  				Type:       Array,
   145  				KeySize:    4,
   146  				ValueSize:  4,
   147  				MaxEntries: 1,
   148  			},
   149  		},
   150  		Programs: map[string]*ProgramSpec{
   151  			"test-prog": {
   152  				Type:         SocketFilter,
   153  				Instructions: insns,
   154  				License:      "MIT",
   155  			},
   156  		},
   157  	}
   158  
   159  	// Override the map with another one
   160  	newMap, err := NewMap(cs.Maps["test-map"])
   161  	if err != nil {
   162  		t.Fatal(err)
   163  	}
   164  	defer newMap.Close()
   165  
   166  	err = newMap.Put(uint32(0), uint32(2))
   167  	if err != nil {
   168  		t.Fatal(err)
   169  	}
   170  
   171  	err = cs.RewriteMaps(map[string]*Map{
   172  		"test-map": newMap,
   173  	})
   174  	if err != nil {
   175  		t.Fatal(err)
   176  	}
   177  
   178  	if cs.Maps["test-map"] != nil {
   179  		t.Error("RewriteMaps doesn't remove map from CollectionSpec.Maps")
   180  	}
   181  
   182  	coll, err := NewCollection(cs)
   183  	if err != nil {
   184  		t.Fatal(err)
   185  	}
   186  	defer coll.Close()
   187  
   188  	ret, _, err := coll.Programs["test-prog"].Test(internal.EmptyBPFContext)
   189  	testutils.SkipIfNotSupported(t, err)
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  
   194  	if ret != 2 {
   195  		t.Fatal("new / override map not used")
   196  	}
   197  }
   198  
   199  func TestCollectionSpecMapReplacements(t *testing.T) {
   200  	insns := asm.Instructions{
   201  		// R1 map
   202  		asm.LoadMapPtr(asm.R1, 0).WithReference("test-map"),
   203  		// R2 key
   204  		asm.Mov.Reg(asm.R2, asm.R10),
   205  		asm.Add.Imm(asm.R2, -4),
   206  		asm.StoreImm(asm.R2, 0, 0, asm.Word),
   207  		// Lookup map[0]
   208  		asm.FnMapLookupElem.Call(),
   209  		asm.JEq.Imm(asm.R0, 0, "ret"),
   210  		asm.LoadMem(asm.R0, asm.R0, 0, asm.Word),
   211  		asm.Return().WithSymbol("ret"),
   212  	}
   213  
   214  	cs := &CollectionSpec{
   215  		Maps: map[string]*MapSpec{
   216  			"test-map": {
   217  				Type:       Array,
   218  				KeySize:    4,
   219  				ValueSize:  4,
   220  				MaxEntries: 1,
   221  			},
   222  		},
   223  		Programs: map[string]*ProgramSpec{
   224  			"test-prog": {
   225  				Type:         SocketFilter,
   226  				Instructions: insns,
   227  				License:      "MIT",
   228  			},
   229  		},
   230  	}
   231  
   232  	// Replace the map with another one
   233  	newMap, err := NewMap(cs.Maps["test-map"])
   234  	if err != nil {
   235  		t.Fatal(err)
   236  	}
   237  	defer newMap.Close()
   238  
   239  	err = newMap.Put(uint32(0), uint32(2))
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	coll, err := NewCollectionWithOptions(cs, CollectionOptions{
   245  		MapReplacements: map[string]*Map{
   246  			"test-map": newMap,
   247  		},
   248  	})
   249  	if err != nil {
   250  		t.Fatal(err)
   251  	}
   252  	defer coll.Close()
   253  
   254  	ret, _, err := coll.Programs["test-prog"].Test(internal.EmptyBPFContext)
   255  	testutils.SkipIfNotSupported(t, err)
   256  	if err != nil {
   257  		t.Fatal(err)
   258  	}
   259  
   260  	if ret != 2 {
   261  		t.Fatal("new / override map not used")
   262  	}
   263  
   264  	// Check that newMap isn't closed when the collection is closed
   265  	coll.Close()
   266  	err = newMap.Put(uint32(0), uint32(3))
   267  	if err != nil {
   268  		t.Fatalf("failed to update replaced map: %s", err)
   269  	}
   270  }
   271  func TestCollectionSpecMapReplacements_NonExistingMap(t *testing.T) {
   272  	cs := &CollectionSpec{
   273  		Maps: map[string]*MapSpec{
   274  			"test-map": {
   275  				Type:       Array,
   276  				KeySize:    4,
   277  				ValueSize:  4,
   278  				MaxEntries: 1,
   279  			},
   280  		},
   281  	}
   282  
   283  	// Override non-existing map
   284  	newMap, err := NewMap(cs.Maps["test-map"])
   285  	if err != nil {
   286  		t.Fatal(err)
   287  	}
   288  	defer newMap.Close()
   289  
   290  	coll, err := NewCollectionWithOptions(cs, CollectionOptions{
   291  		MapReplacements: map[string]*Map{
   292  			"non-existing-map": newMap,
   293  		},
   294  	})
   295  	if err == nil {
   296  		coll.Close()
   297  		t.Fatal("Overriding a non existing map did not fail")
   298  	}
   299  }
   300  
   301  func TestCollectionSpecMapReplacements_SpecMismatch(t *testing.T) {
   302  	cs := &CollectionSpec{
   303  		Maps: map[string]*MapSpec{
   304  			"test-map": {
   305  				Type:       Array,
   306  				KeySize:    4,
   307  				ValueSize:  4,
   308  				MaxEntries: 1,
   309  			},
   310  		},
   311  	}
   312  
   313  	// Override map with mismatching spec
   314  	newMap, err := NewMap(&MapSpec{
   315  		Type:       Array,
   316  		KeySize:    4,
   317  		ValueSize:  8, // this is different
   318  		MaxEntries: 1,
   319  	})
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	// Map fd is duplicated by MapReplacements, this one can be safely closed.
   324  	defer newMap.Close()
   325  
   326  	coll, err := NewCollectionWithOptions(cs, CollectionOptions{
   327  		MapReplacements: map[string]*Map{
   328  			"test-map": newMap,
   329  		},
   330  	})
   331  	if err == nil {
   332  		coll.Close()
   333  		t.Fatal("Overriding a map with a mismatching spec did not fail")
   334  	}
   335  	if !errors.Is(err, ErrMapIncompatible) {
   336  		t.Fatalf("Overriding a map with a mismatching spec failed with the wrong error")
   337  	}
   338  }
   339  
   340  func TestCollectionRewriteConstants(t *testing.T) {
   341  	cs := &CollectionSpec{
   342  		Maps: map[string]*MapSpec{
   343  			".rodata": {
   344  				Type:       Array,
   345  				KeySize:    4,
   346  				ValueSize:  4,
   347  				MaxEntries: 1,
   348  				Value: &btf.Datasec{
   349  					Vars: []btf.VarSecinfo{
   350  						{
   351  							Type: &btf.Var{
   352  								Name: "the_constant",
   353  								Type: &btf.Int{Size: 4},
   354  							},
   355  							Offset: 0,
   356  							Size:   4,
   357  						},
   358  					},
   359  				},
   360  				Contents: []MapKV{
   361  					{Key: uint32(0), Value: []byte{1, 1, 1, 1}},
   362  				},
   363  			},
   364  		},
   365  	}
   366  
   367  	err := cs.RewriteConstants(map[string]interface{}{
   368  		"fake_constant_one": uint32(1),
   369  		"fake_constant_two": uint32(2),
   370  	})
   371  	qt.Assert(t, qt.IsNotNil(err), qt.Commentf("RewriteConstants did not fail"))
   372  
   373  	var mErr *MissingConstantsError
   374  	if !errors.As(err, &mErr) {
   375  		t.Fatal("Error doesn't wrap MissingConstantsError:", err)
   376  	}
   377  	qt.Assert(t, qt.ContentEquals(mErr.Constants, []string{"fake_constant_one", "fake_constant_two"}))
   378  
   379  	err = cs.RewriteConstants(map[string]interface{}{
   380  		"the_constant": uint32(0x42424242),
   381  	})
   382  	qt.Assert(t, qt.IsNil(err))
   383  	qt.Assert(t, qt.ContentEquals(cs.Maps[".rodata"].Contents[0].Value.([]byte), []byte{0x42, 0x42, 0x42, 0x42}))
   384  }
   385  
   386  func TestCollectionSpec_LoadAndAssign_LazyLoading(t *testing.T) {
   387  	spec := &CollectionSpec{
   388  		Maps: map[string]*MapSpec{
   389  			"valid": {
   390  				Type:       Array,
   391  				KeySize:    4,
   392  				ValueSize:  4,
   393  				MaxEntries: 1,
   394  			},
   395  			"bogus": {
   396  				Type:       Array,
   397  				MaxEntries: 0,
   398  			},
   399  		},
   400  		Programs: map[string]*ProgramSpec{
   401  			"valid": {
   402  				Type: SocketFilter,
   403  				Instructions: asm.Instructions{
   404  					asm.LoadImm(asm.R0, 0, asm.DWord),
   405  					asm.Return(),
   406  				},
   407  				License: "MIT",
   408  			},
   409  			"bogus": {
   410  				Type: SocketFilter,
   411  				Instructions: asm.Instructions{
   412  					// Undefined return value is rejected
   413  					asm.Return(),
   414  				},
   415  				License: "MIT",
   416  			},
   417  		},
   418  	}
   419  
   420  	var objs struct {
   421  		Prog *Program `ebpf:"valid"`
   422  		Map  *Map     `ebpf:"valid"`
   423  	}
   424  
   425  	if err := spec.LoadAndAssign(&objs, nil); err != nil {
   426  		t.Fatal("Assign loads a map or program that isn't requested in the struct:", err)
   427  	}
   428  	defer objs.Prog.Close()
   429  	defer objs.Map.Close()
   430  
   431  	if objs.Prog == nil {
   432  		t.Error("Program is nil")
   433  	}
   434  
   435  	if objs.Map == nil {
   436  		t.Error("Map is nil")
   437  	}
   438  }
   439  
   440  func TestCollectionSpecAssign(t *testing.T) {
   441  	var specs struct {
   442  		Program *ProgramSpec `ebpf:"prog1"`
   443  		Map     *MapSpec     `ebpf:"map1"`
   444  	}
   445  
   446  	mapSpec := &MapSpec{
   447  		Type:       Array,
   448  		KeySize:    4,
   449  		ValueSize:  4,
   450  		MaxEntries: 1,
   451  	}
   452  	progSpec := &ProgramSpec{
   453  		Type: SocketFilter,
   454  		Instructions: asm.Instructions{
   455  			asm.LoadImm(asm.R0, 0, asm.DWord),
   456  			asm.Return(),
   457  		},
   458  		License: "MIT",
   459  	}
   460  
   461  	cs := &CollectionSpec{
   462  		Maps: map[string]*MapSpec{
   463  			"map1": mapSpec,
   464  		},
   465  		Programs: map[string]*ProgramSpec{
   466  			"prog1": progSpec,
   467  		},
   468  	}
   469  
   470  	if err := cs.Assign(&specs); err != nil {
   471  		t.Fatal("Can't assign spec:", err)
   472  	}
   473  
   474  	if specs.Program != progSpec {
   475  		t.Fatalf("Expected Program to be %p, got %p", progSpec, specs.Program)
   476  	}
   477  
   478  	if specs.Map != mapSpec {
   479  		t.Fatalf("Expected Map to be %p, got %p", mapSpec, specs.Map)
   480  	}
   481  
   482  	if err := cs.Assign(new(int)); err == nil {
   483  		t.Fatal("Assign allows to besides *struct")
   484  	}
   485  
   486  	if err := cs.Assign(new(struct{ Foo int })); err != nil {
   487  		t.Fatal("Assign doesn't ignore untagged fields")
   488  	}
   489  
   490  	unexported := new(struct {
   491  		foo *MapSpec `ebpf:"map1"`
   492  	})
   493  
   494  	if err := cs.Assign(unexported); err == nil {
   495  		t.Error("Assign should return an error on unexported fields")
   496  	}
   497  }
   498  
   499  func TestAssignValues(t *testing.T) {
   500  	zero := func(t reflect.Type, name string) (interface{}, error) {
   501  		return reflect.Zero(t).Interface(), nil
   502  	}
   503  
   504  	type t1 struct {
   505  		Bar int `ebpf:"bar"`
   506  	}
   507  
   508  	type t2 struct {
   509  		t1
   510  		Foo int `ebpf:"foo"`
   511  	}
   512  
   513  	type t2ptr struct {
   514  		*t1
   515  		Foo int `ebpf:"foo"`
   516  	}
   517  
   518  	invalid := []struct {
   519  		name string
   520  		to   interface{}
   521  	}{
   522  		{"non-struct", 1},
   523  		{"non-pointer struct", t1{}},
   524  		{"pointer to non-struct", new(int)},
   525  		{"embedded nil pointer", &t2ptr{}},
   526  		{"unexported field", new(struct {
   527  			foo int `ebpf:"foo"`
   528  		})},
   529  		{"identical tag", new(struct {
   530  			Foo1 int `ebpf:"foo"`
   531  			Foo2 int `ebpf:"foo"`
   532  		})},
   533  	}
   534  
   535  	for _, testcase := range invalid {
   536  		t.Run(testcase.name, func(t *testing.T) {
   537  			if err := assignValues(testcase.to, zero); err == nil {
   538  				t.Fatal("assignValues didn't return an error")
   539  			} else {
   540  				t.Log(err)
   541  			}
   542  		})
   543  	}
   544  
   545  	valid := []struct {
   546  		name string
   547  		to   interface{}
   548  	}{
   549  		{"pointer to struct", new(t1)},
   550  		{"embedded struct", new(t2)},
   551  		{"embedded struct pointer", &t2ptr{t1: new(t1)}},
   552  		{"untagged field", new(struct{ Foo int })},
   553  	}
   554  
   555  	for _, testcase := range valid {
   556  		t.Run(testcase.name, func(t *testing.T) {
   557  			if err := assignValues(testcase.to, zero); err != nil {
   558  				t.Fatal("assignValues returned", err)
   559  			}
   560  		})
   561  	}
   562  
   563  }
   564  
   565  func TestCollectionAssign(t *testing.T) {
   566  	var objs struct {
   567  		Program *Program `ebpf:"prog1"`
   568  		Map     *Map     `ebpf:"map1"`
   569  	}
   570  
   571  	cs := &CollectionSpec{
   572  		Maps: map[string]*MapSpec{
   573  			"map1": {
   574  				Type:       Array,
   575  				KeySize:    4,
   576  				ValueSize:  4,
   577  				MaxEntries: 1,
   578  			},
   579  		},
   580  		Programs: map[string]*ProgramSpec{
   581  			"prog1": {
   582  				Type: SocketFilter,
   583  				Instructions: asm.Instructions{
   584  					asm.LoadImm(asm.R0, 0, asm.DWord),
   585  					asm.Return(),
   586  				},
   587  				License: "MIT",
   588  			},
   589  		},
   590  	}
   591  
   592  	coll, err := NewCollection(cs)
   593  	qt.Assert(t, qt.IsNil(err))
   594  	defer coll.Close()
   595  
   596  	qt.Assert(t, qt.IsNil(coll.Assign(&objs)))
   597  	defer objs.Program.Close()
   598  	defer objs.Map.Close()
   599  
   600  	// Check that objs has received ownership of map and prog
   601  	qt.Assert(t, qt.IsTrue(objs.Program.FD() >= 0))
   602  	qt.Assert(t, qt.IsTrue(objs.Map.FD() >= 0))
   603  
   604  	// Check that the collection has lost ownership
   605  	qt.Assert(t, qt.IsNil(coll.Programs["prog1"]))
   606  	qt.Assert(t, qt.IsNil(coll.Maps["map1"]))
   607  }
   608  
   609  func TestCollectionAssignFail(t *testing.T) {
   610  	// `map2` does not exist
   611  	var objs struct {
   612  		Program *Program `ebpf:"prog1"`
   613  		Map     *Map     `ebpf:"map2"`
   614  	}
   615  
   616  	cs := &CollectionSpec{
   617  		Maps: map[string]*MapSpec{
   618  			"map1": {
   619  				Type:       Array,
   620  				KeySize:    4,
   621  				ValueSize:  4,
   622  				MaxEntries: 1,
   623  			},
   624  		},
   625  		Programs: map[string]*ProgramSpec{
   626  			"prog1": {
   627  				Type: SocketFilter,
   628  				Instructions: asm.Instructions{
   629  					asm.LoadImm(asm.R0, 0, asm.DWord),
   630  					asm.Return(),
   631  				},
   632  				License: "MIT",
   633  			},
   634  		},
   635  	}
   636  
   637  	coll, err := NewCollection(cs)
   638  	qt.Assert(t, qt.IsNil(err))
   639  	defer coll.Close()
   640  
   641  	qt.Assert(t, qt.IsNotNil(coll.Assign(&objs)))
   642  
   643  	// Check that the collection has retained ownership
   644  	qt.Assert(t, qt.IsNotNil(coll.Programs["prog1"]))
   645  	qt.Assert(t, qt.IsNotNil(coll.Maps["map1"]))
   646  }
   647  
   648  func TestIncompleteLoadAndAssign(t *testing.T) {
   649  	spec := &CollectionSpec{
   650  		Programs: map[string]*ProgramSpec{
   651  			"valid": {
   652  				Type: SocketFilter,
   653  				Instructions: asm.Instructions{
   654  					asm.LoadImm(asm.R0, 0, asm.DWord),
   655  					asm.Return(),
   656  				},
   657  				License: "MIT",
   658  			},
   659  			"invalid": {
   660  				Type: SocketFilter,
   661  				Instructions: asm.Instructions{
   662  					asm.Return(),
   663  				},
   664  				License: "MIT",
   665  			},
   666  		},
   667  	}
   668  
   669  	s := struct {
   670  		// Assignment to Valid should execute and succeed.
   671  		Valid *Program `ebpf:"valid"`
   672  		// Assignment to Invalid should fail and cause Valid's fd to be closed.
   673  		Invalid *Program `ebpf:"invalid"`
   674  	}{}
   675  
   676  	if err := spec.LoadAndAssign(&s, nil); err == nil {
   677  		t.Fatal("expected error loading invalid ProgramSpec")
   678  	}
   679  
   680  	if s.Valid == nil {
   681  		t.Fatal("expected valid prog to be non-nil")
   682  	}
   683  
   684  	if fd := s.Valid.FD(); fd != -1 {
   685  		t.Fatal("expected valid prog to have closed fd -1, got:", fd)
   686  	}
   687  
   688  	if s.Invalid != nil {
   689  		t.Fatal("expected invalid prog to be nil due to never being assigned")
   690  	}
   691  }
   692  
   693  func BenchmarkNewCollection(b *testing.B) {
   694  	file := testutils.NativeFile(b, "testdata/loader-%s.elf")
   695  	spec, err := LoadCollectionSpec(file)
   696  	if err != nil {
   697  		b.Fatal(err)
   698  	}
   699  
   700  	spec.Maps["array_of_hash_map"].InnerMap = spec.Maps["hash_map"]
   701  	for _, m := range spec.Maps {
   702  		m.Pinning = PinNone
   703  	}
   704  
   705  	b.ReportAllocs()
   706  	b.ResetTimer()
   707  
   708  	for i := 0; i < b.N; i++ {
   709  		coll, err := NewCollection(spec)
   710  		if err != nil {
   711  			b.Fatal(err)
   712  		}
   713  		coll.Close()
   714  	}
   715  }
   716  
   717  func BenchmarkNewCollectionManyProgs(b *testing.B) {
   718  	file := testutils.NativeFile(b, "testdata/manyprogs-%s.elf")
   719  	spec, err := LoadCollectionSpec(file)
   720  	if err != nil {
   721  		b.Fatal(err)
   722  	}
   723  
   724  	b.ReportAllocs()
   725  	b.ResetTimer()
   726  
   727  	for i := 0; i < b.N; i++ {
   728  		coll, err := NewCollection(spec)
   729  		if err != nil {
   730  			b.Fatal(err)
   731  		}
   732  		coll.Close()
   733  	}
   734  }
   735  
   736  func BenchmarkLoadCollectionManyProgs(b *testing.B) {
   737  	file, err := os.Open(testutils.NativeFile(b, "testdata/manyprogs-%s.elf"))
   738  	qt.Assert(b, qt.IsNil(err))
   739  	defer file.Close()
   740  
   741  	b.ReportAllocs()
   742  	b.ResetTimer()
   743  
   744  	for i := 0; i < b.N; i++ {
   745  		_, err := file.Seek(0, io.SeekStart)
   746  		if err != nil {
   747  			b.Fatal(err)
   748  		}
   749  
   750  		_, err = LoadCollectionSpecFromReader(file)
   751  		if err != nil {
   752  			b.Fatal(err)
   753  		}
   754  	}
   755  }
   756  
   757  func ExampleCollectionSpec_Assign() {
   758  	spec := &CollectionSpec{
   759  		Maps: map[string]*MapSpec{
   760  			"map1": {
   761  				Type:       Array,
   762  				KeySize:    4,
   763  				ValueSize:  4,
   764  				MaxEntries: 1,
   765  			},
   766  		},
   767  		Programs: map[string]*ProgramSpec{
   768  			"prog1": {
   769  				Type: SocketFilter,
   770  				Instructions: asm.Instructions{
   771  					asm.LoadImm(asm.R0, 0, asm.DWord),
   772  					asm.Return(),
   773  				},
   774  				License: "MIT",
   775  			},
   776  		},
   777  	}
   778  
   779  	type maps struct {
   780  		Map *MapSpec `ebpf:"map1"`
   781  	}
   782  
   783  	var specs struct {
   784  		maps
   785  		Program *ProgramSpec `ebpf:"prog1"`
   786  	}
   787  
   788  	if err := spec.Assign(&specs); err != nil {
   789  		panic(err)
   790  	}
   791  
   792  	fmt.Println(specs.Program.Type)
   793  	fmt.Println(specs.Map.Type)
   794  
   795  	// Output: SocketFilter
   796  	// Array
   797  }
   798  
   799  func ExampleCollectionSpec_LoadAndAssign() {
   800  	spec := &CollectionSpec{
   801  		Maps: map[string]*MapSpec{
   802  			"map1": {
   803  				Type:       Array,
   804  				KeySize:    4,
   805  				ValueSize:  4,
   806  				MaxEntries: 1,
   807  			},
   808  		},
   809  		Programs: map[string]*ProgramSpec{
   810  			"prog1": {
   811  				Type: SocketFilter,
   812  				Instructions: asm.Instructions{
   813  					asm.LoadImm(asm.R0, 0, asm.DWord),
   814  					asm.Return(),
   815  				},
   816  				License: "MIT",
   817  			},
   818  		},
   819  	}
   820  
   821  	var objs struct {
   822  		Program *Program `ebpf:"prog1"`
   823  		Map     *Map     `ebpf:"map1"`
   824  	}
   825  
   826  	if err := spec.LoadAndAssign(&objs, nil); err != nil {
   827  		panic(err)
   828  	}
   829  	defer objs.Program.Close()
   830  	defer objs.Map.Close()
   831  
   832  	fmt.Println(objs.Program.Type())
   833  	fmt.Println(objs.Map.Type())
   834  
   835  	// Output: SocketFilter
   836  	// Array
   837  }