Source file
src/runtime/mpagecache_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "internal/goos"
9 "math/rand"
10 . "runtime"
11 "testing"
12 )
13
14 func checkPageCache(t *testing.T, got, want PageCache) {
15 if got.Base() != want.Base() {
16 t.Errorf("bad pageCache base: got 0x%x, want 0x%x", got.Base(), want.Base())
17 }
18 if got.Cache() != want.Cache() {
19 t.Errorf("bad pageCache bits: got %016x, want %016x", got.Base(), want.Base())
20 }
21 if got.Scav() != want.Scav() {
22 t.Errorf("bad pageCache scav: got %016x, want %016x", got.Scav(), want.Scav())
23 }
24 }
25
26 func TestPageCacheAlloc(t *testing.T) {
27 base := PageBase(BaseChunkIdx, 0)
28 type hit struct {
29 npages uintptr
30 base uintptr
31 scav uintptr
32 }
33 tests := map[string]struct {
34 cache PageCache
35 hits []hit
36 }{
37 "Empty": {
38 cache: NewPageCache(base, 0, 0),
39 hits: []hit{
40 {1, 0, 0},
41 {2, 0, 0},
42 {3, 0, 0},
43 {4, 0, 0},
44 {5, 0, 0},
45 {11, 0, 0},
46 {12, 0, 0},
47 {16, 0, 0},
48 {27, 0, 0},
49 {32, 0, 0},
50 {43, 0, 0},
51 {57, 0, 0},
52 {64, 0, 0},
53 {121, 0, 0},
54 },
55 },
56 "Lo1": {
57 cache: NewPageCache(base, 0x1, 0x1),
58 hits: []hit{
59 {1, base, PageSize},
60 {1, 0, 0},
61 {10, 0, 0},
62 },
63 },
64 "Hi1": {
65 cache: NewPageCache(base, 0x1<<63, 0x1),
66 hits: []hit{
67 {1, base + 63*PageSize, 0},
68 {1, 0, 0},
69 {10, 0, 0},
70 },
71 },
72 "Swiss1": {
73 cache: NewPageCache(base, 0x20005555, 0x5505),
74 hits: []hit{
75 {2, 0, 0},
76 {1, base, PageSize},
77 {1, base + 2*PageSize, PageSize},
78 {1, base + 4*PageSize, 0},
79 {1, base + 6*PageSize, 0},
80 {1, base + 8*PageSize, PageSize},
81 {1, base + 10*PageSize, PageSize},
82 {1, base + 12*PageSize, PageSize},
83 {1, base + 14*PageSize, PageSize},
84 {1, base + 29*PageSize, 0},
85 {1, 0, 0},
86 {10, 0, 0},
87 },
88 },
89 "Lo2": {
90 cache: NewPageCache(base, 0x3, 0x2<<62),
91 hits: []hit{
92 {2, base, 0},
93 {2, 0, 0},
94 {1, 0, 0},
95 },
96 },
97 "Hi2": {
98 cache: NewPageCache(base, 0x3<<62, 0x3<<62),
99 hits: []hit{
100 {2, base + 62*PageSize, 2 * PageSize},
101 {2, 0, 0},
102 {1, 0, 0},
103 },
104 },
105 "Swiss2": {
106 cache: NewPageCache(base, 0x3333<<31, 0x3030<<31),
107 hits: []hit{
108 {2, base + 31*PageSize, 0},
109 {2, base + 35*PageSize, 2 * PageSize},
110 {2, base + 39*PageSize, 0},
111 {2, base + 43*PageSize, 2 * PageSize},
112 {2, 0, 0},
113 },
114 },
115 "Hi53": {
116 cache: NewPageCache(base, ((uint64(1)<<53)-1)<<10, ((uint64(1)<<16)-1)<<10),
117 hits: []hit{
118 {53, base + 10*PageSize, 16 * PageSize},
119 {53, 0, 0},
120 {1, 0, 0},
121 },
122 },
123 "Full53": {
124 cache: NewPageCache(base, ^uint64(0), ((uint64(1)<<16)-1)<<10),
125 hits: []hit{
126 {53, base, 16 * PageSize},
127 {53, 0, 0},
128 {1, base + 53*PageSize, 0},
129 },
130 },
131 "Full64": {
132 cache: NewPageCache(base, ^uint64(0), ^uint64(0)),
133 hits: []hit{
134 {64, base, 64 * PageSize},
135 {64, 0, 0},
136 {1, 0, 0},
137 },
138 },
139 "FullMixed": {
140 cache: NewPageCache(base, ^uint64(0), ^uint64(0)),
141 hits: []hit{
142 {5, base, 5 * PageSize},
143 {7, base + 5*PageSize, 7 * PageSize},
144 {1, base + 12*PageSize, 1 * PageSize},
145 {23, base + 13*PageSize, 23 * PageSize},
146 {63, 0, 0},
147 {3, base + 36*PageSize, 3 * PageSize},
148 {3, base + 39*PageSize, 3 * PageSize},
149 {3, base + 42*PageSize, 3 * PageSize},
150 {12, base + 45*PageSize, 12 * PageSize},
151 {11, 0, 0},
152 {4, base + 57*PageSize, 4 * PageSize},
153 {4, 0, 0},
154 {6, 0, 0},
155 {36, 0, 0},
156 {2, base + 61*PageSize, 2 * PageSize},
157 {3, 0, 0},
158 {1, base + 63*PageSize, 1 * PageSize},
159 {4, 0, 0},
160 {2, 0, 0},
161 {62, 0, 0},
162 {1, 0, 0},
163 },
164 },
165 }
166 for name, test := range tests {
167 t.Run(name, func(t *testing.T) {
168 c := test.cache
169 for i, h := range test.hits {
170 b, s := c.Alloc(h.npages)
171 if b != h.base {
172 t.Fatalf("bad alloc base #%d: got 0x%x, want 0x%x", i, b, h.base)
173 }
174 if s != h.scav {
175 t.Fatalf("bad alloc scav #%d: got %d, want %d", i, s, h.scav)
176 }
177 }
178 })
179 }
180 }
181
182 func TestPageCacheFlush(t *testing.T) {
183 if GOOS == "openbsd" && testing.Short() {
184 t.Skip("skipping because virtual memory is limited; see #36210")
185 }
186 bits64ToBitRanges := func(bits uint64, base uint) []BitRange {
187 var ranges []BitRange
188 start, size := uint(0), uint(0)
189 for i := 0; i < 64; i++ {
190 if bits&(1<<i) != 0 {
191 if size == 0 {
192 start = uint(i) + base
193 }
194 size++
195 } else {
196 if size != 0 {
197 ranges = append(ranges, BitRange{start, size})
198 size = 0
199 }
200 }
201 }
202 if size != 0 {
203 ranges = append(ranges, BitRange{start, size})
204 }
205 return ranges
206 }
207 runTest := func(t *testing.T, base uint, cache, scav uint64) {
208
209 beforeAlloc := map[ChunkIdx][]BitRange{
210 BaseChunkIdx: {{base, 64}},
211 }
212 beforeScav := map[ChunkIdx][]BitRange{
213 BaseChunkIdx: {},
214 }
215 b := NewPageAlloc(beforeAlloc, beforeScav)
216 defer FreePageAlloc(b)
217
218
219 c := NewPageCache(PageBase(BaseChunkIdx, base), cache, scav)
220 c.Flush(b)
221 if !c.Empty() {
222 t.Errorf("pageCache flush did not clear cache")
223 }
224
225
226 afterAlloc := map[ChunkIdx][]BitRange{
227 BaseChunkIdx: bits64ToBitRanges(^cache, base),
228 }
229 afterScav := map[ChunkIdx][]BitRange{
230 BaseChunkIdx: bits64ToBitRanges(scav, base),
231 }
232 want := NewPageAlloc(afterAlloc, afterScav)
233 defer FreePageAlloc(want)
234
235
236 checkPageAlloc(t, want, b)
237 }
238
239
240 runTest(t, 0, 0, 0)
241
242
243 runTest(t, 0, ^uint64(0), ^uint64(0))
244
245
246 for i := 0; i < 100; i++ {
247
248 base := uint(rand.Intn(PallocChunkPages/64)) * 64
249
250
251 cache := rand.Uint64()
252 scav := rand.Uint64() & cache
253
254
255 runTest(t, base, cache, scav)
256 }
257 }
258
259 func TestPageAllocAllocToCache(t *testing.T) {
260 if GOOS == "openbsd" && testing.Short() {
261 t.Skip("skipping because virtual memory is limited; see #36210")
262 }
263 type test struct {
264 beforeAlloc map[ChunkIdx][]BitRange
265 beforeScav map[ChunkIdx][]BitRange
266 hits []PageCache
267 afterAlloc map[ChunkIdx][]BitRange
268 afterScav map[ChunkIdx][]BitRange
269 }
270 tests := map[string]test{
271 "ManyArena": {
272 beforeAlloc: map[ChunkIdx][]BitRange{
273 BaseChunkIdx: {{0, PallocChunkPages}},
274 BaseChunkIdx + 1: {{0, PallocChunkPages}},
275 BaseChunkIdx + 2: {{0, PallocChunkPages - 64}},
276 },
277 beforeScav: map[ChunkIdx][]BitRange{
278 BaseChunkIdx: {{0, PallocChunkPages}},
279 BaseChunkIdx + 1: {{0, PallocChunkPages}},
280 BaseChunkIdx + 2: {},
281 },
282 hits: []PageCache{
283 NewPageCache(PageBase(BaseChunkIdx+2, PallocChunkPages-64), ^uint64(0), 0),
284 },
285 afterAlloc: map[ChunkIdx][]BitRange{
286 BaseChunkIdx: {{0, PallocChunkPages}},
287 BaseChunkIdx + 1: {{0, PallocChunkPages}},
288 BaseChunkIdx + 2: {{0, PallocChunkPages}},
289 },
290 },
291
292 "Fail": {
293 beforeAlloc: map[ChunkIdx][]BitRange{
294 BaseChunkIdx: {{0, PallocChunkPages}},
295 },
296 hits: []PageCache{
297 NewPageCache(0, 0, 0),
298 NewPageCache(0, 0, 0),
299 NewPageCache(0, 0, 0),
300 },
301 afterAlloc: map[ChunkIdx][]BitRange{
302 BaseChunkIdx: {{0, PallocChunkPages}},
303 },
304 },
305 "RetainScavBits": {
306 beforeAlloc: map[ChunkIdx][]BitRange{
307 BaseChunkIdx: {{0, 1}, {10, 2}},
308 },
309 beforeScav: map[ChunkIdx][]BitRange{
310 BaseChunkIdx: {{0, 4}, {11, 1}},
311 },
312 hits: []PageCache{
313 NewPageCache(PageBase(BaseChunkIdx, 0), ^uint64(0x1|(0x3<<10)), 0x7<<1),
314 },
315 afterAlloc: map[ChunkIdx][]BitRange{
316 BaseChunkIdx: {{0, 64}},
317 },
318 afterScav: map[ChunkIdx][]BitRange{
319 BaseChunkIdx: {{0, 1}, {11, 1}},
320 },
321 },
322 }
323 if PallocChunkPages >= 512 {
324 tests["AllFree"] = test{
325 beforeAlloc: map[ChunkIdx][]BitRange{
326 BaseChunkIdx: {},
327 },
328 beforeScav: map[ChunkIdx][]BitRange{
329 BaseChunkIdx: {{1, 1}, {64, 64}},
330 },
331 hits: []PageCache{
332 NewPageCache(PageBase(BaseChunkIdx, 0), ^uint64(0), 0x2),
333 NewPageCache(PageBase(BaseChunkIdx, 64), ^uint64(0), ^uint64(0)),
334 NewPageCache(PageBase(BaseChunkIdx, 128), ^uint64(0), 0),
335 NewPageCache(PageBase(BaseChunkIdx, 192), ^uint64(0), 0),
336 },
337 afterAlloc: map[ChunkIdx][]BitRange{
338 BaseChunkIdx: {{0, 256}},
339 },
340 }
341 tests["NotContiguous"] = test{
342 beforeAlloc: map[ChunkIdx][]BitRange{
343 BaseChunkIdx: {{0, PallocChunkPages}},
344 BaseChunkIdx + 0xff: {{0, 0}},
345 },
346 beforeScav: map[ChunkIdx][]BitRange{
347 BaseChunkIdx: {{0, PallocChunkPages}},
348 BaseChunkIdx + 0xff: {{31, 67}},
349 },
350 hits: []PageCache{
351 NewPageCache(PageBase(BaseChunkIdx+0xff, 0), ^uint64(0), ((uint64(1)<<33)-1)<<31),
352 },
353 afterAlloc: map[ChunkIdx][]BitRange{
354 BaseChunkIdx: {{0, PallocChunkPages}},
355 BaseChunkIdx + 0xff: {{0, 64}},
356 },
357 afterScav: map[ChunkIdx][]BitRange{
358 BaseChunkIdx: {{0, PallocChunkPages}},
359 BaseChunkIdx + 0xff: {{64, 34}},
360 },
361 }
362 tests["First"] = test{
363 beforeAlloc: map[ChunkIdx][]BitRange{
364 BaseChunkIdx: {{0, 32}, {33, 31}, {96, 32}},
365 },
366 beforeScav: map[ChunkIdx][]BitRange{
367 BaseChunkIdx: {{1, 4}, {31, 5}, {66, 2}},
368 },
369 hits: []PageCache{
370 NewPageCache(PageBase(BaseChunkIdx, 0), 1<<32, 1<<32),
371 NewPageCache(PageBase(BaseChunkIdx, 64), (uint64(1)<<32)-1, 0x3<<2),
372 },
373 afterAlloc: map[ChunkIdx][]BitRange{
374 BaseChunkIdx: {{0, 128}},
375 },
376 }
377 }
378
379
380 if PageAlloc64Bit != 0 && goos.IsIos == 0 {
381 const chunkIdxBigJump = 0x100000
382
383
384
385
386 sumsPerPhysPage := ChunkIdx(PhysPageSize / PallocSumBytes)
387 baseChunkIdx := BaseChunkIdx &^ (sumsPerPhysPage - 1)
388 tests["DiscontiguousMappedSumBoundary"] = test{
389 beforeAlloc: map[ChunkIdx][]BitRange{
390 baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages - 1}},
391 baseChunkIdx + chunkIdxBigJump: {{1, PallocChunkPages - 1}},
392 },
393 beforeScav: map[ChunkIdx][]BitRange{
394 baseChunkIdx + sumsPerPhysPage - 1: {},
395 baseChunkIdx + chunkIdxBigJump: {},
396 },
397 hits: []PageCache{
398 NewPageCache(PageBase(baseChunkIdx+sumsPerPhysPage-1, PallocChunkPages-64), 1<<63, 0),
399 NewPageCache(PageBase(baseChunkIdx+chunkIdxBigJump, 0), 1, 0),
400 NewPageCache(0, 0, 0),
401 },
402 afterAlloc: map[ChunkIdx][]BitRange{
403 baseChunkIdx + sumsPerPhysPage - 1: {{0, PallocChunkPages}},
404 baseChunkIdx + chunkIdxBigJump: {{0, PallocChunkPages}},
405 },
406 }
407 }
408 for name, v := range tests {
409 t.Run(name, func(t *testing.T) {
410 b := NewPageAlloc(v.beforeAlloc, v.beforeScav)
411 defer FreePageAlloc(b)
412
413 for _, expect := range v.hits {
414 checkPageCache(t, b.AllocToCache(), expect)
415 if t.Failed() {
416 return
417 }
418 }
419 want := NewPageAlloc(v.afterAlloc, v.afterScav)
420 defer FreePageAlloc(want)
421
422 checkPageAlloc(t, want, b)
423 })
424 }
425 }
426
View as plain text