%PDF- %PDF-
Direktori : /usr/local/go/src/runtime/ |
Current File : //usr/local/go/src/runtime/mpagecache_test.go |
// Copyright 2019 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime_test import ( "math/rand" . "runtime" "runtime/internal/sys" "testing" ) func checkPageCache(t *testing.T, got, want PageCache) { if got.Base() != want.Base() { t.Errorf("bad pageCache base: got 0x%x, want 0x%x", got.Base(), want.Base()) } if got.Cache() != want.Cache() { t.Errorf("bad pageCache bits: got %016x, want %016x", got.Base(), want.Base()) } if got.Scav() != want.Scav() { t.Errorf("bad pageCache scav: got %016x, want %016x", got.Scav(), want.Scav()) } } func TestPageCacheAlloc(t *testing.T) { base := PageBase(BaseChunkIdx, 0) type hit struct { npages uintptr base uintptr scav uintptr } tests := map[string]struct { cache PageCache hits []hit }{ "Empty": { cache: NewPageCache(base, 0, 0), hits: []hit{ {1, 0, 0}, {2, 0, 0}, {3, 0, 0}, {4, 0, 0}, {5, 0, 0}, {11, 0, 0}, {12, 0, 0}, {16, 0, 0}, {27, 0, 0}, {32, 0, 0}, {43, 0, 0}, {57, 0, 0}, {64, 0, 0}, {121, 0, 0}, }, }, "Lo1": { cache: NewPageCache(base, 0x1, 0x1), hits: []hit{ {1, base, PageSize}, {1, 0, 0}, {10, 0, 0}, }, }, "Hi1": { cache: NewPageCache(base, 0x1<<63, 0x1), hits: []hit{ {1, base + 63*PageSize, 0}, {1, 0, 0}, {10, 0, 0}, }, }, "Swiss1": { cache: NewPageCache(base, 0x20005555, 0x5505), hits: []hit{ {2, 0, 0}, {1, base, PageSize}, {1, base + 2*PageSize, PageSize}, {1, base + 4*PageSize, 0}, {1, base + 6*PageSize, 0}, {1, base + 8*PageSize, PageSize}, {1, base + 10*PageSize, PageSize}, {1, base + 12*PageSize, PageSize}, {1, base + 14*PageSize, PageSize}, {1, base + 29*PageSize, 0}, {1, 0, 0}, {10, 0, 0}, }, }, "Lo2": { cache: NewPageCache(base, 0x3, 0x2<<62), hits: []hit{ {2, base, 0}, {2, 0, 0}, {1, 0, 0}, }, }, "Hi2": { cache: NewPageCache(base, 0x3<<62, 0x3<<62), hits: []hit{ {2, base + 62*PageSize, 2 * PageSize}, {2, 0, 0}, {1, 0, 0}, }, }, "Swiss2": { cache: NewPageCache(base, 0x3333<<31, 0x3030<<31), hits: []hit{ {2, base + 31*PageSize, 0}, {2, base + 35*PageSize, 2 * PageSize}, {2, base + 39*PageSize, 0}, {2, base + 43*PageSize, 2 * PageSize}, {2, 0, 0}, }, }, "Hi53": { cache: NewPageCache(base, ((uint64(1)<<53)-1)<<10, ((uint64(1)<<16)-1)<<10), hits: []hit{ {53, base + 10*PageSize, 16 * PageSize}, {53, 0, 0}, {1, 0, 0}, }, }, "Full53": { cache: NewPageCache(base, ^uint64(0), ((uint64(1)<<16)-1)<<10), hits: []hit{ {53, base, 16 * PageSize}, {53, 0, 0}, {1, base + 53*PageSize, 0}, }, }, "Full64": { cache: NewPageCache(base, ^uint64(0), ^uint64(0)), hits: []hit{ {64, base, 64 * PageSize}, {64, 0, 0}, {1, 0, 0}, }, }, "FullMixed": { cache: NewPageCache(base, ^uint64(0), ^uint64(0)), hits: []hit{ {5, base, 5 * PageSize}, {7, base + 5*PageSize, 7 * PageSize}, {1, base + 12*PageSize, 1 * PageSize}, {23, base + 13*PageSize, 23 * PageSize}, {63, 0, 0}, {3, base + 36*PageSize, 3 * PageSize}, {3, base + 39*PageSize, 3 * PageSize}, {3, base + 42*PageSize, 3 * PageSize}, {12, base + 45*PageSize, 12 * PageSize}, {11, 0, 0}, {4, base + 57*PageSize, 4 * PageSize}, {4, 0, 0}, {6, 0, 0}, {36, 0, 0}, {2, base + 61*PageSize, 2 * PageSize}, {3, 0, 0}, {1, base + 63*PageSize, 1 * PageSize}, {4, 0, 0}, {2, 0, 0}, {62, 0, 0}, {1, 0, 0}, }, }, } for name, test := range tests { test := test t.Run(name, func(t *testing.T) { c := test.cache for i, h := range test.hits { b, s := c.Alloc(h.npages) if b != h.base { t.Fatalf("bad alloc base #%d: got 0x%x, want 0x%x", i, b, h.base) } if s != h.scav { t.Fatalf("bad alloc scav #%d: got %d, want %d", i, s, h.scav) } } }) } } func TestPageCacheFlush(t *testing.T) { if GOOS == "openbsd" && testing.Short() { t.Skip("skipping because virtual memory is limited; see #36210") } bits64ToBitRanges := func(bits uint64, base uint) []BitRange { var ranges []BitRange start, size := uint(0), uint(0) for i := 0; i < 64; i++ { if bits&(1<<i) != 0 { if size == 0 { start = uint(i) + base } size++ } else { if size != 0 { ranges = append(ranges, BitRange{start, size}) size = 0 } } } if size != 0 { ranges = append(ranges, BitRange{start, size}) } return ranges } runTest := func(t *testing.T, base uint, cache, scav uint64) { // Set up the before state. beforeAlloc := map[ChunkIdx][]BitRange{ BaseChunkIdx: {{base, 64}}, } beforeScav := map[ChunkIdx][]BitRange{ BaseChunkIdx: {}, } b := NewPageAlloc(beforeAlloc, beforeScav) defer FreePageAlloc(b) // Create and flush the cache. c := NewPageCache(PageBase(BaseChunkIdx, base), cache, scav) c.Flush(b) if !c.Empty() { t.Errorf("pageCache flush did not clear cache") } // Set up the expected after state. afterAlloc := map[ChunkIdx][]BitRange{ BaseChunkIdx: bits64ToBitRanges(^cache, base), } afterScav := map[ChunkIdx][]BitRange{ BaseChunkIdx: bits64ToBitRanges(scav, base), } want := NewPageAlloc(afterAlloc, afterScav) defer FreePageAlloc(want) // Check to see if it worked. checkPageAlloc(t, want, b) } // Empty. runTest(t, 0, 0, 0) // Full. runTest(t, 0, ^uint64(0), ^uint64(0)) // Random. for i := 0; i < 100; i++ { // Generate random valid base within a chunk. base := uint(rand.Intn(PallocChunkPages/64)) * 64 // Generate random cache. cache := rand.Uint64() scav := rand.Uint64() & cache // Run the test. runTest(t, base, cache, scav) } } func TestPageAllocAllocToCache(t *testing.T) { if GOOS == "openbsd" && testing.Short() { t.Skip("skipping because virtual memory is limited; see #36210") } type test struct { before map[ChunkIdx][]BitRange scav map[ChunkIdx][]BitRange hits []PageCache // expected base addresses and patterns after map[ChunkIdx][]BitRange } tests := map[string]test{ "AllFree": { before: map[ChunkIdx][]BitRange{ BaseChunkIdx: {}, }, scav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{1, 1}, {64, 64}}, }, hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx, 0), ^uint64(0), 0x2), NewPageCache(PageBase(BaseChunkIdx, 64), ^uint64(0), ^uint64(0)), NewPageCache(PageBase(BaseChunkIdx, 128), ^uint64(0), 0), NewPageCache(PageBase(BaseChunkIdx, 192), ^uint64(0), 0), }, after: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, 256}}, }, }, "ManyArena": { before: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 1: {{0, PallocChunkPages}}, BaseChunkIdx + 2: {{0, PallocChunkPages - 64}}, }, scav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 1: {{0, PallocChunkPages}}, BaseChunkIdx + 2: {}, }, hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx+2, PallocChunkPages-64), ^uint64(0), 0), }, after: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 1: {{0, PallocChunkPages}}, BaseChunkIdx + 2: {{0, PallocChunkPages}}, }, }, "NotContiguous": { before: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 0xff: {{0, 0}}, }, scav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 0xff: {{31, 67}}, }, hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx+0xff, 0), ^uint64(0), ((uint64(1)<<33)-1)<<31), }, after: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, BaseChunkIdx + 0xff: {{0, 64}}, }, }, "First": { before: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, 32}, {33, 31}, {96, 32}}, }, scav: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{1, 4}, {31, 5}, {66, 2}}, }, hits: []PageCache{ NewPageCache(PageBase(BaseChunkIdx, 0), 1<<32, 1<<32), NewPageCache(PageBase(BaseChunkIdx, 64), (uint64(1)<<32)-1, 0x3<<2), }, after: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, 128}}, }, }, "Fail": { before: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, }, hits: []PageCache{ NewPageCache(0, 0, 0), NewPageCache(0, 0, 0), NewPageCache(0, 0, 0), }, after: map[ChunkIdx][]BitRange{ BaseChunkIdx: {{0, PallocChunkPages}}, }, }, } // Disable these tests on iOS since we have a small address space. // See #46860. if PageAlloc64Bit != 0 && sys.GoosIos == 0 { const chunkIdxBigJump = 0x100000 // chunk index offset which translates to O(TiB) // This test is similar to the one with the same name for // pageAlloc.alloc and serves the same purpose. // See mpagealloc_test.go for details. sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes) baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1) tests["DiscontiguousMappedSumBoundary"] = test{ before: map[ChunkIdx][]BitRange{ baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages - 1}}, baseChunkIdx + chunkIdxBigJump: {{1, PallocChunkPages - 1}}, }, scav: map[ChunkIdx][]BitRange{ baseChunkIdx + sumsPerPhysPage - 1: {}, baseChunkIdx + chunkIdxBigJump: {}, }, hits: []PageCache{ NewPageCache(PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-64), 1<<63, 0), NewPageCache(PageBase(baseChunkIdx+chunkIdxBigJump, 0), 1, 0), NewPageCache(0, 0, 0), }, after: map[ChunkIdx][]BitRange{ baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}}, baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}}, }, } } for name, v := range tests { v := v t.Run(name, func(t *testing.T) { b := NewPageAlloc(v.before, v.scav) defer FreePageAlloc(b) for _, expect := range v.hits { checkPageCache(t, b.AllocToCache(), expect) if t.Failed() { return } } want := NewPageAlloc(v.after, v.scav) defer FreePageAlloc(want) checkPageAlloc(t, want, b) }) } }