Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/mpagealloc_test.go

Documentation: runtime

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"fmt"
     9  	. "runtime"
    10  	"testing"
    11  )
    12  
    13  func checkPageAlloc(t *testing.T, want, got *PageAlloc) {
    14  	// Ensure start and end are correct.
    15  	wantStart, wantEnd := want.Bounds()
    16  	gotStart, gotEnd := got.Bounds()
    17  	if gotStart != wantStart {
    18  		t.Fatalf("start values not equal: got %d, want %d", gotStart, wantStart)
    19  	}
    20  	if gotEnd != wantEnd {
    21  		t.Fatalf("end values not equal: got %d, want %d", gotEnd, wantEnd)
    22  	}
    23  
    24  	for i := gotStart; i < gotEnd; i++ {
    25  		// Check the bitmaps. Note that we may have nil data.
    26  		gb, wb := got.PallocData(i), want.PallocData(i)
    27  		if gb == nil && wb == nil {
    28  			continue
    29  		}
    30  		if (gb == nil && wb != nil) || (gb != nil && wb == nil) {
    31  			t.Errorf("chunk %d nilness mismatch", i)
    32  		}
    33  		if !checkPallocBits(t, gb.PallocBits(), wb.PallocBits()) {
    34  			t.Logf("in chunk %d (mallocBits)", i)
    35  		}
    36  		if !checkPallocBits(t, gb.Scavenged(), wb.Scavenged()) {
    37  			t.Logf("in chunk %d (scavenged)", i)
    38  		}
    39  	}
    40  	// TODO(mknyszek): Verify summaries too?
    41  }
    42  
    43  func TestPageAllocGrow(t *testing.T) {
    44  	if GOOS == "openbsd" && testing.Short() {
    45  		t.Skip("skipping because virtual memory is limited; see #36210")
    46  	}
    47  	type test struct {
    48  		chunks []ChunkIdx
    49  		inUse  []AddrRange
    50  	}
    51  	tests := map[string]test{
    52  		"One": {
    53  			chunks: []ChunkIdx{
    54  				BaseChunkIdx,
    55  			},
    56  			inUse: []AddrRange{
    57  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)),
    58  			},
    59  		},
    60  		"Contiguous2": {
    61  			chunks: []ChunkIdx{
    62  				BaseChunkIdx,
    63  				BaseChunkIdx + 1,
    64  			},
    65  			inUse: []AddrRange{
    66  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+2, 0)),
    67  			},
    68  		},
    69  		"Contiguous5": {
    70  			chunks: []ChunkIdx{
    71  				BaseChunkIdx,
    72  				BaseChunkIdx + 1,
    73  				BaseChunkIdx + 2,
    74  				BaseChunkIdx + 3,
    75  				BaseChunkIdx + 4,
    76  			},
    77  			inUse: []AddrRange{
    78  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+5, 0)),
    79  			},
    80  		},
    81  		"Discontiguous": {
    82  			chunks: []ChunkIdx{
    83  				BaseChunkIdx,
    84  				BaseChunkIdx + 2,
    85  				BaseChunkIdx + 4,
    86  			},
    87  			inUse: []AddrRange{
    88  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)),
    89  				MakeAddrRange(PageBase(BaseChunkIdx+2, 0), PageBase(BaseChunkIdx+3, 0)),
    90  				MakeAddrRange(PageBase(BaseChunkIdx+4, 0), PageBase(BaseChunkIdx+5, 0)),
    91  			},
    92  		},
    93  		"Mixed": {
    94  			chunks: []ChunkIdx{
    95  				BaseChunkIdx,
    96  				BaseChunkIdx + 1,
    97  				BaseChunkIdx + 2,
    98  				BaseChunkIdx + 4,
    99  			},
   100  			inUse: []AddrRange{
   101  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+3, 0)),
   102  				MakeAddrRange(PageBase(BaseChunkIdx+4, 0), PageBase(BaseChunkIdx+5, 0)),
   103  			},
   104  		},
   105  		"WildlyDiscontiguous": {
   106  			chunks: []ChunkIdx{
   107  				BaseChunkIdx,
   108  				BaseChunkIdx + 1,
   109  				BaseChunkIdx + 0x10,
   110  				BaseChunkIdx + 0x21,
   111  			},
   112  			inUse: []AddrRange{
   113  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+2, 0)),
   114  				MakeAddrRange(PageBase(BaseChunkIdx+0x10, 0), PageBase(BaseChunkIdx+0x11, 0)),
   115  				MakeAddrRange(PageBase(BaseChunkIdx+0x21, 0), PageBase(BaseChunkIdx+0x22, 0)),
   116  			},
   117  		},
   118  		"ManyDiscontiguous": {
   119  			// The initial cap is 16. Test 33 ranges, to exercise the growth path (twice).
   120  			chunks: []ChunkIdx{
   121  				BaseChunkIdx, BaseChunkIdx + 2, BaseChunkIdx + 4, BaseChunkIdx + 6,
   122  				BaseChunkIdx + 8, BaseChunkIdx + 10, BaseChunkIdx + 12, BaseChunkIdx + 14,
   123  				BaseChunkIdx + 16, BaseChunkIdx + 18, BaseChunkIdx + 20, BaseChunkIdx + 22,
   124  				BaseChunkIdx + 24, BaseChunkIdx + 26, BaseChunkIdx + 28, BaseChunkIdx + 30,
   125  				BaseChunkIdx + 32, BaseChunkIdx + 34, BaseChunkIdx + 36, BaseChunkIdx + 38,
   126  				BaseChunkIdx + 40, BaseChunkIdx + 42, BaseChunkIdx + 44, BaseChunkIdx + 46,
   127  				BaseChunkIdx + 48, BaseChunkIdx + 50, BaseChunkIdx + 52, BaseChunkIdx + 54,
   128  				BaseChunkIdx + 56, BaseChunkIdx + 58, BaseChunkIdx + 60, BaseChunkIdx + 62,
   129  				BaseChunkIdx + 64,
   130  			},
   131  			inUse: []AddrRange{
   132  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)),
   133  				MakeAddrRange(PageBase(BaseChunkIdx+2, 0), PageBase(BaseChunkIdx+3, 0)),
   134  				MakeAddrRange(PageBase(BaseChunkIdx+4, 0), PageBase(BaseChunkIdx+5, 0)),
   135  				MakeAddrRange(PageBase(BaseChunkIdx+6, 0), PageBase(BaseChunkIdx+7, 0)),
   136  				MakeAddrRange(PageBase(BaseChunkIdx+8, 0), PageBase(BaseChunkIdx+9, 0)),
   137  				MakeAddrRange(PageBase(BaseChunkIdx+10, 0), PageBase(BaseChunkIdx+11, 0)),
   138  				MakeAddrRange(PageBase(BaseChunkIdx+12, 0), PageBase(BaseChunkIdx+13, 0)),
   139  				MakeAddrRange(PageBase(BaseChunkIdx+14, 0), PageBase(BaseChunkIdx+15, 0)),
   140  				MakeAddrRange(PageBase(BaseChunkIdx+16, 0), PageBase(BaseChunkIdx+17, 0)),
   141  				MakeAddrRange(PageBase(BaseChunkIdx+18, 0), PageBase(BaseChunkIdx+19, 0)),
   142  				MakeAddrRange(PageBase(BaseChunkIdx+20, 0), PageBase(BaseChunkIdx+21, 0)),
   143  				MakeAddrRange(PageBase(BaseChunkIdx+22, 0), PageBase(BaseChunkIdx+23, 0)),
   144  				MakeAddrRange(PageBase(BaseChunkIdx+24, 0), PageBase(BaseChunkIdx+25, 0)),
   145  				MakeAddrRange(PageBase(BaseChunkIdx+26, 0), PageBase(BaseChunkIdx+27, 0)),
   146  				MakeAddrRange(PageBase(BaseChunkIdx+28, 0), PageBase(BaseChunkIdx+29, 0)),
   147  				MakeAddrRange(PageBase(BaseChunkIdx+30, 0), PageBase(BaseChunkIdx+31, 0)),
   148  				MakeAddrRange(PageBase(BaseChunkIdx+32, 0), PageBase(BaseChunkIdx+33, 0)),
   149  				MakeAddrRange(PageBase(BaseChunkIdx+34, 0), PageBase(BaseChunkIdx+35, 0)),
   150  				MakeAddrRange(PageBase(BaseChunkIdx+36, 0), PageBase(BaseChunkIdx+37, 0)),
   151  				MakeAddrRange(PageBase(BaseChunkIdx+38, 0), PageBase(BaseChunkIdx+39, 0)),
   152  				MakeAddrRange(PageBase(BaseChunkIdx+40, 0), PageBase(BaseChunkIdx+41, 0)),
   153  				MakeAddrRange(PageBase(BaseChunkIdx+42, 0), PageBase(BaseChunkIdx+43, 0)),
   154  				MakeAddrRange(PageBase(BaseChunkIdx+44, 0), PageBase(BaseChunkIdx+45, 0)),
   155  				MakeAddrRange(PageBase(BaseChunkIdx+46, 0), PageBase(BaseChunkIdx+47, 0)),
   156  				MakeAddrRange(PageBase(BaseChunkIdx+48, 0), PageBase(BaseChunkIdx+49, 0)),
   157  				MakeAddrRange(PageBase(BaseChunkIdx+50, 0), PageBase(BaseChunkIdx+51, 0)),
   158  				MakeAddrRange(PageBase(BaseChunkIdx+52, 0), PageBase(BaseChunkIdx+53, 0)),
   159  				MakeAddrRange(PageBase(BaseChunkIdx+54, 0), PageBase(BaseChunkIdx+55, 0)),
   160  				MakeAddrRange(PageBase(BaseChunkIdx+56, 0), PageBase(BaseChunkIdx+57, 0)),
   161  				MakeAddrRange(PageBase(BaseChunkIdx+58, 0), PageBase(BaseChunkIdx+59, 0)),
   162  				MakeAddrRange(PageBase(BaseChunkIdx+60, 0), PageBase(BaseChunkIdx+61, 0)),
   163  				MakeAddrRange(PageBase(BaseChunkIdx+62, 0), PageBase(BaseChunkIdx+63, 0)),
   164  				MakeAddrRange(PageBase(BaseChunkIdx+64, 0), PageBase(BaseChunkIdx+65, 0)),
   165  			},
   166  		},
   167  	}
   168  	if PageAlloc64Bit != 0 {
   169  		tests["ExtremelyDiscontiguous"] = test{
   170  			chunks: []ChunkIdx{
   171  				BaseChunkIdx,
   172  				BaseChunkIdx + 0x100000, // constant translates to O(TiB)
   173  			},
   174  			inUse: []AddrRange{
   175  				MakeAddrRange(PageBase(BaseChunkIdx, 0), PageBase(BaseChunkIdx+1, 0)),
   176  				MakeAddrRange(PageBase(BaseChunkIdx+0x100000, 0), PageBase(BaseChunkIdx+0x100001, 0)),
   177  			},
   178  		}
   179  	}
   180  	for name, v := range tests {
   181  		v := v
   182  		t.Run(name, func(t *testing.T) {
   183  			// By creating a new pageAlloc, we will
   184  			// grow it for each chunk defined in x.
   185  			x := make(map[ChunkIdx][]BitRange)
   186  			for _, c := range v.chunks {
   187  				x[c] = []BitRange{}
   188  			}
   189  			b := NewPageAlloc(x, nil)
   190  			defer FreePageAlloc(b)
   191  
   192  			got := b.InUse()
   193  			want := v.inUse
   194  
   195  			// Check for mismatches.
   196  			if len(got) != len(want) {
   197  				t.Fail()
   198  			} else {
   199  				for i := range want {
   200  					if !want[i].Equals(got[i]) {
   201  						t.Fail()
   202  						break
   203  					}
   204  				}
   205  			}
   206  			if t.Failed() {
   207  				t.Logf("found inUse mismatch")
   208  				t.Logf("got:")
   209  				for i, r := range got {
   210  					t.Logf("\t#%d [0x%x, 0x%x)", i, r.Base(), r.Limit())
   211  				}
   212  				t.Logf("want:")
   213  				for i, r := range want {
   214  					t.Logf("\t#%d [0x%x, 0x%x)", i, r.Base(), r.Limit())
   215  				}
   216  			}
   217  		})
   218  	}
   219  }
   220  
   221  func TestPageAllocAlloc(t *testing.T) {
   222  	if GOOS == "openbsd" && testing.Short() {
   223  		t.Skip("skipping because virtual memory is limited; see #36210")
   224  	}
   225  	type hit struct {
   226  		npages, base, scav uintptr
   227  	}
   228  	type test struct {
   229  		scav   map[ChunkIdx][]BitRange
   230  		before map[ChunkIdx][]BitRange
   231  		after  map[ChunkIdx][]BitRange
   232  		hits   []hit
   233  	}
   234  	tests := map[string]test{
   235  		"AllFree1": {
   236  			before: map[ChunkIdx][]BitRange{
   237  				BaseChunkIdx: {},
   238  			},
   239  			scav: map[ChunkIdx][]BitRange{
   240  				BaseChunkIdx: {{0, 1}, {2, 2}},
   241  			},
   242  			hits: []hit{
   243  				{1, PageBase(BaseChunkIdx, 0), PageSize},
   244  				{1, PageBase(BaseChunkIdx, 1), 0},
   245  				{1, PageBase(BaseChunkIdx, 2), PageSize},
   246  				{1, PageBase(BaseChunkIdx, 3), PageSize},
   247  				{1, PageBase(BaseChunkIdx, 4), 0},
   248  			},
   249  			after: map[ChunkIdx][]BitRange{
   250  				BaseChunkIdx: {{0, 5}},
   251  			},
   252  		},
   253  		"ManyArena1": {
   254  			before: map[ChunkIdx][]BitRange{
   255  				BaseChunkIdx:     {{0, PallocChunkPages}},
   256  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   257  				BaseChunkIdx + 2: {{0, PallocChunkPages - 1}},
   258  			},
   259  			scav: map[ChunkIdx][]BitRange{
   260  				BaseChunkIdx:     {{0, PallocChunkPages}},
   261  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   262  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   263  			},
   264  			hits: []hit{
   265  				{1, PageBase(BaseChunkIdx+2, PallocChunkPages-1), PageSize},
   266  			},
   267  			after: map[ChunkIdx][]BitRange{
   268  				BaseChunkIdx:     {{0, PallocChunkPages}},
   269  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   270  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   271  			},
   272  		},
   273  		"NotContiguous1": {
   274  			before: map[ChunkIdx][]BitRange{
   275  				BaseChunkIdx:        {{0, PallocChunkPages}},
   276  				BaseChunkIdx + 0xff: {{0, 0}},
   277  			},
   278  			scav: map[ChunkIdx][]BitRange{
   279  				BaseChunkIdx:        {{0, PallocChunkPages}},
   280  				BaseChunkIdx + 0xff: {{0, PallocChunkPages}},
   281  			},
   282  			hits: []hit{
   283  				{1, PageBase(BaseChunkIdx+0xff, 0), PageSize},
   284  			},
   285  			after: map[ChunkIdx][]BitRange{
   286  				BaseChunkIdx:        {{0, PallocChunkPages}},
   287  				BaseChunkIdx + 0xff: {{0, 1}},
   288  			},
   289  		},
   290  		"AllFree2": {
   291  			before: map[ChunkIdx][]BitRange{
   292  				BaseChunkIdx: {},
   293  			},
   294  			scav: map[ChunkIdx][]BitRange{
   295  				BaseChunkIdx: {{0, 3}, {7, 1}},
   296  			},
   297  			hits: []hit{
   298  				{2, PageBase(BaseChunkIdx, 0), 2 * PageSize},
   299  				{2, PageBase(BaseChunkIdx, 2), PageSize},
   300  				{2, PageBase(BaseChunkIdx, 4), 0},
   301  				{2, PageBase(BaseChunkIdx, 6), PageSize},
   302  				{2, PageBase(BaseChunkIdx, 8), 0},
   303  			},
   304  			after: map[ChunkIdx][]BitRange{
   305  				BaseChunkIdx: {{0, 10}},
   306  			},
   307  		},
   308  		"Straddle2": {
   309  			before: map[ChunkIdx][]BitRange{
   310  				BaseChunkIdx:     {{0, PallocChunkPages - 1}},
   311  				BaseChunkIdx + 1: {{1, PallocChunkPages - 1}},
   312  			},
   313  			scav: map[ChunkIdx][]BitRange{
   314  				BaseChunkIdx:     {{PallocChunkPages - 1, 1}},
   315  				BaseChunkIdx + 1: {},
   316  			},
   317  			hits: []hit{
   318  				{2, PageBase(BaseChunkIdx, PallocChunkPages-1), PageSize},
   319  			},
   320  			after: map[ChunkIdx][]BitRange{
   321  				BaseChunkIdx:     {{0, PallocChunkPages}},
   322  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   323  			},
   324  		},
   325  		"AllFree5": {
   326  			before: map[ChunkIdx][]BitRange{
   327  				BaseChunkIdx: {},
   328  			},
   329  			scav: map[ChunkIdx][]BitRange{
   330  				BaseChunkIdx: {{0, 8}, {9, 1}, {17, 5}},
   331  			},
   332  			hits: []hit{
   333  				{5, PageBase(BaseChunkIdx, 0), 5 * PageSize},
   334  				{5, PageBase(BaseChunkIdx, 5), 4 * PageSize},
   335  				{5, PageBase(BaseChunkIdx, 10), 0},
   336  				{5, PageBase(BaseChunkIdx, 15), 3 * PageSize},
   337  				{5, PageBase(BaseChunkIdx, 20), 2 * PageSize},
   338  			},
   339  			after: map[ChunkIdx][]BitRange{
   340  				BaseChunkIdx: {{0, 25}},
   341  			},
   342  		},
   343  		"AllFree64": {
   344  			before: map[ChunkIdx][]BitRange{
   345  				BaseChunkIdx: {},
   346  			},
   347  			scav: map[ChunkIdx][]BitRange{
   348  				BaseChunkIdx: {{21, 1}, {63, 65}},
   349  			},
   350  			hits: []hit{
   351  				{64, PageBase(BaseChunkIdx, 0), 2 * PageSize},
   352  				{64, PageBase(BaseChunkIdx, 64), 64 * PageSize},
   353  				{64, PageBase(BaseChunkIdx, 128), 0},
   354  			},
   355  			after: map[ChunkIdx][]BitRange{
   356  				BaseChunkIdx: {{0, 192}},
   357  			},
   358  		},
   359  		"AllFree65": {
   360  			before: map[ChunkIdx][]BitRange{
   361  				BaseChunkIdx: {},
   362  			},
   363  			scav: map[ChunkIdx][]BitRange{
   364  				BaseChunkIdx: {{129, 1}},
   365  			},
   366  			hits: []hit{
   367  				{65, PageBase(BaseChunkIdx, 0), 0},
   368  				{65, PageBase(BaseChunkIdx, 65), PageSize},
   369  				{65, PageBase(BaseChunkIdx, 130), 0},
   370  			},
   371  			after: map[ChunkIdx][]BitRange{
   372  				BaseChunkIdx: {{0, 195}},
   373  			},
   374  		},
   375  		"ExhaustPallocChunkPages-3": {
   376  			before: map[ChunkIdx][]BitRange{
   377  				BaseChunkIdx: {},
   378  			},
   379  			scav: map[ChunkIdx][]BitRange{
   380  				BaseChunkIdx: {{10, 1}},
   381  			},
   382  			hits: []hit{
   383  				{PallocChunkPages - 3, PageBase(BaseChunkIdx, 0), PageSize},
   384  				{PallocChunkPages - 3, 0, 0},
   385  				{1, PageBase(BaseChunkIdx, PallocChunkPages-3), 0},
   386  				{2, PageBase(BaseChunkIdx, PallocChunkPages-2), 0},
   387  				{1, 0, 0},
   388  				{PallocChunkPages - 3, 0, 0},
   389  			},
   390  			after: map[ChunkIdx][]BitRange{
   391  				BaseChunkIdx: {{0, PallocChunkPages}},
   392  			},
   393  		},
   394  		"AllFreePallocChunkPages": {
   395  			before: map[ChunkIdx][]BitRange{
   396  				BaseChunkIdx: {},
   397  			},
   398  			scav: map[ChunkIdx][]BitRange{
   399  				BaseChunkIdx: {{0, 1}, {PallocChunkPages - 1, 1}},
   400  			},
   401  			hits: []hit{
   402  				{PallocChunkPages, PageBase(BaseChunkIdx, 0), 2 * PageSize},
   403  				{PallocChunkPages, 0, 0},
   404  				{1, 0, 0},
   405  			},
   406  			after: map[ChunkIdx][]BitRange{
   407  				BaseChunkIdx: {{0, PallocChunkPages}},
   408  			},
   409  		},
   410  		"StraddlePallocChunkPages": {
   411  			before: map[ChunkIdx][]BitRange{
   412  				BaseChunkIdx:     {{0, PallocChunkPages / 2}},
   413  				BaseChunkIdx + 1: {{PallocChunkPages / 2, PallocChunkPages / 2}},
   414  			},
   415  			scav: map[ChunkIdx][]BitRange{
   416  				BaseChunkIdx:     {},
   417  				BaseChunkIdx + 1: {{3, 100}},
   418  			},
   419  			hits: []hit{
   420  				{PallocChunkPages, PageBase(BaseChunkIdx, PallocChunkPages/2), 100 * PageSize},
   421  				{PallocChunkPages, 0, 0},
   422  				{1, 0, 0},
   423  			},
   424  			after: map[ChunkIdx][]BitRange{
   425  				BaseChunkIdx:     {{0, PallocChunkPages}},
   426  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   427  			},
   428  		},
   429  		"StraddlePallocChunkPages+1": {
   430  			before: map[ChunkIdx][]BitRange{
   431  				BaseChunkIdx:     {{0, PallocChunkPages / 2}},
   432  				BaseChunkIdx + 1: {},
   433  			},
   434  			scav: map[ChunkIdx][]BitRange{
   435  				BaseChunkIdx:     {{0, PallocChunkPages}},
   436  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   437  			},
   438  			hits: []hit{
   439  				{PallocChunkPages + 1, PageBase(BaseChunkIdx, PallocChunkPages/2), (PallocChunkPages + 1) * PageSize},
   440  				{PallocChunkPages, 0, 0},
   441  				{1, PageBase(BaseChunkIdx+1, PallocChunkPages/2+1), PageSize},
   442  			},
   443  			after: map[ChunkIdx][]BitRange{
   444  				BaseChunkIdx:     {{0, PallocChunkPages}},
   445  				BaseChunkIdx + 1: {{0, PallocChunkPages/2 + 2}},
   446  			},
   447  		},
   448  		"AllFreePallocChunkPages*2": {
   449  			before: map[ChunkIdx][]BitRange{
   450  				BaseChunkIdx:     {},
   451  				BaseChunkIdx + 1: {},
   452  			},
   453  			scav: map[ChunkIdx][]BitRange{
   454  				BaseChunkIdx:     {},
   455  				BaseChunkIdx + 1: {},
   456  			},
   457  			hits: []hit{
   458  				{PallocChunkPages * 2, PageBase(BaseChunkIdx, 0), 0},
   459  				{PallocChunkPages * 2, 0, 0},
   460  				{1, 0, 0},
   461  			},
   462  			after: map[ChunkIdx][]BitRange{
   463  				BaseChunkIdx:     {{0, PallocChunkPages}},
   464  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   465  			},
   466  		},
   467  		"NotContiguousPallocChunkPages*2": {
   468  			before: map[ChunkIdx][]BitRange{
   469  				BaseChunkIdx:        {},
   470  				BaseChunkIdx + 0x40: {},
   471  				BaseChunkIdx + 0x41: {},
   472  			},
   473  			scav: map[ChunkIdx][]BitRange{
   474  				BaseChunkIdx:        {{0, PallocChunkPages}},
   475  				BaseChunkIdx + 0x40: {},
   476  				BaseChunkIdx + 0x41: {},
   477  			},
   478  			hits: []hit{
   479  				{PallocChunkPages * 2, PageBase(BaseChunkIdx+0x40, 0), 0},
   480  				{21, PageBase(BaseChunkIdx, 0), 21 * PageSize},
   481  				{1, PageBase(BaseChunkIdx, 21), PageSize},
   482  			},
   483  			after: map[ChunkIdx][]BitRange{
   484  				BaseChunkIdx:        {{0, 22}},
   485  				BaseChunkIdx + 0x40: {{0, PallocChunkPages}},
   486  				BaseChunkIdx + 0x41: {{0, PallocChunkPages}},
   487  			},
   488  		},
   489  		"StraddlePallocChunkPages*2": {
   490  			before: map[ChunkIdx][]BitRange{
   491  				BaseChunkIdx:     {{0, PallocChunkPages / 2}},
   492  				BaseChunkIdx + 1: {},
   493  				BaseChunkIdx + 2: {{PallocChunkPages / 2, PallocChunkPages / 2}},
   494  			},
   495  			scav: map[ChunkIdx][]BitRange{
   496  				BaseChunkIdx:     {{0, 7}},
   497  				BaseChunkIdx + 1: {{3, 5}, {121, 10}},
   498  				BaseChunkIdx + 2: {{PallocChunkPages/2 + 12, 2}},
   499  			},
   500  			hits: []hit{
   501  				{PallocChunkPages * 2, PageBase(BaseChunkIdx, PallocChunkPages/2), 15 * PageSize},
   502  				{PallocChunkPages * 2, 0, 0},
   503  				{1, 0, 0},
   504  			},
   505  			after: map[ChunkIdx][]BitRange{
   506  				BaseChunkIdx:     {{0, PallocChunkPages}},
   507  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   508  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   509  			},
   510  		},
   511  		"StraddlePallocChunkPages*5/4": {
   512  			before: map[ChunkIdx][]BitRange{
   513  				BaseChunkIdx:     {{0, PallocChunkPages}},
   514  				BaseChunkIdx + 1: {{0, PallocChunkPages * 3 / 4}},
   515  				BaseChunkIdx + 2: {{0, PallocChunkPages * 3 / 4}},
   516  				BaseChunkIdx + 3: {{0, 0}},
   517  			},
   518  			scav: map[ChunkIdx][]BitRange{
   519  				BaseChunkIdx:     {{0, PallocChunkPages}},
   520  				BaseChunkIdx + 1: {{PallocChunkPages / 2, PallocChunkPages/4 + 1}},
   521  				BaseChunkIdx + 2: {{PallocChunkPages / 3, 1}},
   522  				BaseChunkIdx + 3: {{PallocChunkPages * 2 / 3, 1}},
   523  			},
   524  			hits: []hit{
   525  				{PallocChunkPages * 5 / 4, PageBase(BaseChunkIdx+2, PallocChunkPages*3/4), PageSize},
   526  				{PallocChunkPages * 5 / 4, 0, 0},
   527  				{1, PageBase(BaseChunkIdx+1, PallocChunkPages*3/4), PageSize},
   528  			},
   529  			after: map[ChunkIdx][]BitRange{
   530  				BaseChunkIdx:     {{0, PallocChunkPages}},
   531  				BaseChunkIdx + 1: {{0, PallocChunkPages*3/4 + 1}},
   532  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   533  				BaseChunkIdx + 3: {{0, PallocChunkPages}},
   534  			},
   535  		},
   536  		"AllFreePallocChunkPages*7+5": {
   537  			before: map[ChunkIdx][]BitRange{
   538  				BaseChunkIdx:     {},
   539  				BaseChunkIdx + 1: {},
   540  				BaseChunkIdx + 2: {},
   541  				BaseChunkIdx + 3: {},
   542  				BaseChunkIdx + 4: {},
   543  				BaseChunkIdx + 5: {},
   544  				BaseChunkIdx + 6: {},
   545  				BaseChunkIdx + 7: {},
   546  			},
   547  			scav: map[ChunkIdx][]BitRange{
   548  				BaseChunkIdx:     {{50, 1}},
   549  				BaseChunkIdx + 1: {{31, 1}},
   550  				BaseChunkIdx + 2: {{7, 1}},
   551  				BaseChunkIdx + 3: {{200, 1}},
   552  				BaseChunkIdx + 4: {{3, 1}},
   553  				BaseChunkIdx + 5: {{51, 1}},
   554  				BaseChunkIdx + 6: {{20, 1}},
   555  				BaseChunkIdx + 7: {{1, 1}},
   556  			},
   557  			hits: []hit{
   558  				{PallocChunkPages*7 + 5, PageBase(BaseChunkIdx, 0), 8 * PageSize},
   559  				{PallocChunkPages*7 + 5, 0, 0},
   560  				{1, PageBase(BaseChunkIdx+7, 5), 0},
   561  			},
   562  			after: map[ChunkIdx][]BitRange{
   563  				BaseChunkIdx:     {{0, PallocChunkPages}},
   564  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   565  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   566  				BaseChunkIdx + 3: {{0, PallocChunkPages}},
   567  				BaseChunkIdx + 4: {{0, PallocChunkPages}},
   568  				BaseChunkIdx + 5: {{0, PallocChunkPages}},
   569  				BaseChunkIdx + 6: {{0, PallocChunkPages}},
   570  				BaseChunkIdx + 7: {{0, 6}},
   571  			},
   572  		},
   573  	}
   574  	if PageAlloc64Bit != 0 {
   575  		const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB)
   576  
   577  		// This test attempts to trigger a bug wherein we look at unmapped summary
   578  		// memory that isn't just in the case where we exhaust the heap.
   579  		//
   580  		// It achieves this by placing a chunk such that its summary will be
   581  		// at the very end of a physical page. It then also places another chunk
   582  		// much further up in the address space, such that any allocations into the
   583  		// first chunk do not exhaust the heap and the second chunk's summary is not in the
   584  		// page immediately adjacent to the first chunk's summary's page.
   585  		// Allocating into this first chunk to exhaustion and then into the second
   586  		// chunk may then trigger a check in the allocator which erroneously looks at
   587  		// unmapped summary memory and crashes.
   588  
   589  		// Figure out how many chunks are in a physical page, then align BaseChunkIdx
   590  		// to a physical page in the chunk summary array. Here we only assume that
   591  		// each summary array is aligned to some physical page.
   592  		sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes)
   593  		baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1)
   594  		tests["DiscontiguousMappedSumBoundary"] = test{
   595  			before: map[ChunkIdx][]BitRange{
   596  				baseChunkIdx + sumsPerPhysPage - 1: {},
   597  				baseChunkIdx + chunkIdxBigJump:     {},
   598  			},
   599  			scav: map[ChunkIdx][]BitRange{
   600  				baseChunkIdx + sumsPerPhysPage - 1: {},
   601  				baseChunkIdx + chunkIdxBigJump:     {},
   602  			},
   603  			hits: []hit{
   604  				{PallocChunkPages - 1, PageBase(baseChunkIdx+sumsPerPhysPage-1, 0), 0},
   605  				{1, PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-1), 0},
   606  				{1, PageBase(baseChunkIdx+chunkIdxBigJump, 0), 0},
   607  				{PallocChunkPages - 1, PageBase(baseChunkIdx+chunkIdxBigJump, 1), 0},
   608  				{1, 0, 0},
   609  			},
   610  			after: map[ChunkIdx][]BitRange{
   611  				baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}},
   612  				baseChunkIdx + chunkIdxBigJump:     {{0, PallocChunkPages}},
   613  			},
   614  		}
   615  
   616  		// Test to check for issue #40191. Essentially, the candidate searchAddr
   617  		// discovered by find may not point to mapped memory, so we need to handle
   618  		// that explicitly.
   619  		//
   620  		// chunkIdxSmallOffset is an offset intended to be used within chunkIdxBigJump.
   621  		// It is far enough within chunkIdxBigJump that the summaries at the beginning
   622  		// of an address range the size of chunkIdxBigJump will not be mapped in.
   623  		const chunkIdxSmallOffset = 0x503
   624  		tests["DiscontiguousBadSearchAddr"] = test{
   625  			before: map[ChunkIdx][]BitRange{
   626  				// The mechanism for the bug involves three chunks, A, B, and C, which are
   627  				// far apart in the address space. In particular, B is chunkIdxBigJump +
   628  				// chunkIdxSmalloffset chunks away from B, and C is 2*chunkIdxBigJump chunks
   629  				// away from A. A has 1 page free, B has several (NOT at the end of B), and
   630  				// C is totally free.
   631  				// Note that B's free memory must not be at the end of B because the fast
   632  				// path in the page allocator will check if the searchAddr even gives us
   633  				// enough space to place the allocation in a chunk before accessing the
   634  				// summary.
   635  				BaseChunkIdx + chunkIdxBigJump*0: {{0, PallocChunkPages - 1}},
   636  				BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {
   637  					{0, PallocChunkPages - 10},
   638  					{PallocChunkPages - 1, 1},
   639  				},
   640  				BaseChunkIdx + chunkIdxBigJump*2: {},
   641  			},
   642  			scav: map[ChunkIdx][]BitRange{
   643  				BaseChunkIdx + chunkIdxBigJump*0:                       {},
   644  				BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {},
   645  				BaseChunkIdx + chunkIdxBigJump*2:                       {},
   646  			},
   647  			hits: []hit{
   648  				// We first allocate into A to set the page allocator's searchAddr to the
   649  				// end of that chunk. That is the only purpose A serves.
   650  				{1, PageBase(BaseChunkIdx, PallocChunkPages-1), 0},
   651  				// Then, we make a big allocation that doesn't fit into B, and so must be
   652  				// fulfilled by C.
   653  				//
   654  				// On the way to fulfilling the allocation into C, we estimate searchAddr
   655  				// using the summary structure, but that will give us a searchAddr of
   656  				// B's base address minus chunkIdxSmallOffset chunks. These chunks will
   657  				// not be mapped.
   658  				{100, PageBase(baseChunkIdx+chunkIdxBigJump*2, 0), 0},
   659  				// Now we try to make a smaller allocation that can be fulfilled by B.
   660  				// In an older implementation of the page allocator, this will segfault,
   661  				// because this last allocation will first try to access the summary
   662  				// for B's base address minus chunkIdxSmallOffset chunks in the fast path,
   663  				// and this will not be mapped.
   664  				{9, PageBase(baseChunkIdx+chunkIdxBigJump*1+chunkIdxSmallOffset, PallocChunkPages-10), 0},
   665  			},
   666  			after: map[ChunkIdx][]BitRange{
   667  				BaseChunkIdx + chunkIdxBigJump*0:                       {{0, PallocChunkPages}},
   668  				BaseChunkIdx + chunkIdxBigJump*1 + chunkIdxSmallOffset: {{0, PallocChunkPages}},
   669  				BaseChunkIdx + chunkIdxBigJump*2:                       {{0, 100}},
   670  			},
   671  		}
   672  	}
   673  	for name, v := range tests {
   674  		v := v
   675  		t.Run(name, func(t *testing.T) {
   676  			b := NewPageAlloc(v.before, v.scav)
   677  			defer FreePageAlloc(b)
   678  
   679  			for iter, i := range v.hits {
   680  				a, s := b.Alloc(i.npages)
   681  				if a != i.base {
   682  					t.Fatalf("bad alloc #%d: want base 0x%x, got 0x%x", iter+1, i.base, a)
   683  				}
   684  				if s != i.scav {
   685  					t.Fatalf("bad alloc #%d: want scav %d, got %d", iter+1, i.scav, s)
   686  				}
   687  			}
   688  			want := NewPageAlloc(v.after, v.scav)
   689  			defer FreePageAlloc(want)
   690  
   691  			checkPageAlloc(t, want, b)
   692  		})
   693  	}
   694  }
   695  
   696  func TestPageAllocExhaust(t *testing.T) {
   697  	if GOOS == "openbsd" && testing.Short() {
   698  		t.Skip("skipping because virtual memory is limited; see #36210")
   699  	}
   700  	for _, npages := range []uintptr{1, 2, 3, 4, 5, 8, 16, 64, 1024, 1025, 2048, 2049} {
   701  		npages := npages
   702  		t.Run(fmt.Sprintf("%d", npages), func(t *testing.T) {
   703  			// Construct b.
   704  			bDesc := make(map[ChunkIdx][]BitRange)
   705  			for i := ChunkIdx(0); i < 4; i++ {
   706  				bDesc[BaseChunkIdx+i] = []BitRange{}
   707  			}
   708  			b := NewPageAlloc(bDesc, nil)
   709  			defer FreePageAlloc(b)
   710  
   711  			// Allocate into b with npages until we've exhausted the heap.
   712  			nAlloc := (PallocChunkPages * 4) / int(npages)
   713  			for i := 0; i < nAlloc; i++ {
   714  				addr := PageBase(BaseChunkIdx, uint(i)*uint(npages))
   715  				if a, _ := b.Alloc(npages); a != addr {
   716  					t.Fatalf("bad alloc #%d: want 0x%x, got 0x%x", i+1, addr, a)
   717  				}
   718  			}
   719  
   720  			// Check to make sure the next allocation fails.
   721  			if a, _ := b.Alloc(npages); a != 0 {
   722  				t.Fatalf("bad alloc #%d: want 0, got 0x%x", nAlloc, a)
   723  			}
   724  
   725  			// Construct what we want the heap to look like now.
   726  			allocPages := nAlloc * int(npages)
   727  			wantDesc := make(map[ChunkIdx][]BitRange)
   728  			for i := ChunkIdx(0); i < 4; i++ {
   729  				if allocPages >= PallocChunkPages {
   730  					wantDesc[BaseChunkIdx+i] = []BitRange{{0, PallocChunkPages}}
   731  					allocPages -= PallocChunkPages
   732  				} else if allocPages > 0 {
   733  					wantDesc[BaseChunkIdx+i] = []BitRange{{0, uint(allocPages)}}
   734  					allocPages = 0
   735  				} else {
   736  					wantDesc[BaseChunkIdx+i] = []BitRange{}
   737  				}
   738  			}
   739  			want := NewPageAlloc(wantDesc, nil)
   740  			defer FreePageAlloc(want)
   741  
   742  			// Check to make sure the heap b matches what we want.
   743  			checkPageAlloc(t, want, b)
   744  		})
   745  	}
   746  }
   747  
   748  func TestPageAllocFree(t *testing.T) {
   749  	if GOOS == "openbsd" && testing.Short() {
   750  		t.Skip("skipping because virtual memory is limited; see #36210")
   751  	}
   752  	tests := map[string]struct {
   753  		before map[ChunkIdx][]BitRange
   754  		after  map[ChunkIdx][]BitRange
   755  		npages uintptr
   756  		frees  []uintptr
   757  	}{
   758  		"Free1": {
   759  			npages: 1,
   760  			before: map[ChunkIdx][]BitRange{
   761  				BaseChunkIdx: {{0, PallocChunkPages}},
   762  			},
   763  			frees: []uintptr{
   764  				PageBase(BaseChunkIdx, 0),
   765  				PageBase(BaseChunkIdx, 1),
   766  				PageBase(BaseChunkIdx, 2),
   767  				PageBase(BaseChunkIdx, 3),
   768  				PageBase(BaseChunkIdx, 4),
   769  			},
   770  			after: map[ChunkIdx][]BitRange{
   771  				BaseChunkIdx: {{5, PallocChunkPages - 5}},
   772  			},
   773  		},
   774  		"ManyArena1": {
   775  			npages: 1,
   776  			before: map[ChunkIdx][]BitRange{
   777  				BaseChunkIdx:     {{0, PallocChunkPages}},
   778  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   779  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   780  			},
   781  			frees: []uintptr{
   782  				PageBase(BaseChunkIdx, PallocChunkPages/2),
   783  				PageBase(BaseChunkIdx+1, 0),
   784  				PageBase(BaseChunkIdx+2, PallocChunkPages-1),
   785  			},
   786  			after: map[ChunkIdx][]BitRange{
   787  				BaseChunkIdx:     {{0, PallocChunkPages / 2}, {PallocChunkPages/2 + 1, PallocChunkPages/2 - 1}},
   788  				BaseChunkIdx + 1: {{1, PallocChunkPages - 1}},
   789  				BaseChunkIdx + 2: {{0, PallocChunkPages - 1}},
   790  			},
   791  		},
   792  		"Free2": {
   793  			npages: 2,
   794  			before: map[ChunkIdx][]BitRange{
   795  				BaseChunkIdx: {{0, PallocChunkPages}},
   796  			},
   797  			frees: []uintptr{
   798  				PageBase(BaseChunkIdx, 0),
   799  				PageBase(BaseChunkIdx, 2),
   800  				PageBase(BaseChunkIdx, 4),
   801  				PageBase(BaseChunkIdx, 6),
   802  				PageBase(BaseChunkIdx, 8),
   803  			},
   804  			after: map[ChunkIdx][]BitRange{
   805  				BaseChunkIdx: {{10, PallocChunkPages - 10}},
   806  			},
   807  		},
   808  		"Straddle2": {
   809  			npages: 2,
   810  			before: map[ChunkIdx][]BitRange{
   811  				BaseChunkIdx:     {{PallocChunkPages - 1, 1}},
   812  				BaseChunkIdx + 1: {{0, 1}},
   813  			},
   814  			frees: []uintptr{
   815  				PageBase(BaseChunkIdx, PallocChunkPages-1),
   816  			},
   817  			after: map[ChunkIdx][]BitRange{
   818  				BaseChunkIdx:     {},
   819  				BaseChunkIdx + 1: {},
   820  			},
   821  		},
   822  		"Free5": {
   823  			npages: 5,
   824  			before: map[ChunkIdx][]BitRange{
   825  				BaseChunkIdx: {{0, PallocChunkPages}},
   826  			},
   827  			frees: []uintptr{
   828  				PageBase(BaseChunkIdx, 0),
   829  				PageBase(BaseChunkIdx, 5),
   830  				PageBase(BaseChunkIdx, 10),
   831  				PageBase(BaseChunkIdx, 15),
   832  				PageBase(BaseChunkIdx, 20),
   833  			},
   834  			after: map[ChunkIdx][]BitRange{
   835  				BaseChunkIdx: {{25, PallocChunkPages - 25}},
   836  			},
   837  		},
   838  		"Free64": {
   839  			npages: 64,
   840  			before: map[ChunkIdx][]BitRange{
   841  				BaseChunkIdx: {{0, PallocChunkPages}},
   842  			},
   843  			frees: []uintptr{
   844  				PageBase(BaseChunkIdx, 0),
   845  				PageBase(BaseChunkIdx, 64),
   846  				PageBase(BaseChunkIdx, 128),
   847  			},
   848  			after: map[ChunkIdx][]BitRange{
   849  				BaseChunkIdx: {{192, PallocChunkPages - 192}},
   850  			},
   851  		},
   852  		"Free65": {
   853  			npages: 65,
   854  			before: map[ChunkIdx][]BitRange{
   855  				BaseChunkIdx: {{0, PallocChunkPages}},
   856  			},
   857  			frees: []uintptr{
   858  				PageBase(BaseChunkIdx, 0),
   859  				PageBase(BaseChunkIdx, 65),
   860  				PageBase(BaseChunkIdx, 130),
   861  			},
   862  			after: map[ChunkIdx][]BitRange{
   863  				BaseChunkIdx: {{195, PallocChunkPages - 195}},
   864  			},
   865  		},
   866  		"FreePallocChunkPages": {
   867  			npages: PallocChunkPages,
   868  			before: map[ChunkIdx][]BitRange{
   869  				BaseChunkIdx: {{0, PallocChunkPages}},
   870  			},
   871  			frees: []uintptr{
   872  				PageBase(BaseChunkIdx, 0),
   873  			},
   874  			after: map[ChunkIdx][]BitRange{
   875  				BaseChunkIdx: {},
   876  			},
   877  		},
   878  		"StraddlePallocChunkPages": {
   879  			npages: PallocChunkPages,
   880  			before: map[ChunkIdx][]BitRange{
   881  				BaseChunkIdx:     {{PallocChunkPages / 2, PallocChunkPages / 2}},
   882  				BaseChunkIdx + 1: {{0, PallocChunkPages / 2}},
   883  			},
   884  			frees: []uintptr{
   885  				PageBase(BaseChunkIdx, PallocChunkPages/2),
   886  			},
   887  			after: map[ChunkIdx][]BitRange{
   888  				BaseChunkIdx:     {},
   889  				BaseChunkIdx + 1: {},
   890  			},
   891  		},
   892  		"StraddlePallocChunkPages+1": {
   893  			npages: PallocChunkPages + 1,
   894  			before: map[ChunkIdx][]BitRange{
   895  				BaseChunkIdx:     {{0, PallocChunkPages}},
   896  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   897  			},
   898  			frees: []uintptr{
   899  				PageBase(BaseChunkIdx, PallocChunkPages/2),
   900  			},
   901  			after: map[ChunkIdx][]BitRange{
   902  				BaseChunkIdx:     {{0, PallocChunkPages / 2}},
   903  				BaseChunkIdx + 1: {{PallocChunkPages/2 + 1, PallocChunkPages/2 - 1}},
   904  			},
   905  		},
   906  		"FreePallocChunkPages*2": {
   907  			npages: PallocChunkPages * 2,
   908  			before: map[ChunkIdx][]BitRange{
   909  				BaseChunkIdx:     {{0, PallocChunkPages}},
   910  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   911  			},
   912  			frees: []uintptr{
   913  				PageBase(BaseChunkIdx, 0),
   914  			},
   915  			after: map[ChunkIdx][]BitRange{
   916  				BaseChunkIdx:     {},
   917  				BaseChunkIdx + 1: {},
   918  			},
   919  		},
   920  		"StraddlePallocChunkPages*2": {
   921  			npages: PallocChunkPages * 2,
   922  			before: map[ChunkIdx][]BitRange{
   923  				BaseChunkIdx:     {{0, PallocChunkPages}},
   924  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   925  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   926  			},
   927  			frees: []uintptr{
   928  				PageBase(BaseChunkIdx, PallocChunkPages/2),
   929  			},
   930  			after: map[ChunkIdx][]BitRange{
   931  				BaseChunkIdx:     {{0, PallocChunkPages / 2}},
   932  				BaseChunkIdx + 1: {},
   933  				BaseChunkIdx + 2: {{PallocChunkPages / 2, PallocChunkPages / 2}},
   934  			},
   935  		},
   936  		"AllFreePallocChunkPages*7+5": {
   937  			npages: PallocChunkPages*7 + 5,
   938  			before: map[ChunkIdx][]BitRange{
   939  				BaseChunkIdx:     {{0, PallocChunkPages}},
   940  				BaseChunkIdx + 1: {{0, PallocChunkPages}},
   941  				BaseChunkIdx + 2: {{0, PallocChunkPages}},
   942  				BaseChunkIdx + 3: {{0, PallocChunkPages}},
   943  				BaseChunkIdx + 4: {{0, PallocChunkPages}},
   944  				BaseChunkIdx + 5: {{0, PallocChunkPages}},
   945  				BaseChunkIdx + 6: {{0, PallocChunkPages}},
   946  				BaseChunkIdx + 7: {{0, PallocChunkPages}},
   947  			},
   948  			frees: []uintptr{
   949  				PageBase(BaseChunkIdx, 0),
   950  			},
   951  			after: map[ChunkIdx][]BitRange{
   952  				BaseChunkIdx:     {},
   953  				BaseChunkIdx + 1: {},
   954  				BaseChunkIdx + 2: {},
   955  				BaseChunkIdx + 3: {},
   956  				BaseChunkIdx + 4: {},
   957  				BaseChunkIdx + 5: {},
   958  				BaseChunkIdx + 6: {},
   959  				BaseChunkIdx + 7: {{5, PallocChunkPages - 5}},
   960  			},
   961  		},
   962  	}
   963  	for name, v := range tests {
   964  		v := v
   965  		t.Run(name, func(t *testing.T) {
   966  			b := NewPageAlloc(v.before, nil)
   967  			defer FreePageAlloc(b)
   968  
   969  			for _, addr := range v.frees {
   970  				b.Free(addr, v.npages)
   971  			}
   972  			want := NewPageAlloc(v.after, nil)
   973  			defer FreePageAlloc(want)
   974  
   975  			checkPageAlloc(t, want, b)
   976  		})
   977  	}
   978  }
   979  
   980  func TestPageAllocAllocAndFree(t *testing.T) {
   981  	if GOOS == "openbsd" && testing.Short() {
   982  		t.Skip("skipping because virtual memory is limited; see #36210")
   983  	}
   984  	type hit struct {
   985  		alloc  bool
   986  		npages uintptr
   987  		base   uintptr
   988  	}
   989  	tests := map[string]struct {
   990  		init map[ChunkIdx][]BitRange
   991  		hits []hit
   992  	}{
   993  		// TODO(mknyszek): Write more tests here.
   994  		"Chunks8": {
   995  			init: map[ChunkIdx][]BitRange{
   996  				BaseChunkIdx:     {},
   997  				BaseChunkIdx + 1: {},
   998  				BaseChunkIdx + 2: {},
   999  				BaseChunkIdx + 3: {},
  1000  				BaseChunkIdx + 4: {},
  1001  				BaseChunkIdx + 5: {},
  1002  				BaseChunkIdx + 6: {},
  1003  				BaseChunkIdx + 7: {},
  1004  			},
  1005  			hits: []hit{
  1006  				{true, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1007  				{false, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1008  				{true, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1009  				{false, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1010  				{true, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1011  				{false, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1012  				{true, 1, PageBase(BaseChunkIdx, 0)},
  1013  				{false, 1, PageBase(BaseChunkIdx, 0)},
  1014  				{true, PallocChunkPages * 8, PageBase(BaseChunkIdx, 0)},
  1015  			},
  1016  		},
  1017  	}
  1018  	for name, v := range tests {
  1019  		v := v
  1020  		t.Run(name, func(t *testing.T) {
  1021  			b := NewPageAlloc(v.init, nil)
  1022  			defer FreePageAlloc(b)
  1023  
  1024  			for iter, i := range v.hits {
  1025  				if i.alloc {
  1026  					if a, _ := b.Alloc(i.npages); a != i.base {
  1027  						t.Fatalf("bad alloc #%d: want 0x%x, got 0x%x", iter+1, i.base, a)
  1028  					}
  1029  				} else {
  1030  					b.Free(i.base, i.npages)
  1031  				}
  1032  			}
  1033  		})
  1034  	}
  1035  }
  1036  

View as plain text