Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5)

Side by Side Diff: third_party/WebKit/Source/wtf/allocator/PartitionAllocTest.cpp

Issue 2518253002: Move Partition Allocator into Chromium base. (Closed)
Patch Set: Move OOM_CRASH into its own, more specific header. Fixes Windows build. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "wtf/allocator/PartitionAlloc.h"
32
33 #include "testing/gtest/include/gtest/gtest.h"
34 #include "wtf/BitwiseOperations.h"
35 #include "wtf/CPU.h"
36 #include "wtf/PtrUtil.h"
37 #include "wtf/Vector.h"
38 #include <memory>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #if OS(POSIX)
43 #include <sys/mman.h>
44 #include <sys/resource.h>
45 #include <sys/time.h>
46
47 #ifndef MAP_ANONYMOUS
48 #define MAP_ANONYMOUS MAP_ANON
49 #endif
50 #endif // OS(POSIX)
51
52 #if !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
53
54 namespace WTF {
55
56 namespace {
57
58 const size_t kTestMaxAllocation = 4096;
59 SizeSpecificPartitionAllocator<kTestMaxAllocation> allocator;
60 PartitionAllocatorGeneric genericAllocator;
61
62 const size_t kTestAllocSize = 16;
63 #if !ENABLE(ASSERT)
64 const size_t kPointerOffset = 0;
65 const size_t kExtraAllocSize = 0;
66 #else
67 const size_t kPointerOffset = WTF::kCookieSize;
68 const size_t kExtraAllocSize = WTF::kCookieSize * 2;
69 #endif
70 const size_t kRealAllocSize = kTestAllocSize + kExtraAllocSize;
71 const size_t kTestBucketIndex = kRealAllocSize >> WTF::kBucketShift;
72
73 const char* typeName = nullptr;
74
75 void TestSetup() {
76 allocator.init();
77 genericAllocator.init();
78 }
79
80 void TestShutdown() {
81 // We expect no leaks in the general case. We have a test for leak
82 // detection.
83 EXPECT_TRUE(allocator.shutdown());
84 EXPECT_TRUE(genericAllocator.shutdown());
85 }
86
87 #if !CPU(64BIT) || OS(POSIX)
88 bool SetAddressSpaceLimit() {
89 #if !CPU(64BIT)
90 // 32 bits => address space is limited already.
91 return true;
92 #elif OS(POSIX) && !OS(MACOSX)
93 // Mac will accept RLIMIT_AS changes but it is not enforced.
94 // See https://crbug.com/435269 and rdar://17576114.
95 const size_t kAddressSpaceLimit = static_cast<size_t>(4096) * 1024 * 1024;
96 struct rlimit limit;
97 if (getrlimit(RLIMIT_AS, &limit) != 0)
98 return false;
99 if (limit.rlim_cur == RLIM_INFINITY || limit.rlim_cur > kAddressSpaceLimit) {
100 limit.rlim_cur = kAddressSpaceLimit;
101 if (setrlimit(RLIMIT_AS, &limit) != 0)
102 return false;
103 }
104 return true;
105 #else
106 return false;
107 #endif
108 }
109
110 bool ClearAddressSpaceLimit() {
111 #if !CPU(64BIT)
112 return true;
113 #elif OS(POSIX)
114 struct rlimit limit;
115 if (getrlimit(RLIMIT_AS, &limit) != 0)
116 return false;
117 limit.rlim_cur = limit.rlim_max;
118 if (setrlimit(RLIMIT_AS, &limit) != 0)
119 return false;
120 return true;
121 #else
122 return false;
123 #endif
124 }
125 #endif
126
127 PartitionPage* GetFullPage(size_t size) {
128 size_t realSize = size + kExtraAllocSize;
129 size_t bucketIdx = realSize >> kBucketShift;
130 PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx];
131 size_t numSlots =
132 (bucket->numSystemPagesPerSlotSpan * kSystemPageSize) / realSize;
133 void* first = 0;
134 void* last = 0;
135 size_t i;
136 for (i = 0; i < numSlots; ++i) {
137 void* ptr = partitionAlloc(allocator.root(), size, typeName);
138 EXPECT_TRUE(ptr);
139 if (!i)
140 first = partitionCookieFreePointerAdjust(ptr);
141 else if (i == numSlots - 1)
142 last = partitionCookieFreePointerAdjust(ptr);
143 }
144 EXPECT_EQ(partitionPointerToPage(first), partitionPointerToPage(last));
145 if (bucket->numSystemPagesPerSlotSpan == kNumSystemPagesPerPartitionPage)
146 EXPECT_EQ(reinterpret_cast<size_t>(first) & kPartitionPageBaseMask,
147 reinterpret_cast<size_t>(last) & kPartitionPageBaseMask);
148 EXPECT_EQ(numSlots,
149 static_cast<size_t>(bucket->activePagesHead->numAllocatedSlots));
150 EXPECT_EQ(0, bucket->activePagesHead->freelistHead);
151 EXPECT_TRUE(bucket->activePagesHead);
152 EXPECT_TRUE(bucket->activePagesHead != &PartitionRootGeneric::gSeedPage);
153 return bucket->activePagesHead;
154 }
155
156 void FreeFullPage(PartitionPage* page) {
157 size_t size = page->bucket->slotSize;
158 size_t numSlots =
159 (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) / size;
160 EXPECT_EQ(numSlots, static_cast<size_t>(abs(page->numAllocatedSlots)));
161 char* ptr = reinterpret_cast<char*>(partitionPageToPointer(page));
162 size_t i;
163 for (i = 0; i < numSlots; ++i) {
164 partitionFree(ptr + kPointerOffset);
165 ptr += size;
166 }
167 }
168
169 void CycleFreeCache(size_t size) {
170 size_t realSize = size + kExtraAllocSize;
171 size_t bucketIdx = realSize >> kBucketShift;
172 PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx];
173 ASSERT(!bucket->activePagesHead->numAllocatedSlots);
174
175 for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
176 void* ptr = partitionAlloc(allocator.root(), size, typeName);
177 EXPECT_EQ(1, bucket->activePagesHead->numAllocatedSlots);
178 partitionFree(ptr);
179 EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots);
180 EXPECT_NE(-1, bucket->activePagesHead->emptyCacheIndex);
181 }
182 }
183
184 void CycleGenericFreeCache(size_t size) {
185 for (size_t i = 0; i < kMaxFreeableSpans; ++i) {
186 void* ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
187 PartitionPage* page =
188 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
189 PartitionBucket* bucket = page->bucket;
190 EXPECT_EQ(1, bucket->activePagesHead->numAllocatedSlots);
191 partitionFreeGeneric(genericAllocator.root(), ptr);
192 EXPECT_EQ(0, bucket->activePagesHead->numAllocatedSlots);
193 EXPECT_NE(-1, bucket->activePagesHead->emptyCacheIndex);
194 }
195 }
196
197 void CheckPageInCore(void* ptr, bool inCore) {
198 #if OS(LINUX)
199 unsigned char ret;
200 EXPECT_EQ(0, mincore(ptr, kSystemPageSize, &ret));
201 EXPECT_EQ(inCore, ret);
202 #endif
203 }
204
205 class MockPartitionStatsDumper : public PartitionStatsDumper {
206 public:
207 MockPartitionStatsDumper()
208 : m_totalResidentBytes(0),
209 m_totalActiveBytes(0),
210 m_totalDecommittableBytes(0),
211 m_totalDiscardableBytes(0) {}
212
213 void partitionDumpTotals(const char* partitionName,
214 const PartitionMemoryStats* memoryStats) override {
215 EXPECT_GE(memoryStats->totalMmappedBytes, memoryStats->totalResidentBytes);
216 EXPECT_EQ(m_totalResidentBytes, memoryStats->totalResidentBytes);
217 EXPECT_EQ(m_totalActiveBytes, memoryStats->totalActiveBytes);
218 EXPECT_EQ(m_totalDecommittableBytes, memoryStats->totalDecommittableBytes);
219 EXPECT_EQ(m_totalDiscardableBytes, memoryStats->totalDiscardableBytes);
220 }
221
222 void partitionsDumpBucketStats(
223 const char* partitionName,
224 const PartitionBucketMemoryStats* memoryStats) override {
225 (void)partitionName;
226 EXPECT_TRUE(memoryStats->isValid);
227 EXPECT_EQ(0u, memoryStats->bucketSlotSize & kAllocationGranularityMask);
228 m_bucketStats.append(*memoryStats);
229 m_totalResidentBytes += memoryStats->residentBytes;
230 m_totalActiveBytes += memoryStats->activeBytes;
231 m_totalDecommittableBytes += memoryStats->decommittableBytes;
232 m_totalDiscardableBytes += memoryStats->discardableBytes;
233 }
234
235 bool IsMemoryAllocationRecorded() {
236 return m_totalResidentBytes != 0 && m_totalActiveBytes != 0;
237 }
238
239 const PartitionBucketMemoryStats* GetBucketStats(size_t bucketSize) {
240 for (size_t i = 0; i < m_bucketStats.size(); ++i) {
241 if (m_bucketStats[i].bucketSlotSize == bucketSize)
242 return &m_bucketStats[i];
243 }
244 return 0;
245 }
246
247 private:
248 size_t m_totalResidentBytes;
249 size_t m_totalActiveBytes;
250 size_t m_totalDecommittableBytes;
251 size_t m_totalDiscardableBytes;
252
253 Vector<PartitionBucketMemoryStats> m_bucketStats;
254 };
255
256 } // anonymous namespace
257
258 // Check that the most basic of allocate / free pairs work.
259 TEST(PartitionAllocTest, Basic) {
260 TestSetup();
261 PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
262 PartitionPage* seedPage = &PartitionRootGeneric::gSeedPage;
263
264 EXPECT_FALSE(bucket->emptyPagesHead);
265 EXPECT_FALSE(bucket->decommittedPagesHead);
266 EXPECT_EQ(seedPage, bucket->activePagesHead);
267 EXPECT_EQ(0, bucket->activePagesHead->nextPage);
268
269 void* ptr = partitionAlloc(allocator.root(), kTestAllocSize, typeName);
270 EXPECT_TRUE(ptr);
271 EXPECT_EQ(kPointerOffset,
272 reinterpret_cast<size_t>(ptr) & kPartitionPageOffsetMask);
273 // Check that the offset appears to include a guard page.
274 EXPECT_EQ(kPartitionPageSize + kPointerOffset,
275 reinterpret_cast<size_t>(ptr) & kSuperPageOffsetMask);
276
277 partitionFree(ptr);
278 // Expect that the last active page gets noticed as empty but doesn't get
279 // decommitted.
280 EXPECT_TRUE(bucket->emptyPagesHead);
281 EXPECT_FALSE(bucket->decommittedPagesHead);
282
283 TestShutdown();
284 }
285
286 // Check that we can detect a memory leak.
287 TEST(PartitionAllocTest, SimpleLeak) {
288 TestSetup();
289 void* leakedPtr = partitionAlloc(allocator.root(), kTestAllocSize, typeName);
290 (void)leakedPtr;
291 void* leakedPtr2 =
292 partitionAllocGeneric(genericAllocator.root(), kTestAllocSize, typeName);
293 (void)leakedPtr2;
294 EXPECT_FALSE(allocator.shutdown());
295 EXPECT_FALSE(genericAllocator.shutdown());
296 }
297
298 // Test multiple allocations, and freelist handling.
299 TEST(PartitionAllocTest, MultiAlloc) {
300 TestSetup();
301
302 char* ptr1 = reinterpret_cast<char*>(
303 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
304 char* ptr2 = reinterpret_cast<char*>(
305 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
306 EXPECT_TRUE(ptr1);
307 EXPECT_TRUE(ptr2);
308 ptrdiff_t diff = ptr2 - ptr1;
309 EXPECT_EQ(static_cast<ptrdiff_t>(kRealAllocSize), diff);
310
311 // Check that we re-use the just-freed slot.
312 partitionFree(ptr2);
313 ptr2 = reinterpret_cast<char*>(
314 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
315 EXPECT_TRUE(ptr2);
316 diff = ptr2 - ptr1;
317 EXPECT_EQ(static_cast<ptrdiff_t>(kRealAllocSize), diff);
318 partitionFree(ptr1);
319 ptr1 = reinterpret_cast<char*>(
320 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
321 EXPECT_TRUE(ptr1);
322 diff = ptr2 - ptr1;
323 EXPECT_EQ(static_cast<ptrdiff_t>(kRealAllocSize), diff);
324
325 char* ptr3 = reinterpret_cast<char*>(
326 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
327 EXPECT_TRUE(ptr3);
328 diff = ptr3 - ptr1;
329 EXPECT_EQ(static_cast<ptrdiff_t>(kRealAllocSize * 2), diff);
330
331 partitionFree(ptr1);
332 partitionFree(ptr2);
333 partitionFree(ptr3);
334
335 TestShutdown();
336 }
337
338 // Test a bucket with multiple pages.
339 TEST(PartitionAllocTest, MultiPages) {
340 TestSetup();
341 PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
342
343 PartitionPage* page = GetFullPage(kTestAllocSize);
344 FreeFullPage(page);
345 EXPECT_TRUE(bucket->emptyPagesHead);
346 EXPECT_EQ(&PartitionRootGeneric::gSeedPage, bucket->activePagesHead);
347 EXPECT_EQ(0, page->nextPage);
348 EXPECT_EQ(0, page->numAllocatedSlots);
349
350 page = GetFullPage(kTestAllocSize);
351 PartitionPage* page2 = GetFullPage(kTestAllocSize);
352
353 EXPECT_EQ(page2, bucket->activePagesHead);
354 EXPECT_EQ(0, page2->nextPage);
355 EXPECT_EQ(reinterpret_cast<uintptr_t>(partitionPageToPointer(page)) &
356 kSuperPageBaseMask,
357 reinterpret_cast<uintptr_t>(partitionPageToPointer(page2)) &
358 kSuperPageBaseMask);
359
360 // Fully free the non-current page. This will leave us with no current
361 // active page because one is empty and the other is full.
362 FreeFullPage(page);
363 EXPECT_EQ(0, page->numAllocatedSlots);
364 EXPECT_TRUE(bucket->emptyPagesHead);
365 EXPECT_EQ(&PartitionRootGeneric::gSeedPage, bucket->activePagesHead);
366
367 // Allocate a new page, it should pull from the freelist.
368 page = GetFullPage(kTestAllocSize);
369 EXPECT_FALSE(bucket->emptyPagesHead);
370 EXPECT_EQ(page, bucket->activePagesHead);
371
372 FreeFullPage(page);
373 FreeFullPage(page2);
374 EXPECT_EQ(0, page->numAllocatedSlots);
375 EXPECT_EQ(0, page2->numAllocatedSlots);
376 EXPECT_EQ(0, page2->numUnprovisionedSlots);
377 EXPECT_NE(-1, page2->emptyCacheIndex);
378
379 TestShutdown();
380 }
381
382 // Test some finer aspects of internal page transitions.
383 TEST(PartitionAllocTest, PageTransitions) {
384 TestSetup();
385 PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
386
387 PartitionPage* page1 = GetFullPage(kTestAllocSize);
388 EXPECT_EQ(page1, bucket->activePagesHead);
389 EXPECT_EQ(0, page1->nextPage);
390 PartitionPage* page2 = GetFullPage(kTestAllocSize);
391 EXPECT_EQ(page2, bucket->activePagesHead);
392 EXPECT_EQ(0, page2->nextPage);
393
394 // Bounce page1 back into the non-full list then fill it up again.
395 char* ptr =
396 reinterpret_cast<char*>(partitionPageToPointer(page1)) + kPointerOffset;
397 partitionFree(ptr);
398 EXPECT_EQ(page1, bucket->activePagesHead);
399 (void)partitionAlloc(allocator.root(), kTestAllocSize, typeName);
400 EXPECT_EQ(page1, bucket->activePagesHead);
401 EXPECT_EQ(page2, bucket->activePagesHead->nextPage);
402
403 // Allocating another page at this point should cause us to scan over page1
404 // (which is both full and NOT our current page), and evict it from the
405 // freelist. Older code had a O(n^2) condition due to failure to do this.
406 PartitionPage* page3 = GetFullPage(kTestAllocSize);
407 EXPECT_EQ(page3, bucket->activePagesHead);
408 EXPECT_EQ(0, page3->nextPage);
409
410 // Work out a pointer into page2 and free it.
411 ptr = reinterpret_cast<char*>(partitionPageToPointer(page2)) + kPointerOffset;
412 partitionFree(ptr);
413 // Trying to allocate at this time should cause us to cycle around to page2
414 // and find the recently freed slot.
415 char* newPtr = reinterpret_cast<char*>(
416 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
417 EXPECT_EQ(ptr, newPtr);
418 EXPECT_EQ(page2, bucket->activePagesHead);
419 EXPECT_EQ(page3, page2->nextPage);
420
421 // Work out a pointer into page1 and free it. This should pull the page
422 // back into the list of available pages.
423 ptr = reinterpret_cast<char*>(partitionPageToPointer(page1)) + kPointerOffset;
424 partitionFree(ptr);
425 // This allocation should be satisfied by page1.
426 newPtr = reinterpret_cast<char*>(
427 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
428 EXPECT_EQ(ptr, newPtr);
429 EXPECT_EQ(page1, bucket->activePagesHead);
430 EXPECT_EQ(page2, page1->nextPage);
431
432 FreeFullPage(page3);
433 FreeFullPage(page2);
434 FreeFullPage(page1);
435
436 // Allocating whilst in this state exposed a bug, so keep the test.
437 ptr = reinterpret_cast<char*>(
438 partitionAlloc(allocator.root(), kTestAllocSize, typeName));
439 partitionFree(ptr);
440
441 TestShutdown();
442 }
443
444 // Test some corner cases relating to page transitions in the internal
445 // free page list metadata bucket.
446 TEST(PartitionAllocTest, FreePageListPageTransitions) {
447 TestSetup();
448 PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
449
450 size_t numToFillFreeListPage =
451 kPartitionPageSize / (sizeof(PartitionPage) + kExtraAllocSize);
452 // The +1 is because we need to account for the fact that the current page
453 // never gets thrown on the freelist.
454 ++numToFillFreeListPage;
455 std::unique_ptr<PartitionPage* []> pages =
456 wrapArrayUnique(new PartitionPage*[numToFillFreeListPage]);
457
458 size_t i;
459 for (i = 0; i < numToFillFreeListPage; ++i) {
460 pages[i] = GetFullPage(kTestAllocSize);
461 }
462 EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->activePagesHead);
463 for (i = 0; i < numToFillFreeListPage; ++i)
464 FreeFullPage(pages[i]);
465 EXPECT_EQ(&PartitionRootGeneric::gSeedPage, bucket->activePagesHead);
466 EXPECT_TRUE(bucket->emptyPagesHead);
467
468 // Allocate / free in a different bucket size so we get control of a
469 // different free page list. We need two pages because one will be the last
470 // active page and not get freed.
471 PartitionPage* page1 = GetFullPage(kTestAllocSize * 2);
472 PartitionPage* page2 = GetFullPage(kTestAllocSize * 2);
473 FreeFullPage(page1);
474 FreeFullPage(page2);
475
476 for (i = 0; i < numToFillFreeListPage; ++i) {
477 pages[i] = GetFullPage(kTestAllocSize);
478 }
479 EXPECT_EQ(pages[numToFillFreeListPage - 1], bucket->activePagesHead);
480
481 for (i = 0; i < numToFillFreeListPage; ++i)
482 FreeFullPage(pages[i]);
483 EXPECT_EQ(&PartitionRootGeneric::gSeedPage, bucket->activePagesHead);
484 EXPECT_TRUE(bucket->emptyPagesHead);
485
486 TestShutdown();
487 }
488
489 // Test a large series of allocations that cross more than one underlying
490 // 64KB super page allocation.
491 TEST(PartitionAllocTest, MultiPageAllocs) {
492 TestSetup();
493 // This is guaranteed to cross a super page boundary because the first
494 // partition page "slot" will be taken up by a guard page.
495 size_t numPagesNeeded = kNumPartitionPagesPerSuperPage;
496 // The super page should begin and end in a guard so we one less page in
497 // order to allocate a single page in the new super page.
498 --numPagesNeeded;
499
500 EXPECT_GT(numPagesNeeded, 1u);
501 std::unique_ptr<PartitionPage* []> pages;
502 pages = wrapArrayUnique(new PartitionPage*[numPagesNeeded]);
503 uintptr_t firstSuperPageBase = 0;
504 size_t i;
505 for (i = 0; i < numPagesNeeded; ++i) {
506 pages[i] = GetFullPage(kTestAllocSize);
507 void* storagePtr = partitionPageToPointer(pages[i]);
508 if (!i)
509 firstSuperPageBase =
510 reinterpret_cast<uintptr_t>(storagePtr) & kSuperPageBaseMask;
511 if (i == numPagesNeeded - 1) {
512 uintptr_t secondSuperPageBase =
513 reinterpret_cast<uintptr_t>(storagePtr) & kSuperPageBaseMask;
514 uintptr_t secondSuperPageOffset =
515 reinterpret_cast<uintptr_t>(storagePtr) & kSuperPageOffsetMask;
516 EXPECT_FALSE(secondSuperPageBase == firstSuperPageBase);
517 // Check that we allocated a guard page for the second page.
518 EXPECT_EQ(kPartitionPageSize, secondSuperPageOffset);
519 }
520 }
521 for (i = 0; i < numPagesNeeded; ++i)
522 FreeFullPage(pages[i]);
523
524 TestShutdown();
525 }
526
527 // Test the generic allocation functions that can handle arbitrary sizes and
528 // reallocing etc.
529 TEST(PartitionAllocTest, GenericAlloc) {
530 TestSetup();
531
532 void* ptr = partitionAllocGeneric(genericAllocator.root(), 1, typeName);
533 EXPECT_TRUE(ptr);
534 partitionFreeGeneric(genericAllocator.root(), ptr);
535 ptr = partitionAllocGeneric(genericAllocator.root(), kGenericMaxBucketed + 1,
536 typeName);
537 EXPECT_TRUE(ptr);
538 partitionFreeGeneric(genericAllocator.root(), ptr);
539
540 ptr = partitionAllocGeneric(genericAllocator.root(), 1, typeName);
541 EXPECT_TRUE(ptr);
542 void* origPtr = ptr;
543 char* charPtr = static_cast<char*>(ptr);
544 *charPtr = 'A';
545
546 // Change the size of the realloc, remaining inside the same bucket.
547 void* newPtr =
548 partitionReallocGeneric(genericAllocator.root(), ptr, 2, typeName);
549 EXPECT_EQ(ptr, newPtr);
550 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1, typeName);
551 EXPECT_EQ(ptr, newPtr);
552 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr,
553 kGenericSmallestBucket, typeName);
554 EXPECT_EQ(ptr, newPtr);
555
556 // Change the size of the realloc, switching buckets.
557 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr,
558 kGenericSmallestBucket + 1, typeName);
559 EXPECT_NE(newPtr, ptr);
560 // Check that the realloc copied correctly.
561 char* newCharPtr = static_cast<char*>(newPtr);
562 EXPECT_EQ(*newCharPtr, 'A');
563 #if ENABLE(ASSERT)
564 // Subtle: this checks for an old bug where we copied too much from the
565 // source of the realloc. The condition can be detected by a trashing of
566 // the uninitialized value in the space of the upsized allocation.
567 EXPECT_EQ(kUninitializedByte,
568 static_cast<unsigned char>(*(newCharPtr + kGenericSmallestBucket)));
569 #endif
570 *newCharPtr = 'B';
571 // The realloc moved. To check that the old allocation was freed, we can
572 // do an alloc of the old allocation size and check that the old allocation
573 // address is at the head of the freelist and reused.
574 void* reusedPtr = partitionAllocGeneric(genericAllocator.root(), 1, typeName);
575 EXPECT_EQ(reusedPtr, origPtr);
576 partitionFreeGeneric(genericAllocator.root(), reusedPtr);
577
578 // Downsize the realloc.
579 ptr = newPtr;
580 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1, typeName);
581 EXPECT_EQ(newPtr, origPtr);
582 newCharPtr = static_cast<char*>(newPtr);
583 EXPECT_EQ(*newCharPtr, 'B');
584 *newCharPtr = 'C';
585
586 // Upsize the realloc to outside the partition.
587 ptr = newPtr;
588 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr,
589 kGenericMaxBucketed + 1, typeName);
590 EXPECT_NE(newPtr, ptr);
591 newCharPtr = static_cast<char*>(newPtr);
592 EXPECT_EQ(*newCharPtr, 'C');
593 *newCharPtr = 'D';
594
595 // Upsize and downsize the realloc, remaining outside the partition.
596 ptr = newPtr;
597 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr,
598 kGenericMaxBucketed * 10, typeName);
599 newCharPtr = static_cast<char*>(newPtr);
600 EXPECT_EQ(*newCharPtr, 'D');
601 *newCharPtr = 'E';
602 ptr = newPtr;
603 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr,
604 kGenericMaxBucketed * 2, typeName);
605 newCharPtr = static_cast<char*>(newPtr);
606 EXPECT_EQ(*newCharPtr, 'E');
607 *newCharPtr = 'F';
608
609 // Downsize the realloc to inside the partition.
610 ptr = newPtr;
611 newPtr = partitionReallocGeneric(genericAllocator.root(), ptr, 1, typeName);
612 EXPECT_NE(newPtr, ptr);
613 EXPECT_EQ(newPtr, origPtr);
614 newCharPtr = static_cast<char*>(newPtr);
615 EXPECT_EQ(*newCharPtr, 'F');
616
617 partitionFreeGeneric(genericAllocator.root(), newPtr);
618 TestShutdown();
619 }
620
621 // Test the generic allocation functions can handle some specific sizes of
622 // interest.
623 TEST(PartitionAllocTest, GenericAllocSizes) {
624 TestSetup();
625
626 void* ptr = partitionAllocGeneric(genericAllocator.root(), 0, typeName);
627 EXPECT_TRUE(ptr);
628 partitionFreeGeneric(genericAllocator.root(), ptr);
629
630 // kPartitionPageSize is interesting because it results in just one
631 // allocation per page, which tripped up some corner cases.
632 size_t size = kPartitionPageSize - kExtraAllocSize;
633 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
634 EXPECT_TRUE(ptr);
635 void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
636 EXPECT_TRUE(ptr2);
637 partitionFreeGeneric(genericAllocator.root(), ptr);
638 // Should be freeable at this point.
639 PartitionPage* page =
640 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
641 EXPECT_NE(-1, page->emptyCacheIndex);
642 partitionFreeGeneric(genericAllocator.root(), ptr2);
643
644 size = (((kPartitionPageSize * kMaxPartitionPagesPerSlotSpan) -
645 kSystemPageSize) /
646 2) -
647 kExtraAllocSize;
648 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
649 EXPECT_TRUE(ptr);
650 memset(ptr, 'A', size);
651 ptr2 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
652 EXPECT_TRUE(ptr2);
653 void* ptr3 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
654 EXPECT_TRUE(ptr3);
655 void* ptr4 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
656 EXPECT_TRUE(ptr4);
657
658 page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
659 PartitionPage* page2 =
660 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr3));
661 EXPECT_NE(page, page2);
662
663 partitionFreeGeneric(genericAllocator.root(), ptr);
664 partitionFreeGeneric(genericAllocator.root(), ptr3);
665 partitionFreeGeneric(genericAllocator.root(), ptr2);
666 // Should be freeable at this point.
667 EXPECT_NE(-1, page->emptyCacheIndex);
668 EXPECT_EQ(0, page->numAllocatedSlots);
669 EXPECT_EQ(0, page->numUnprovisionedSlots);
670 void* newPtr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
671 EXPECT_EQ(ptr3, newPtr);
672 newPtr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
673 EXPECT_EQ(ptr2, newPtr);
674 #if OS(LINUX) && !ENABLE(ASSERT)
675 // On Linux, we have a guarantee that freelisting a page should cause its
676 // contents to be nulled out. We check for null here to detect an bug we
677 // had where a large slot size was causing us to not properly free all
678 // resources back to the system.
679 // We only run the check when asserts are disabled because when they are
680 // enabled, the allocated area is overwritten with an "uninitialized"
681 // byte pattern.
682 EXPECT_EQ(0, *(reinterpret_cast<char*>(newPtr) + (size - 1)));
683 #endif
684 partitionFreeGeneric(genericAllocator.root(), newPtr);
685 partitionFreeGeneric(genericAllocator.root(), ptr3);
686 partitionFreeGeneric(genericAllocator.root(), ptr4);
687
688 // Can we allocate a massive (512MB) size?
689 // Allocate 512MB, but +1, to test for cookie writing alignment issues.
690 ptr = partitionAllocGeneric(genericAllocator.root(), 512 * 1024 * 1024 + 1,
691 typeName);
692 partitionFreeGeneric(genericAllocator.root(), ptr);
693
694 // Check a more reasonable, but still direct mapped, size.
695 // Chop a system page and a byte off to test for rounding errors.
696 size = 20 * 1024 * 1024;
697 size -= kSystemPageSize;
698 size -= 1;
699 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
700 char* charPtr = reinterpret_cast<char*>(ptr);
701 *(charPtr + (size - 1)) = 'A';
702 partitionFreeGeneric(genericAllocator.root(), ptr);
703
704 // Can we free null?
705 partitionFreeGeneric(genericAllocator.root(), 0);
706
707 // Do we correctly get a null for a failed allocation?
708 EXPECT_EQ(0, partitionAllocGenericFlags(genericAllocator.root(),
709 PartitionAllocReturnNull,
710 3u * 1024 * 1024 * 1024, typeName));
711
712 TestShutdown();
713 }
714
715 // Test that we can fetch the real allocated size after an allocation.
716 TEST(PartitionAllocTest, GenericAllocGetSize) {
717 TestSetup();
718
719 void* ptr;
720 size_t requestedSize, actualSize, predictedSize;
721
722 EXPECT_TRUE(partitionAllocSupportsGetSize());
723
724 // Allocate something small.
725 requestedSize = 511 - kExtraAllocSize;
726 predictedSize =
727 partitionAllocActualSize(genericAllocator.root(), requestedSize);
728 ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize, typeName);
729 EXPECT_TRUE(ptr);
730 actualSize = partitionAllocGetSize(ptr);
731 EXPECT_EQ(predictedSize, actualSize);
732 EXPECT_LT(requestedSize, actualSize);
733 partitionFreeGeneric(genericAllocator.root(), ptr);
734
735 // Allocate a size that should be a perfect match for a bucket, because it
736 // is an exact power of 2.
737 requestedSize = (256 * 1024) - kExtraAllocSize;
738 predictedSize =
739 partitionAllocActualSize(genericAllocator.root(), requestedSize);
740 ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize, typeName);
741 EXPECT_TRUE(ptr);
742 actualSize = partitionAllocGetSize(ptr);
743 EXPECT_EQ(predictedSize, actualSize);
744 EXPECT_EQ(requestedSize, actualSize);
745 partitionFreeGeneric(genericAllocator.root(), ptr);
746
747 // Allocate a size that is a system page smaller than a bucket. GetSize()
748 // should return a larger size than we asked for now.
749 requestedSize = (256 * 1024) - kSystemPageSize - kExtraAllocSize;
750 predictedSize =
751 partitionAllocActualSize(genericAllocator.root(), requestedSize);
752 ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize, typeName);
753 EXPECT_TRUE(ptr);
754 actualSize = partitionAllocGetSize(ptr);
755 EXPECT_EQ(predictedSize, actualSize);
756 EXPECT_EQ(requestedSize + kSystemPageSize, actualSize);
757 // Check that we can write at the end of the reported size too.
758 char* charPtr = reinterpret_cast<char*>(ptr);
759 *(charPtr + (actualSize - 1)) = 'A';
760 partitionFreeGeneric(genericAllocator.root(), ptr);
761
762 // Allocate something very large, and uneven.
763 requestedSize = 512 * 1024 * 1024 - 1;
764 predictedSize =
765 partitionAllocActualSize(genericAllocator.root(), requestedSize);
766 ptr = partitionAllocGeneric(genericAllocator.root(), requestedSize, typeName);
767 EXPECT_TRUE(ptr);
768 actualSize = partitionAllocGetSize(ptr);
769 EXPECT_EQ(predictedSize, actualSize);
770 EXPECT_LT(requestedSize, actualSize);
771 partitionFreeGeneric(genericAllocator.root(), ptr);
772
773 // Too large allocation.
774 requestedSize = INT_MAX;
775 predictedSize =
776 partitionAllocActualSize(genericAllocator.root(), requestedSize);
777 EXPECT_EQ(requestedSize, predictedSize);
778
779 TestShutdown();
780 }
781
782 // Test the realloc() contract.
783 TEST(PartitionAllocTest, Realloc) {
784 TestSetup();
785
786 // realloc(0, size) should be equivalent to malloc().
787 void* ptr = partitionReallocGeneric(genericAllocator.root(), 0,
788 kTestAllocSize, typeName);
789 memset(ptr, 'A', kTestAllocSize);
790 PartitionPage* page =
791 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
792 // realloc(ptr, 0) should be equivalent to free().
793 void* ptr2 =
794 partitionReallocGeneric(genericAllocator.root(), ptr, 0, typeName);
795 EXPECT_EQ(0, ptr2);
796 EXPECT_EQ(partitionCookieFreePointerAdjust(ptr), page->freelistHead);
797
798 // Test that growing an allocation with realloc() copies everything from the
799 // old allocation.
800 size_t size = kSystemPageSize - kExtraAllocSize;
801 EXPECT_EQ(size, partitionAllocActualSize(genericAllocator.root(), size));
802 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
803 memset(ptr, 'A', size);
804 ptr2 =
805 partitionReallocGeneric(genericAllocator.root(), ptr, size + 1, typeName);
806 EXPECT_NE(ptr, ptr2);
807 char* charPtr2 = static_cast<char*>(ptr2);
808 EXPECT_EQ('A', charPtr2[0]);
809 EXPECT_EQ('A', charPtr2[size - 1]);
810 #if ENABLE(ASSERT)
811 EXPECT_EQ(kUninitializedByte, static_cast<unsigned char>(charPtr2[size]));
812 #endif
813
814 // Test that shrinking an allocation with realloc() also copies everything
815 // from the old allocation.
816 ptr = partitionReallocGeneric(genericAllocator.root(), ptr2, size - 1,
817 typeName);
818 EXPECT_NE(ptr2, ptr);
819 char* charPtr = static_cast<char*>(ptr);
820 EXPECT_EQ('A', charPtr[0]);
821 EXPECT_EQ('A', charPtr[size - 2]);
822 #if ENABLE(ASSERT)
823 EXPECT_EQ(kUninitializedByte, static_cast<unsigned char>(charPtr[size - 1]));
824 #endif
825
826 partitionFreeGeneric(genericAllocator.root(), ptr);
827
828 // Test that shrinking a direct mapped allocation happens in-place.
829 size = kGenericMaxBucketed + 16 * kSystemPageSize;
830 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
831 size_t actualSize = partitionAllocGetSize(ptr);
832 ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr,
833 kGenericMaxBucketed + 8 * kSystemPageSize,
834 typeName);
835 EXPECT_EQ(ptr, ptr2);
836 EXPECT_EQ(actualSize - 8 * kSystemPageSize, partitionAllocGetSize(ptr2));
837
838 // Test that a previously in-place shrunk direct mapped allocation can be
839 // expanded up again within its original size.
840 ptr = partitionReallocGeneric(genericAllocator.root(), ptr2,
841 size - kSystemPageSize, typeName);
842 EXPECT_EQ(ptr2, ptr);
843 EXPECT_EQ(actualSize - kSystemPageSize, partitionAllocGetSize(ptr));
844
845 // Test that a direct mapped allocation is performed not in-place when the
846 // new size is small enough.
847 ptr2 = partitionReallocGeneric(genericAllocator.root(), ptr, kSystemPageSize,
848 typeName);
849 EXPECT_NE(ptr, ptr2);
850
851 partitionFreeGeneric(genericAllocator.root(), ptr2);
852
853 TestShutdown();
854 }
855
856 // Tests the handing out of freelists for partial pages.
857 TEST(PartitionAllocTest, PartialPageFreelists) {
858 TestSetup();
859
860 size_t bigSize = allocator.root()->maxAllocation - kExtraAllocSize;
861 EXPECT_EQ(kSystemPageSize - kAllocationGranularity,
862 bigSize + kExtraAllocSize);
863 size_t bucketIdx = (bigSize + kExtraAllocSize) >> kBucketShift;
864 PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx];
865 EXPECT_EQ(0, bucket->emptyPagesHead);
866
867 void* ptr = partitionAlloc(allocator.root(), bigSize, typeName);
868 EXPECT_TRUE(ptr);
869
870 PartitionPage* page =
871 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
872 size_t totalSlots =
873 (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) /
874 (bigSize + kExtraAllocSize);
875 EXPECT_EQ(4u, totalSlots);
876 // The freelist should have one entry, because we were able to exactly fit
877 // one object slot and one freelist pointer (the null that the head points
878 // to) into a system page.
879 EXPECT_TRUE(page->freelistHead);
880 EXPECT_EQ(1, page->numAllocatedSlots);
881 EXPECT_EQ(2, page->numUnprovisionedSlots);
882
883 void* ptr2 = partitionAlloc(allocator.root(), bigSize, typeName);
884 EXPECT_TRUE(ptr2);
885 EXPECT_FALSE(page->freelistHead);
886 EXPECT_EQ(2, page->numAllocatedSlots);
887 EXPECT_EQ(2, page->numUnprovisionedSlots);
888
889 void* ptr3 = partitionAlloc(allocator.root(), bigSize, typeName);
890 EXPECT_TRUE(ptr3);
891 EXPECT_TRUE(page->freelistHead);
892 EXPECT_EQ(3, page->numAllocatedSlots);
893 EXPECT_EQ(0, page->numUnprovisionedSlots);
894
895 void* ptr4 = partitionAlloc(allocator.root(), bigSize, typeName);
896 EXPECT_TRUE(ptr4);
897 EXPECT_FALSE(page->freelistHead);
898 EXPECT_EQ(4, page->numAllocatedSlots);
899 EXPECT_EQ(0, page->numUnprovisionedSlots);
900
901 void* ptr5 = partitionAlloc(allocator.root(), bigSize, typeName);
902 EXPECT_TRUE(ptr5);
903
904 PartitionPage* page2 =
905 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr5));
906 EXPECT_EQ(1, page2->numAllocatedSlots);
907
908 // Churn things a little whilst there's a partial page freelist.
909 partitionFree(ptr);
910 ptr = partitionAlloc(allocator.root(), bigSize, typeName);
911 void* ptr6 = partitionAlloc(allocator.root(), bigSize, typeName);
912
913 partitionFree(ptr);
914 partitionFree(ptr2);
915 partitionFree(ptr3);
916 partitionFree(ptr4);
917 partitionFree(ptr5);
918 partitionFree(ptr6);
919 EXPECT_NE(-1, page->emptyCacheIndex);
920 EXPECT_NE(-1, page2->emptyCacheIndex);
921 EXPECT_TRUE(page2->freelistHead);
922 EXPECT_EQ(0, page2->numAllocatedSlots);
923
924 // And test a couple of sizes that do not cross kSystemPageSize with a single
925 // allocation.
926 size_t mediumSize = (kSystemPageSize / 2) - kExtraAllocSize;
927 bucketIdx = (mediumSize + kExtraAllocSize) >> kBucketShift;
928 bucket = &allocator.root()->buckets()[bucketIdx];
929 EXPECT_EQ(0, bucket->emptyPagesHead);
930
931 ptr = partitionAlloc(allocator.root(), mediumSize, typeName);
932 EXPECT_TRUE(ptr);
933 page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
934 EXPECT_EQ(1, page->numAllocatedSlots);
935 totalSlots = (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) /
936 (mediumSize + kExtraAllocSize);
937 size_t firstPageSlots = kSystemPageSize / (mediumSize + kExtraAllocSize);
938 EXPECT_EQ(2u, firstPageSlots);
939 EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots);
940
941 partitionFree(ptr);
942
943 size_t smallSize = (kSystemPageSize / 4) - kExtraAllocSize;
944 bucketIdx = (smallSize + kExtraAllocSize) >> kBucketShift;
945 bucket = &allocator.root()->buckets()[bucketIdx];
946 EXPECT_EQ(0, bucket->emptyPagesHead);
947
948 ptr = partitionAlloc(allocator.root(), smallSize, typeName);
949 EXPECT_TRUE(ptr);
950 page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
951 EXPECT_EQ(1, page->numAllocatedSlots);
952 totalSlots = (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) /
953 (smallSize + kExtraAllocSize);
954 firstPageSlots = kSystemPageSize / (smallSize + kExtraAllocSize);
955 EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots);
956
957 partitionFree(ptr);
958 EXPECT_TRUE(page->freelistHead);
959 EXPECT_EQ(0, page->numAllocatedSlots);
960
961 size_t verySmallSize = 32 - kExtraAllocSize;
962 bucketIdx = (verySmallSize + kExtraAllocSize) >> kBucketShift;
963 bucket = &allocator.root()->buckets()[bucketIdx];
964 EXPECT_EQ(0, bucket->emptyPagesHead);
965
966 ptr = partitionAlloc(allocator.root(), verySmallSize, typeName);
967 EXPECT_TRUE(ptr);
968 page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
969 EXPECT_EQ(1, page->numAllocatedSlots);
970 totalSlots = (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) /
971 (verySmallSize + kExtraAllocSize);
972 firstPageSlots = kSystemPageSize / (verySmallSize + kExtraAllocSize);
973 EXPECT_EQ(totalSlots - firstPageSlots, page->numUnprovisionedSlots);
974
975 partitionFree(ptr);
976 EXPECT_TRUE(page->freelistHead);
977 EXPECT_EQ(0, page->numAllocatedSlots);
978
979 // And try an allocation size (against the generic allocator) that is
980 // larger than a system page.
981 size_t pageAndAHalfSize =
982 (kSystemPageSize + (kSystemPageSize / 2)) - kExtraAllocSize;
983 ptr = partitionAllocGeneric(genericAllocator.root(), pageAndAHalfSize,
984 typeName);
985 EXPECT_TRUE(ptr);
986 page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
987 EXPECT_EQ(1, page->numAllocatedSlots);
988 EXPECT_TRUE(page->freelistHead);
989 totalSlots = (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) /
990 (pageAndAHalfSize + kExtraAllocSize);
991 EXPECT_EQ(totalSlots - 2, page->numUnprovisionedSlots);
992 partitionFreeGeneric(genericAllocator.root(), ptr);
993
994 // And then make sure than exactly the page size only faults one page.
995 size_t pageSize = kSystemPageSize - kExtraAllocSize;
996 ptr = partitionAllocGeneric(genericAllocator.root(), pageSize, typeName);
997 EXPECT_TRUE(ptr);
998 page = partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
999 EXPECT_EQ(1, page->numAllocatedSlots);
1000 EXPECT_FALSE(page->freelistHead);
1001 totalSlots = (page->bucket->numSystemPagesPerSlotSpan * kSystemPageSize) /
1002 (pageSize + kExtraAllocSize);
1003 EXPECT_EQ(totalSlots - 1, page->numUnprovisionedSlots);
1004 partitionFreeGeneric(genericAllocator.root(), ptr);
1005
1006 TestShutdown();
1007 }
1008
1009 // Test some of the fragmentation-resistant properties of the allocator.
1010 TEST(PartitionAllocTest, PageRefilling) {
1011 TestSetup();
1012 PartitionBucket* bucket = &allocator.root()->buckets()[kTestBucketIndex];
1013
1014 // Grab two full pages and a non-full page.
1015 PartitionPage* page1 = GetFullPage(kTestAllocSize);
1016 PartitionPage* page2 = GetFullPage(kTestAllocSize);
1017 void* ptr = partitionAlloc(allocator.root(), kTestAllocSize, typeName);
1018 EXPECT_TRUE(ptr);
1019 EXPECT_NE(page1, bucket->activePagesHead);
1020 EXPECT_NE(page2, bucket->activePagesHead);
1021 PartitionPage* page =
1022 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
1023 EXPECT_EQ(1, page->numAllocatedSlots);
1024
1025 // Work out a pointer into page2 and free it; and then page1 and free it.
1026 char* ptr2 =
1027 reinterpret_cast<char*>(partitionPageToPointer(page1)) + kPointerOffset;
1028 partitionFree(ptr2);
1029 ptr2 =
1030 reinterpret_cast<char*>(partitionPageToPointer(page2)) + kPointerOffset;
1031 partitionFree(ptr2);
1032
1033 // If we perform two allocations from the same bucket now, we expect to
1034 // refill both the nearly full pages.
1035 (void)partitionAlloc(allocator.root(), kTestAllocSize, typeName);
1036 (void)partitionAlloc(allocator.root(), kTestAllocSize, typeName);
1037 EXPECT_EQ(1, page->numAllocatedSlots);
1038
1039 FreeFullPage(page2);
1040 FreeFullPage(page1);
1041 partitionFree(ptr);
1042
1043 TestShutdown();
1044 }
1045
1046 // Basic tests to ensure that allocations work for partial page buckets.
1047 TEST(PartitionAllocTest, PartialPages) {
1048 TestSetup();
1049
1050 // Find a size that is backed by a partial partition page.
1051 size_t size = sizeof(void*);
1052 PartitionBucket* bucket = 0;
1053 while (size < kTestMaxAllocation) {
1054 bucket = &allocator.root()->buckets()[size >> kBucketShift];
1055 if (bucket->numSystemPagesPerSlotSpan % kNumSystemPagesPerPartitionPage)
1056 break;
1057 size += sizeof(void*);
1058 }
1059 EXPECT_LT(size, kTestMaxAllocation);
1060
1061 PartitionPage* page1 = GetFullPage(size);
1062 PartitionPage* page2 = GetFullPage(size);
1063 FreeFullPage(page2);
1064 FreeFullPage(page1);
1065
1066 TestShutdown();
1067 }
1068
1069 // Test correct handling if our mapping collides with another.
1070 TEST(PartitionAllocTest, MappingCollision) {
1071 TestSetup();
1072 // The -2 is because the first and last partition pages in a super page are
1073 // guard pages.
1074 size_t numPartitionPagesNeeded = kNumPartitionPagesPerSuperPage - 2;
1075 std::unique_ptr<PartitionPage* []> firstSuperPagePages =
1076 wrapArrayUnique(new PartitionPage*[numPartitionPagesNeeded]);
1077 std::unique_ptr<PartitionPage* []> secondSuperPagePages =
1078 wrapArrayUnique(new PartitionPage*[numPartitionPagesNeeded]);
1079
1080 size_t i;
1081 for (i = 0; i < numPartitionPagesNeeded; ++i)
1082 firstSuperPagePages[i] = GetFullPage(kTestAllocSize);
1083
1084 char* pageBase =
1085 reinterpret_cast<char*>(partitionPageToPointer(firstSuperPagePages[0]));
1086 EXPECT_EQ(kPartitionPageSize,
1087 reinterpret_cast<uintptr_t>(pageBase) & kSuperPageOffsetMask);
1088 pageBase -= kPartitionPageSize;
1089 // Map a single system page either side of the mapping for our allocations,
1090 // with the goal of tripping up alignment of the next mapping.
1091 void* map1 = allocPages(pageBase - kPageAllocationGranularity,
1092 kPageAllocationGranularity,
1093 kPageAllocationGranularity, PageInaccessible);
1094 EXPECT_TRUE(map1);
1095 void* map2 = allocPages(pageBase + kSuperPageSize, kPageAllocationGranularity,
1096 kPageAllocationGranularity, PageInaccessible);
1097 EXPECT_TRUE(map2);
1098
1099 for (i = 0; i < numPartitionPagesNeeded; ++i)
1100 secondSuperPagePages[i] = GetFullPage(kTestAllocSize);
1101
1102 freePages(map1, kPageAllocationGranularity);
1103 freePages(map2, kPageAllocationGranularity);
1104
1105 pageBase =
1106 reinterpret_cast<char*>(partitionPageToPointer(secondSuperPagePages[0]));
1107 EXPECT_EQ(kPartitionPageSize,
1108 reinterpret_cast<uintptr_t>(pageBase) & kSuperPageOffsetMask);
1109 pageBase -= kPartitionPageSize;
1110 // Map a single system page either side of the mapping for our allocations,
1111 // with the goal of tripping up alignment of the next mapping.
1112 map1 = allocPages(pageBase - kPageAllocationGranularity,
1113 kPageAllocationGranularity, kPageAllocationGranularity,
1114 PageAccessible);
1115 EXPECT_TRUE(map1);
1116 map2 = allocPages(pageBase + kSuperPageSize, kPageAllocationGranularity,
1117 kPageAllocationGranularity, PageAccessible);
1118 EXPECT_TRUE(map2);
1119 setSystemPagesInaccessible(map1, kPageAllocationGranularity);
1120 setSystemPagesInaccessible(map2, kPageAllocationGranularity);
1121
1122 PartitionPage* pageInThirdSuperPage = GetFullPage(kTestAllocSize);
1123 freePages(map1, kPageAllocationGranularity);
1124 freePages(map2, kPageAllocationGranularity);
1125
1126 EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(
1127 partitionPageToPointer(pageInThirdSuperPage)) &
1128 kPartitionPageOffsetMask);
1129
1130 // And make sure we really did get a page in a new superpage.
1131 EXPECT_NE(reinterpret_cast<uintptr_t>(
1132 partitionPageToPointer(firstSuperPagePages[0])) &
1133 kSuperPageBaseMask,
1134 reinterpret_cast<uintptr_t>(
1135 partitionPageToPointer(pageInThirdSuperPage)) &
1136 kSuperPageBaseMask);
1137 EXPECT_NE(reinterpret_cast<uintptr_t>(
1138 partitionPageToPointer(secondSuperPagePages[0])) &
1139 kSuperPageBaseMask,
1140 reinterpret_cast<uintptr_t>(
1141 partitionPageToPointer(pageInThirdSuperPage)) &
1142 kSuperPageBaseMask);
1143
1144 FreeFullPage(pageInThirdSuperPage);
1145 for (i = 0; i < numPartitionPagesNeeded; ++i) {
1146 FreeFullPage(firstSuperPagePages[i]);
1147 FreeFullPage(secondSuperPagePages[i]);
1148 }
1149
1150 TestShutdown();
1151 }
1152
1153 // Tests that pages in the free page cache do get freed as appropriate.
1154 TEST(PartitionAllocTest, FreeCache) {
1155 TestSetup();
1156
1157 EXPECT_EQ(0U, allocator.root()->totalSizeOfCommittedPages);
1158
1159 size_t bigSize = allocator.root()->maxAllocation - kExtraAllocSize;
1160 size_t bucketIdx = (bigSize + kExtraAllocSize) >> kBucketShift;
1161 PartitionBucket* bucket = &allocator.root()->buckets()[bucketIdx];
1162
1163 void* ptr = partitionAlloc(allocator.root(), bigSize, typeName);
1164 EXPECT_TRUE(ptr);
1165 PartitionPage* page =
1166 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
1167 EXPECT_EQ(0, bucket->emptyPagesHead);
1168 EXPECT_EQ(1, page->numAllocatedSlots);
1169 EXPECT_EQ(kPartitionPageSize, allocator.root()->totalSizeOfCommittedPages);
1170 partitionFree(ptr);
1171 EXPECT_EQ(0, page->numAllocatedSlots);
1172 EXPECT_NE(-1, page->emptyCacheIndex);
1173 EXPECT_TRUE(page->freelistHead);
1174
1175 CycleFreeCache(kTestAllocSize);
1176
1177 // Flushing the cache should have really freed the unused page.
1178 EXPECT_FALSE(page->freelistHead);
1179 EXPECT_EQ(-1, page->emptyCacheIndex);
1180 EXPECT_EQ(0, page->numAllocatedSlots);
1181 PartitionBucket* cycleFreeCacheBucket =
1182 &allocator.root()->buckets()[kTestBucketIndex];
1183 EXPECT_EQ(cycleFreeCacheBucket->numSystemPagesPerSlotSpan * kSystemPageSize,
1184 allocator.root()->totalSizeOfCommittedPages);
1185
1186 // Check that an allocation works ok whilst in this state (a free'd page
1187 // as the active pages head).
1188 ptr = partitionAlloc(allocator.root(), bigSize, typeName);
1189 EXPECT_FALSE(bucket->emptyPagesHead);
1190 partitionFree(ptr);
1191
1192 // Also check that a page that is bouncing immediately between empty and
1193 // used does not get freed.
1194 for (size_t i = 0; i < kMaxFreeableSpans * 2; ++i) {
1195 ptr = partitionAlloc(allocator.root(), bigSize, typeName);
1196 EXPECT_TRUE(page->freelistHead);
1197 partitionFree(ptr);
1198 EXPECT_TRUE(page->freelistHead);
1199 }
1200 EXPECT_EQ(kPartitionPageSize, allocator.root()->totalSizeOfCommittedPages);
1201 TestShutdown();
1202 }
1203
1204 // Tests for a bug we had with losing references to free pages.
1205 TEST(PartitionAllocTest, LostFreePagesBug) {
1206 TestSetup();
1207
1208 size_t size = kPartitionPageSize - kExtraAllocSize;
1209
1210 void* ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1211 EXPECT_TRUE(ptr);
1212 void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1213 EXPECT_TRUE(ptr2);
1214
1215 PartitionPage* page =
1216 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr));
1217 PartitionPage* page2 =
1218 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr2));
1219 PartitionBucket* bucket = page->bucket;
1220
1221 EXPECT_EQ(0, bucket->emptyPagesHead);
1222 EXPECT_EQ(-1, page->numAllocatedSlots);
1223 EXPECT_EQ(1, page2->numAllocatedSlots);
1224
1225 partitionFreeGeneric(genericAllocator.root(), ptr);
1226 partitionFreeGeneric(genericAllocator.root(), ptr2);
1227
1228 EXPECT_TRUE(bucket->emptyPagesHead);
1229 EXPECT_TRUE(bucket->emptyPagesHead->nextPage);
1230 EXPECT_EQ(0, page->numAllocatedSlots);
1231 EXPECT_EQ(0, page2->numAllocatedSlots);
1232 EXPECT_TRUE(page->freelistHead);
1233 EXPECT_TRUE(page2->freelistHead);
1234
1235 CycleGenericFreeCache(kTestAllocSize);
1236
1237 EXPECT_FALSE(page->freelistHead);
1238 EXPECT_FALSE(page2->freelistHead);
1239
1240 EXPECT_TRUE(bucket->emptyPagesHead);
1241 EXPECT_TRUE(bucket->emptyPagesHead->nextPage);
1242 EXPECT_EQ(&PartitionRootGeneric::gSeedPage, bucket->activePagesHead);
1243
1244 // At this moment, we have two decommitted pages, on the empty list.
1245 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1246 EXPECT_TRUE(ptr);
1247 partitionFreeGeneric(genericAllocator.root(), ptr);
1248
1249 EXPECT_EQ(&PartitionRootGeneric::gSeedPage, bucket->activePagesHead);
1250 EXPECT_TRUE(bucket->emptyPagesHead);
1251 EXPECT_TRUE(bucket->decommittedPagesHead);
1252
1253 CycleGenericFreeCache(kTestAllocSize);
1254
1255 // We're now set up to trigger a historical bug by scanning over the active
1256 // pages list. The current code gets into a different state, but we'll keep
1257 // the test as being an interesting corner case.
1258 ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1259 EXPECT_TRUE(ptr);
1260 partitionFreeGeneric(genericAllocator.root(), ptr);
1261
1262 EXPECT_TRUE(bucket->activePagesHead);
1263 EXPECT_TRUE(bucket->emptyPagesHead);
1264 EXPECT_TRUE(bucket->decommittedPagesHead);
1265
1266 TestShutdown();
1267 }
1268
1269 #if !CPU(64BIT) || OS(POSIX)
1270
1271 static void DoReturnNullTest(size_t allocSize) {
1272 TestSetup();
1273
1274 EXPECT_TRUE(SetAddressSpaceLimit());
1275
1276 // Work out the number of allocations for 6 GB of memory.
1277 const int numAllocations = (6 * 1024 * 1024) / (allocSize / 1024);
1278
1279 void** ptrs = reinterpret_cast<void**>(partitionAllocGeneric(
1280 genericAllocator.root(), numAllocations * sizeof(void*), typeName));
1281 int i;
1282
1283 for (i = 0; i < numAllocations; ++i) {
1284 ptrs[i] = partitionAllocGenericFlags(
1285 genericAllocator.root(), PartitionAllocReturnNull, allocSize, typeName);
1286 if (!i)
1287 EXPECT_TRUE(ptrs[0]);
1288 if (!ptrs[i]) {
1289 ptrs[i] = partitionAllocGenericFlags(genericAllocator.root(),
1290 PartitionAllocReturnNull, allocSize,
1291 typeName);
1292 EXPECT_FALSE(ptrs[i]);
1293 break;
1294 }
1295 }
1296
1297 // We shouldn't succeed in allocating all 6 GB of memory. If we do, then
1298 // we're not actually testing anything here.
1299 EXPECT_LT(i, numAllocations);
1300
1301 // Free, reallocate and free again each block we allocated. We do this to
1302 // check that freeing memory also works correctly after a failed allocation.
1303 for (--i; i >= 0; --i) {
1304 partitionFreeGeneric(genericAllocator.root(), ptrs[i]);
1305 ptrs[i] = partitionAllocGenericFlags(
1306 genericAllocator.root(), PartitionAllocReturnNull, allocSize, typeName);
1307 EXPECT_TRUE(ptrs[i]);
1308 partitionFreeGeneric(genericAllocator.root(), ptrs[i]);
1309 }
1310
1311 partitionFreeGeneric(genericAllocator.root(), ptrs);
1312
1313 EXPECT_TRUE(ClearAddressSpaceLimit());
1314
1315 TestShutdown();
1316 }
1317
1318 // Tests that if an allocation fails in "return null" mode, repeating it doesn't
1319 // crash, and still returns null. The test tries to allocate 6 GB of memory in
1320 // 512 kB blocks. On 64-bit POSIX systems, the address space is limited to 4 GB
1321 // using setrlimit() first.
1322 #if OS(MACOSX)
1323 #define MAYBE_RepeatedReturnNull DISABLED_RepeatedReturnNull
1324 #else
1325 #define MAYBE_RepeatedReturnNull RepeatedReturnNull
1326 #endif
1327 TEST(PartitionAllocTest, MAYBE_RepeatedReturnNull) {
1328 // A single-slot but non-direct-mapped allocation size.
1329 DoReturnNullTest(512 * 1024);
1330 }
1331
1332 // Another "return null" test but for larger, direct-mapped allocations.
1333 #if OS(MACOSX)
1334 #define MAYBE_RepeatedReturnNullDirect DISABLED_RepeatedReturnNullDirect
1335 #else
1336 #define MAYBE_RepeatedReturnNullDirect RepeatedReturnNullDirect
1337 #endif
1338 TEST(PartitionAllocTest, MAYBE_RepeatedReturnNullDirect) {
1339 // A direct-mapped allocation size.
1340 DoReturnNullTest(256 * 1024 * 1024);
1341 }
1342
1343 #endif // !CPU(64BIT) || OS(POSIX)
1344
1345 #if !OS(ANDROID)
1346
1347 // Make sure that malloc(-1) dies.
1348 // In the past, we had an integer overflow that would alias malloc(-1) to
1349 // malloc(0), which is not good.
1350 TEST(PartitionAllocDeathTest, LargeAllocs) {
1351 TestSetup();
1352 // Largest alloc.
1353 EXPECT_DEATH(partitionAllocGeneric(genericAllocator.root(),
1354 static_cast<size_t>(-1), typeName),
1355 "");
1356 // And the smallest allocation we expect to die.
1357 EXPECT_DEATH(
1358 partitionAllocGeneric(genericAllocator.root(),
1359 static_cast<size_t>(INT_MAX) + 1, typeName),
1360 "");
1361
1362 TestShutdown();
1363 }
1364
1365 // Check that our immediate double-free detection works.
1366 TEST(PartitionAllocDeathTest, ImmediateDoubleFree) {
1367 TestSetup();
1368
1369 void* ptr =
1370 partitionAllocGeneric(genericAllocator.root(), kTestAllocSize, typeName);
1371 EXPECT_TRUE(ptr);
1372 partitionFreeGeneric(genericAllocator.root(), ptr);
1373
1374 EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), ptr), "");
1375
1376 TestShutdown();
1377 }
1378
1379 // Check that our refcount-based double-free detection works.
1380 TEST(PartitionAllocDeathTest, RefcountDoubleFree) {
1381 TestSetup();
1382
1383 void* ptr =
1384 partitionAllocGeneric(genericAllocator.root(), kTestAllocSize, typeName);
1385 EXPECT_TRUE(ptr);
1386 void* ptr2 =
1387 partitionAllocGeneric(genericAllocator.root(), kTestAllocSize, typeName);
1388 EXPECT_TRUE(ptr2);
1389 partitionFreeGeneric(genericAllocator.root(), ptr);
1390 partitionFreeGeneric(genericAllocator.root(), ptr2);
1391 // This is not an immediate double-free so our immediate detection won't
1392 // fire. However, it does take the "refcount" of the partition page to -1,
1393 // which is illegal and should be trapped.
1394 EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), ptr), "");
1395
1396 TestShutdown();
1397 }
1398
1399 // Check that guard pages are present where expected.
1400 TEST(PartitionAllocDeathTest, GuardPages) {
1401 TestSetup();
1402
1403 // partitionAlloc adds kPartitionPageSize to the requested size
1404 // (for metadata), and then rounds that size to kPageAllocationGranularity.
1405 // To be able to reliably write one past a direct allocation, choose a size
1406 // that's
1407 // a) larger than kGenericMaxBucketed (to make the allocation direct)
1408 // b) aligned at kPageAllocationGranularity boundaries after
1409 // kPartitionPageSize has been added to it.
1410 // (On 32-bit, partitionAlloc adds another kSystemPageSize to the
1411 // allocation size before rounding, but there it marks the memory right
1412 // after size as inaccessible, so it's fine to write 1 past the size we
1413 // hand to partitionAlloc and we don't need to worry about allocation
1414 // granularities.)
1415 #define ALIGN(N, A) (((N) + (A)-1) / (A) * (A))
1416 const int kSize = ALIGN(kGenericMaxBucketed + 1 + kPartitionPageSize,
1417 kPageAllocationGranularity) -
1418 kPartitionPageSize;
1419 #undef ALIGN
1420 static_assert(kSize > kGenericMaxBucketed,
1421 "allocation not large enough for direct allocation");
1422 size_t size = kSize - kExtraAllocSize;
1423 void* ptr = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1424
1425 EXPECT_TRUE(ptr);
1426 char* charPtr = reinterpret_cast<char*>(ptr) - kPointerOffset;
1427
1428 EXPECT_DEATH(*(charPtr - 1) = 'A', "");
1429 EXPECT_DEATH(*(charPtr + size + kExtraAllocSize) = 'A', "");
1430
1431 partitionFreeGeneric(genericAllocator.root(), ptr);
1432
1433 TestShutdown();
1434 }
1435
1436 // Check that a bad free() is caught where the free() refers to an unused
1437 // partition page of a large allocation.
1438 TEST(PartitionAllocDeathTest, FreeWrongPartitionPage) {
1439 TestSetup();
1440
1441 // This large size will result in a direct mapped allocation with guard
1442 // pages at either end.
1443 void* ptr = partitionAllocGeneric(genericAllocator.root(),
1444 kPartitionPageSize * 2, typeName);
1445 EXPECT_TRUE(ptr);
1446 char* badPtr = reinterpret_cast<char*>(ptr) + kPartitionPageSize;
1447
1448 EXPECT_DEATH(partitionFreeGeneric(genericAllocator.root(), badPtr), "");
1449
1450 partitionFreeGeneric(genericAllocator.root(), ptr);
1451
1452 TestShutdown();
1453 }
1454
1455 #endif // !OS(ANDROID)
1456
1457 // Tests that partitionDumpStatsGeneric and partitionDumpStats runs without
1458 // crashing and returns non zero values when memory is allocated.
1459 TEST(PartitionAllocTest, DumpMemoryStats) {
1460 TestSetup();
1461 {
1462 void* ptr = partitionAlloc(allocator.root(), kTestAllocSize, typeName);
1463 MockPartitionStatsDumper mockStatsDumper;
1464 partitionDumpStats(allocator.root(), "mock_allocator",
1465 false /* detailed dump */, &mockStatsDumper);
1466 EXPECT_TRUE(mockStatsDumper.IsMemoryAllocationRecorded());
1467
1468 partitionFree(ptr);
1469 }
1470
1471 // This series of tests checks the active -> empty -> decommitted states.
1472 {
1473 void* genericPtr = partitionAllocGeneric(genericAllocator.root(),
1474 2048 - kExtraAllocSize, typeName);
1475 {
1476 MockPartitionStatsDumper mockStatsDumperGeneric;
1477 partitionDumpStatsGeneric(
1478 genericAllocator.root(), "mock_generic_allocator",
1479 false /* detailed dump */, &mockStatsDumperGeneric);
1480 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1481
1482 const PartitionBucketMemoryStats* stats =
1483 mockStatsDumperGeneric.GetBucketStats(2048);
1484 EXPECT_TRUE(stats);
1485 EXPECT_TRUE(stats->isValid);
1486 EXPECT_EQ(2048u, stats->bucketSlotSize);
1487 EXPECT_EQ(2048u, stats->activeBytes);
1488 EXPECT_EQ(kSystemPageSize, stats->residentBytes);
1489 EXPECT_EQ(0u, stats->decommittableBytes);
1490 EXPECT_EQ(0u, stats->discardableBytes);
1491 EXPECT_EQ(0u, stats->numFullPages);
1492 EXPECT_EQ(1u, stats->numActivePages);
1493 EXPECT_EQ(0u, stats->numEmptyPages);
1494 EXPECT_EQ(0u, stats->numDecommittedPages);
1495 }
1496
1497 partitionFreeGeneric(genericAllocator.root(), genericPtr);
1498
1499 {
1500 MockPartitionStatsDumper mockStatsDumperGeneric;
1501 partitionDumpStatsGeneric(
1502 genericAllocator.root(), "mock_generic_allocator",
1503 false /* detailed dump */, &mockStatsDumperGeneric);
1504 EXPECT_FALSE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1505
1506 const PartitionBucketMemoryStats* stats =
1507 mockStatsDumperGeneric.GetBucketStats(2048);
1508 EXPECT_TRUE(stats);
1509 EXPECT_TRUE(stats->isValid);
1510 EXPECT_EQ(2048u, stats->bucketSlotSize);
1511 EXPECT_EQ(0u, stats->activeBytes);
1512 EXPECT_EQ(kSystemPageSize, stats->residentBytes);
1513 EXPECT_EQ(kSystemPageSize, stats->decommittableBytes);
1514 EXPECT_EQ(0u, stats->discardableBytes);
1515 EXPECT_EQ(0u, stats->numFullPages);
1516 EXPECT_EQ(0u, stats->numActivePages);
1517 EXPECT_EQ(1u, stats->numEmptyPages);
1518 EXPECT_EQ(0u, stats->numDecommittedPages);
1519 }
1520
1521 CycleGenericFreeCache(kTestAllocSize);
1522
1523 {
1524 MockPartitionStatsDumper mockStatsDumperGeneric;
1525 partitionDumpStatsGeneric(
1526 genericAllocator.root(), "mock_generic_allocator",
1527 false /* detailed dump */, &mockStatsDumperGeneric);
1528 EXPECT_FALSE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1529
1530 const PartitionBucketMemoryStats* stats =
1531 mockStatsDumperGeneric.GetBucketStats(2048);
1532 EXPECT_TRUE(stats);
1533 EXPECT_TRUE(stats->isValid);
1534 EXPECT_EQ(2048u, stats->bucketSlotSize);
1535 EXPECT_EQ(0u, stats->activeBytes);
1536 EXPECT_EQ(0u, stats->residentBytes);
1537 EXPECT_EQ(0u, stats->decommittableBytes);
1538 EXPECT_EQ(0u, stats->discardableBytes);
1539 EXPECT_EQ(0u, stats->numFullPages);
1540 EXPECT_EQ(0u, stats->numActivePages);
1541 EXPECT_EQ(0u, stats->numEmptyPages);
1542 EXPECT_EQ(1u, stats->numDecommittedPages);
1543 }
1544 }
1545
1546 // This test checks for correct empty page list accounting.
1547 {
1548 size_t size = kPartitionPageSize - kExtraAllocSize;
1549 void* ptr1 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1550 void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1551 partitionFreeGeneric(genericAllocator.root(), ptr1);
1552 partitionFreeGeneric(genericAllocator.root(), ptr2);
1553
1554 CycleGenericFreeCache(kTestAllocSize);
1555
1556 ptr1 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1557
1558 {
1559 MockPartitionStatsDumper mockStatsDumperGeneric;
1560 partitionDumpStatsGeneric(
1561 genericAllocator.root(), "mock_generic_allocator",
1562 false /* detailed dump */, &mockStatsDumperGeneric);
1563 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1564
1565 const PartitionBucketMemoryStats* stats =
1566 mockStatsDumperGeneric.GetBucketStats(kPartitionPageSize);
1567 EXPECT_TRUE(stats);
1568 EXPECT_TRUE(stats->isValid);
1569 EXPECT_EQ(kPartitionPageSize, stats->bucketSlotSize);
1570 EXPECT_EQ(kPartitionPageSize, stats->activeBytes);
1571 EXPECT_EQ(kPartitionPageSize, stats->residentBytes);
1572 EXPECT_EQ(0u, stats->decommittableBytes);
1573 EXPECT_EQ(0u, stats->discardableBytes);
1574 EXPECT_EQ(1u, stats->numFullPages);
1575 EXPECT_EQ(0u, stats->numActivePages);
1576 EXPECT_EQ(0u, stats->numEmptyPages);
1577 EXPECT_EQ(1u, stats->numDecommittedPages);
1578 }
1579 partitionFreeGeneric(genericAllocator.root(), ptr1);
1580 }
1581
1582 // This test checks for correct direct mapped accounting.
1583 {
1584 size_t sizeSmaller = kGenericMaxBucketed + 1;
1585 size_t sizeBigger = (kGenericMaxBucketed * 2) + 1;
1586 size_t realSizeSmaller =
1587 (sizeSmaller + kSystemPageOffsetMask) & kSystemPageBaseMask;
1588 size_t realSizeBigger =
1589 (sizeBigger + kSystemPageOffsetMask) & kSystemPageBaseMask;
1590 void* ptr =
1591 partitionAllocGeneric(genericAllocator.root(), sizeSmaller, typeName);
1592 void* ptr2 =
1593 partitionAllocGeneric(genericAllocator.root(), sizeBigger, typeName);
1594
1595 {
1596 MockPartitionStatsDumper mockStatsDumperGeneric;
1597 partitionDumpStatsGeneric(
1598 genericAllocator.root(), "mock_generic_allocator",
1599 false /* detailed dump */, &mockStatsDumperGeneric);
1600 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1601
1602 const PartitionBucketMemoryStats* stats =
1603 mockStatsDumperGeneric.GetBucketStats(realSizeSmaller);
1604 EXPECT_TRUE(stats);
1605 EXPECT_TRUE(stats->isValid);
1606 EXPECT_TRUE(stats->isDirectMap);
1607 EXPECT_EQ(realSizeSmaller, stats->bucketSlotSize);
1608 EXPECT_EQ(realSizeSmaller, stats->activeBytes);
1609 EXPECT_EQ(realSizeSmaller, stats->residentBytes);
1610 EXPECT_EQ(0u, stats->decommittableBytes);
1611 EXPECT_EQ(0u, stats->discardableBytes);
1612 EXPECT_EQ(1u, stats->numFullPages);
1613 EXPECT_EQ(0u, stats->numActivePages);
1614 EXPECT_EQ(0u, stats->numEmptyPages);
1615 EXPECT_EQ(0u, stats->numDecommittedPages);
1616
1617 stats = mockStatsDumperGeneric.GetBucketStats(realSizeBigger);
1618 EXPECT_TRUE(stats);
1619 EXPECT_TRUE(stats->isValid);
1620 EXPECT_TRUE(stats->isDirectMap);
1621 EXPECT_EQ(realSizeBigger, stats->bucketSlotSize);
1622 EXPECT_EQ(realSizeBigger, stats->activeBytes);
1623 EXPECT_EQ(realSizeBigger, stats->residentBytes);
1624 EXPECT_EQ(0u, stats->decommittableBytes);
1625 EXPECT_EQ(0u, stats->discardableBytes);
1626 EXPECT_EQ(1u, stats->numFullPages);
1627 EXPECT_EQ(0u, stats->numActivePages);
1628 EXPECT_EQ(0u, stats->numEmptyPages);
1629 EXPECT_EQ(0u, stats->numDecommittedPages);
1630 }
1631
1632 partitionFreeGeneric(genericAllocator.root(), ptr2);
1633 partitionFreeGeneric(genericAllocator.root(), ptr);
1634
1635 // Whilst we're here, allocate again and free with different ordering
1636 // to give a workout to our linked list code.
1637 ptr = partitionAllocGeneric(genericAllocator.root(), sizeSmaller, typeName);
1638 ptr2 = partitionAllocGeneric(genericAllocator.root(), sizeBigger, typeName);
1639 partitionFreeGeneric(genericAllocator.root(), ptr);
1640 partitionFreeGeneric(genericAllocator.root(), ptr2);
1641 }
1642
1643 // This test checks large-but-not-quite-direct allocations.
1644 {
1645 void* ptr =
1646 partitionAllocGeneric(genericAllocator.root(), 65536 + 1, typeName);
1647
1648 {
1649 MockPartitionStatsDumper mockStatsDumperGeneric;
1650 partitionDumpStatsGeneric(
1651 genericAllocator.root(), "mock_generic_allocator",
1652 false /* detailed dump */, &mockStatsDumperGeneric);
1653 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1654
1655 size_t slotSize = 65536 + (65536 / kGenericNumBucketsPerOrder);
1656 const PartitionBucketMemoryStats* stats =
1657 mockStatsDumperGeneric.GetBucketStats(slotSize);
1658 EXPECT_TRUE(stats);
1659 EXPECT_TRUE(stats->isValid);
1660 EXPECT_FALSE(stats->isDirectMap);
1661 EXPECT_EQ(slotSize, stats->bucketSlotSize);
1662 EXPECT_EQ(65536u + 1 + kExtraAllocSize, stats->activeBytes);
1663 EXPECT_EQ(slotSize, stats->residentBytes);
1664 EXPECT_EQ(0u, stats->decommittableBytes);
1665 EXPECT_EQ(kSystemPageSize, stats->discardableBytes);
1666 EXPECT_EQ(1u, stats->numFullPages);
1667 EXPECT_EQ(0u, stats->numActivePages);
1668 EXPECT_EQ(0u, stats->numEmptyPages);
1669 EXPECT_EQ(0u, stats->numDecommittedPages);
1670 }
1671
1672 partitionFreeGeneric(genericAllocator.root(), ptr);
1673
1674 {
1675 MockPartitionStatsDumper mockStatsDumperGeneric;
1676 partitionDumpStatsGeneric(
1677 genericAllocator.root(), "mock_generic_allocator",
1678 false /* detailed dump */, &mockStatsDumperGeneric);
1679 EXPECT_FALSE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1680
1681 size_t slotSize = 65536 + (65536 / kGenericNumBucketsPerOrder);
1682 const PartitionBucketMemoryStats* stats =
1683 mockStatsDumperGeneric.GetBucketStats(slotSize);
1684 EXPECT_TRUE(stats);
1685 EXPECT_TRUE(stats->isValid);
1686 EXPECT_FALSE(stats->isDirectMap);
1687 EXPECT_EQ(slotSize, stats->bucketSlotSize);
1688 EXPECT_EQ(0u, stats->activeBytes);
1689 EXPECT_EQ(slotSize, stats->residentBytes);
1690 EXPECT_EQ(slotSize, stats->decommittableBytes);
1691 EXPECT_EQ(0u, stats->numFullPages);
1692 EXPECT_EQ(0u, stats->numActivePages);
1693 EXPECT_EQ(1u, stats->numEmptyPages);
1694 EXPECT_EQ(0u, stats->numDecommittedPages);
1695 }
1696
1697 void* ptr2 = partitionAllocGeneric(genericAllocator.root(),
1698 65536 + kSystemPageSize + 1, typeName);
1699 EXPECT_EQ(ptr, ptr2);
1700
1701 {
1702 MockPartitionStatsDumper mockStatsDumperGeneric;
1703 partitionDumpStatsGeneric(
1704 genericAllocator.root(), "mock_generic_allocator",
1705 false /* detailed dump */, &mockStatsDumperGeneric);
1706 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1707
1708 size_t slotSize = 65536 + (65536 / kGenericNumBucketsPerOrder);
1709 const PartitionBucketMemoryStats* stats =
1710 mockStatsDumperGeneric.GetBucketStats(slotSize);
1711 EXPECT_TRUE(stats);
1712 EXPECT_TRUE(stats->isValid);
1713 EXPECT_FALSE(stats->isDirectMap);
1714 EXPECT_EQ(slotSize, stats->bucketSlotSize);
1715 EXPECT_EQ(65536u + kSystemPageSize + 1 + kExtraAllocSize,
1716 stats->activeBytes);
1717 EXPECT_EQ(slotSize, stats->residentBytes);
1718 EXPECT_EQ(0u, stats->decommittableBytes);
1719 EXPECT_EQ(0u, stats->discardableBytes);
1720 EXPECT_EQ(1u, stats->numFullPages);
1721 EXPECT_EQ(0u, stats->numActivePages);
1722 EXPECT_EQ(0u, stats->numEmptyPages);
1723 EXPECT_EQ(0u, stats->numDecommittedPages);
1724 }
1725
1726 partitionFreeGeneric(genericAllocator.root(), ptr2);
1727 }
1728
1729 TestShutdown();
1730 }
1731
1732 // Tests the API to purge freeable memory.
1733 TEST(PartitionAllocTest, Purge) {
1734 TestSetup();
1735
1736 char* ptr = reinterpret_cast<char*>(partitionAllocGeneric(
1737 genericAllocator.root(), 2048 - kExtraAllocSize, typeName));
1738 partitionFreeGeneric(genericAllocator.root(), ptr);
1739 {
1740 MockPartitionStatsDumper mockStatsDumperGeneric;
1741 partitionDumpStatsGeneric(genericAllocator.root(), "mock_generic_allocator",
1742 false /* detailed dump */,
1743 &mockStatsDumperGeneric);
1744 EXPECT_FALSE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1745
1746 const PartitionBucketMemoryStats* stats =
1747 mockStatsDumperGeneric.GetBucketStats(2048);
1748 EXPECT_TRUE(stats);
1749 EXPECT_TRUE(stats->isValid);
1750 EXPECT_EQ(kSystemPageSize, stats->decommittableBytes);
1751 EXPECT_EQ(kSystemPageSize, stats->residentBytes);
1752 }
1753 partitionPurgeMemoryGeneric(genericAllocator.root(),
1754 PartitionPurgeDecommitEmptyPages);
1755 {
1756 MockPartitionStatsDumper mockStatsDumperGeneric;
1757 partitionDumpStatsGeneric(genericAllocator.root(), "mock_generic_allocator",
1758 false /* detailed dump */,
1759 &mockStatsDumperGeneric);
1760 EXPECT_FALSE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1761
1762 const PartitionBucketMemoryStats* stats =
1763 mockStatsDumperGeneric.GetBucketStats(2048);
1764 EXPECT_TRUE(stats);
1765 EXPECT_TRUE(stats->isValid);
1766 EXPECT_EQ(0u, stats->decommittableBytes);
1767 EXPECT_EQ(0u, stats->residentBytes);
1768 }
1769 // Calling purge again here is a good way of testing we didn't mess up the
1770 // state of the free cache ring.
1771 partitionPurgeMemoryGeneric(genericAllocator.root(),
1772 PartitionPurgeDecommitEmptyPages);
1773
1774 char* bigPtr = reinterpret_cast<char*>(
1775 partitionAllocGeneric(genericAllocator.root(), 256 * 1024, typeName));
1776 partitionFreeGeneric(genericAllocator.root(), bigPtr);
1777 partitionPurgeMemoryGeneric(genericAllocator.root(),
1778 PartitionPurgeDecommitEmptyPages);
1779
1780 CheckPageInCore(ptr - kPointerOffset, false);
1781 CheckPageInCore(bigPtr - kPointerOffset, false);
1782
1783 TestShutdown();
1784 }
1785
1786 // Tests that we prefer to allocate into a non-empty partition page over an
1787 // empty one. This is an important aspect of minimizing memory usage for some
1788 // allocation sizes, particularly larger ones.
1789 TEST(PartitionAllocTest, PreferActiveOverEmpty) {
1790 TestSetup();
1791
1792 size_t size = (kSystemPageSize * 2) - kExtraAllocSize;
1793 // Allocate 3 full slot spans worth of 8192-byte allocations.
1794 // Each slot span for this size is 16384 bytes, or 1 partition page and 2
1795 // slots.
1796 void* ptr1 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1797 void* ptr2 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1798 void* ptr3 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1799 void* ptr4 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1800 void* ptr5 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1801 void* ptr6 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1802
1803 PartitionPage* page1 =
1804 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr1));
1805 PartitionPage* page2 =
1806 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr3));
1807 PartitionPage* page3 =
1808 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr6));
1809 EXPECT_NE(page1, page2);
1810 EXPECT_NE(page2, page3);
1811 PartitionBucket* bucket = page1->bucket;
1812 EXPECT_EQ(page3, bucket->activePagesHead);
1813
1814 // Free up the 2nd slot in each slot span.
1815 // This leaves the active list containing 3 pages, each with 1 used and 1
1816 // free slot. The active page will be the one containing ptr1.
1817 partitionFreeGeneric(genericAllocator.root(), ptr6);
1818 partitionFreeGeneric(genericAllocator.root(), ptr4);
1819 partitionFreeGeneric(genericAllocator.root(), ptr2);
1820 EXPECT_EQ(page1, bucket->activePagesHead);
1821
1822 // Empty the middle page in the active list.
1823 partitionFreeGeneric(genericAllocator.root(), ptr3);
1824 EXPECT_EQ(page1, bucket->activePagesHead);
1825
1826 // Empty the the first page in the active list -- also the current page.
1827 partitionFreeGeneric(genericAllocator.root(), ptr1);
1828
1829 // A good choice here is to re-fill the third page since the first two are
1830 // empty. We used to fail that.
1831 void* ptr7 = partitionAllocGeneric(genericAllocator.root(), size, typeName);
1832 EXPECT_EQ(ptr6, ptr7);
1833 EXPECT_EQ(page3, bucket->activePagesHead);
1834
1835 partitionFreeGeneric(genericAllocator.root(), ptr5);
1836 partitionFreeGeneric(genericAllocator.root(), ptr7);
1837
1838 TestShutdown();
1839 }
1840
1841 // Tests the API to purge discardable memory.
1842 TEST(PartitionAllocTest, PurgeDiscardable) {
1843 TestSetup();
1844
1845 // Free the second of two 4096 byte allocations and then purge.
1846 {
1847 void* ptr1 = partitionAllocGeneric(
1848 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
1849 char* ptr2 = reinterpret_cast<char*>(partitionAllocGeneric(
1850 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName));
1851 partitionFreeGeneric(genericAllocator.root(), ptr2);
1852 PartitionPage* page =
1853 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr1));
1854 EXPECT_EQ(2u, page->numUnprovisionedSlots);
1855 {
1856 MockPartitionStatsDumper mockStatsDumperGeneric;
1857 partitionDumpStatsGeneric(
1858 genericAllocator.root(), "mock_generic_allocator",
1859 false /* detailed dump */, &mockStatsDumperGeneric);
1860 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1861
1862 const PartitionBucketMemoryStats* stats =
1863 mockStatsDumperGeneric.GetBucketStats(kSystemPageSize);
1864 EXPECT_TRUE(stats);
1865 EXPECT_TRUE(stats->isValid);
1866 EXPECT_EQ(0u, stats->decommittableBytes);
1867 EXPECT_EQ(kSystemPageSize, stats->discardableBytes);
1868 EXPECT_EQ(kSystemPageSize, stats->activeBytes);
1869 EXPECT_EQ(2 * kSystemPageSize, stats->residentBytes);
1870 }
1871 CheckPageInCore(ptr2 - kPointerOffset, true);
1872 partitionPurgeMemoryGeneric(genericAllocator.root(),
1873 PartitionPurgeDiscardUnusedSystemPages);
1874 CheckPageInCore(ptr2 - kPointerOffset, false);
1875 EXPECT_EQ(3u, page->numUnprovisionedSlots);
1876
1877 partitionFreeGeneric(genericAllocator.root(), ptr1);
1878 }
1879 // Free the first of two 4096 byte allocations and then purge.
1880 {
1881 char* ptr1 = reinterpret_cast<char*>(partitionAllocGeneric(
1882 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName));
1883 void* ptr2 = partitionAllocGeneric(
1884 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
1885 partitionFreeGeneric(genericAllocator.root(), ptr1);
1886 {
1887 MockPartitionStatsDumper mockStatsDumperGeneric;
1888 partitionDumpStatsGeneric(
1889 genericAllocator.root(), "mock_generic_allocator",
1890 false /* detailed dump */, &mockStatsDumperGeneric);
1891 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1892
1893 const PartitionBucketMemoryStats* stats =
1894 mockStatsDumperGeneric.GetBucketStats(kSystemPageSize);
1895 EXPECT_TRUE(stats);
1896 EXPECT_TRUE(stats->isValid);
1897 EXPECT_EQ(0u, stats->decommittableBytes);
1898 EXPECT_EQ(kSystemPageSize, stats->discardableBytes);
1899 EXPECT_EQ(kSystemPageSize, stats->activeBytes);
1900 EXPECT_EQ(2 * kSystemPageSize, stats->residentBytes);
1901 }
1902 CheckPageInCore(ptr1 - kPointerOffset, true);
1903 partitionPurgeMemoryGeneric(genericAllocator.root(),
1904 PartitionPurgeDiscardUnusedSystemPages);
1905 CheckPageInCore(ptr1 - kPointerOffset, false);
1906
1907 partitionFreeGeneric(genericAllocator.root(), ptr2);
1908 }
1909 {
1910 char* ptr1 = reinterpret_cast<char*>(partitionAllocGeneric(
1911 genericAllocator.root(), 9216 - kExtraAllocSize, typeName));
1912 void* ptr2 = partitionAllocGeneric(genericAllocator.root(),
1913 9216 - kExtraAllocSize, typeName);
1914 void* ptr3 = partitionAllocGeneric(genericAllocator.root(),
1915 9216 - kExtraAllocSize, typeName);
1916 void* ptr4 = partitionAllocGeneric(genericAllocator.root(),
1917 9216 - kExtraAllocSize, typeName);
1918 memset(ptr1, 'A', 9216 - kExtraAllocSize);
1919 memset(ptr2, 'A', 9216 - kExtraAllocSize);
1920 partitionFreeGeneric(genericAllocator.root(), ptr2);
1921 partitionFreeGeneric(genericAllocator.root(), ptr1);
1922 {
1923 MockPartitionStatsDumper mockStatsDumperGeneric;
1924 partitionDumpStatsGeneric(
1925 genericAllocator.root(), "mock_generic_allocator",
1926 false /* detailed dump */, &mockStatsDumperGeneric);
1927 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1928
1929 const PartitionBucketMemoryStats* stats =
1930 mockStatsDumperGeneric.GetBucketStats(9216);
1931 EXPECT_TRUE(stats);
1932 EXPECT_TRUE(stats->isValid);
1933 EXPECT_EQ(0u, stats->decommittableBytes);
1934 EXPECT_EQ(2 * kSystemPageSize, stats->discardableBytes);
1935 EXPECT_EQ(9216u * 2, stats->activeBytes);
1936 EXPECT_EQ(9 * kSystemPageSize, stats->residentBytes);
1937 }
1938 CheckPageInCore(ptr1 - kPointerOffset, true);
1939 CheckPageInCore(ptr1 - kPointerOffset + kSystemPageSize, true);
1940 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 2), true);
1941 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 3), true);
1942 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 4), true);
1943 partitionPurgeMemoryGeneric(genericAllocator.root(),
1944 PartitionPurgeDiscardUnusedSystemPages);
1945 CheckPageInCore(ptr1 - kPointerOffset, true);
1946 CheckPageInCore(ptr1 - kPointerOffset + kSystemPageSize, false);
1947 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 2), true);
1948 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 3), false);
1949 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 4), true);
1950
1951 partitionFreeGeneric(genericAllocator.root(), ptr3);
1952 partitionFreeGeneric(genericAllocator.root(), ptr4);
1953 }
1954 {
1955 char* ptr1 = reinterpret_cast<char*>(partitionAllocGeneric(
1956 genericAllocator.root(), (64 * kSystemPageSize) - kExtraAllocSize,
1957 typeName));
1958 memset(ptr1, 'A', (64 * kSystemPageSize) - kExtraAllocSize);
1959 partitionFreeGeneric(genericAllocator.root(), ptr1);
1960 ptr1 = reinterpret_cast<char*>(partitionAllocGeneric(
1961 genericAllocator.root(), (61 * kSystemPageSize) - kExtraAllocSize,
1962 typeName));
1963 {
1964 MockPartitionStatsDumper mockStatsDumperGeneric;
1965 partitionDumpStatsGeneric(
1966 genericAllocator.root(), "mock_generic_allocator",
1967 false /* detailed dump */, &mockStatsDumperGeneric);
1968 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
1969
1970 const PartitionBucketMemoryStats* stats =
1971 mockStatsDumperGeneric.GetBucketStats(64 * kSystemPageSize);
1972 EXPECT_TRUE(stats);
1973 EXPECT_TRUE(stats->isValid);
1974 EXPECT_EQ(0u, stats->decommittableBytes);
1975 EXPECT_EQ(3 * kSystemPageSize, stats->discardableBytes);
1976 EXPECT_EQ(61 * kSystemPageSize, stats->activeBytes);
1977 EXPECT_EQ(64 * kSystemPageSize, stats->residentBytes);
1978 }
1979 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 60), true);
1980 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 61), true);
1981 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 62), true);
1982 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 63), true);
1983 partitionPurgeMemoryGeneric(genericAllocator.root(),
1984 PartitionPurgeDiscardUnusedSystemPages);
1985 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 60), true);
1986 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 61), false);
1987 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 62), false);
1988 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 63), false);
1989
1990 partitionFreeGeneric(genericAllocator.root(), ptr1);
1991 }
1992 // This sub-test tests truncation of the provisioned slots in a trickier
1993 // case where the freelist is rewritten.
1994 partitionPurgeMemoryGeneric(genericAllocator.root(),
1995 PartitionPurgeDecommitEmptyPages);
1996 {
1997 char* ptr1 = reinterpret_cast<char*>(partitionAllocGeneric(
1998 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName));
1999 void* ptr2 = partitionAllocGeneric(
2000 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2001 void* ptr3 = partitionAllocGeneric(
2002 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2003 void* ptr4 = partitionAllocGeneric(
2004 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2005 ptr1[0] = 'A';
2006 ptr1[kSystemPageSize] = 'A';
2007 ptr1[kSystemPageSize * 2] = 'A';
2008 ptr1[kSystemPageSize * 3] = 'A';
2009 PartitionPage* page =
2010 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr1));
2011 partitionFreeGeneric(genericAllocator.root(), ptr2);
2012 partitionFreeGeneric(genericAllocator.root(), ptr4);
2013 partitionFreeGeneric(genericAllocator.root(), ptr1);
2014 EXPECT_EQ(0u, page->numUnprovisionedSlots);
2015
2016 {
2017 MockPartitionStatsDumper mockStatsDumperGeneric;
2018 partitionDumpStatsGeneric(
2019 genericAllocator.root(), "mock_generic_allocator",
2020 false /* detailed dump */, &mockStatsDumperGeneric);
2021 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
2022
2023 const PartitionBucketMemoryStats* stats =
2024 mockStatsDumperGeneric.GetBucketStats(kSystemPageSize);
2025 EXPECT_TRUE(stats);
2026 EXPECT_TRUE(stats->isValid);
2027 EXPECT_EQ(0u, stats->decommittableBytes);
2028 EXPECT_EQ(2 * kSystemPageSize, stats->discardableBytes);
2029 EXPECT_EQ(kSystemPageSize, stats->activeBytes);
2030 EXPECT_EQ(4 * kSystemPageSize, stats->residentBytes);
2031 }
2032 CheckPageInCore(ptr1 - kPointerOffset, true);
2033 CheckPageInCore(ptr1 - kPointerOffset + kSystemPageSize, true);
2034 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 2), true);
2035 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 3), true);
2036 partitionPurgeMemoryGeneric(genericAllocator.root(),
2037 PartitionPurgeDiscardUnusedSystemPages);
2038 EXPECT_EQ(1u, page->numUnprovisionedSlots);
2039 CheckPageInCore(ptr1 - kPointerOffset, true);
2040 CheckPageInCore(ptr1 - kPointerOffset + kSystemPageSize, false);
2041 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 2), true);
2042 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 3), false);
2043
2044 // Let's check we didn't brick the freelist.
2045 void* ptr1b = partitionAllocGeneric(
2046 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2047 EXPECT_EQ(ptr1, ptr1b);
2048 void* ptr2b = partitionAllocGeneric(
2049 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2050 EXPECT_EQ(ptr2, ptr2b);
2051 EXPECT_FALSE(page->freelistHead);
2052
2053 partitionFreeGeneric(genericAllocator.root(), ptr1);
2054 partitionFreeGeneric(genericAllocator.root(), ptr2);
2055 partitionFreeGeneric(genericAllocator.root(), ptr3);
2056 }
2057 // This sub-test is similar, but tests a double-truncation.
2058 partitionPurgeMemoryGeneric(genericAllocator.root(),
2059 PartitionPurgeDecommitEmptyPages);
2060 {
2061 char* ptr1 = reinterpret_cast<char*>(partitionAllocGeneric(
2062 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName));
2063 void* ptr2 = partitionAllocGeneric(
2064 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2065 void* ptr3 = partitionAllocGeneric(
2066 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2067 void* ptr4 = partitionAllocGeneric(
2068 genericAllocator.root(), kSystemPageSize - kExtraAllocSize, typeName);
2069 ptr1[0] = 'A';
2070 ptr1[kSystemPageSize] = 'A';
2071 ptr1[kSystemPageSize * 2] = 'A';
2072 ptr1[kSystemPageSize * 3] = 'A';
2073 PartitionPage* page =
2074 partitionPointerToPage(partitionCookieFreePointerAdjust(ptr1));
2075 partitionFreeGeneric(genericAllocator.root(), ptr4);
2076 partitionFreeGeneric(genericAllocator.root(), ptr3);
2077 EXPECT_EQ(0u, page->numUnprovisionedSlots);
2078
2079 {
2080 MockPartitionStatsDumper mockStatsDumperGeneric;
2081 partitionDumpStatsGeneric(
2082 genericAllocator.root(), "mock_generic_allocator",
2083 false /* detailed dump */, &mockStatsDumperGeneric);
2084 EXPECT_TRUE(mockStatsDumperGeneric.IsMemoryAllocationRecorded());
2085
2086 const PartitionBucketMemoryStats* stats =
2087 mockStatsDumperGeneric.GetBucketStats(kSystemPageSize);
2088 EXPECT_TRUE(stats);
2089 EXPECT_TRUE(stats->isValid);
2090 EXPECT_EQ(0u, stats->decommittableBytes);
2091 EXPECT_EQ(2 * kSystemPageSize, stats->discardableBytes);
2092 EXPECT_EQ(2 * kSystemPageSize, stats->activeBytes);
2093 EXPECT_EQ(4 * kSystemPageSize, stats->residentBytes);
2094 }
2095 CheckPageInCore(ptr1 - kPointerOffset, true);
2096 CheckPageInCore(ptr1 - kPointerOffset + kSystemPageSize, true);
2097 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 2), true);
2098 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 3), true);
2099 partitionPurgeMemoryGeneric(genericAllocator.root(),
2100 PartitionPurgeDiscardUnusedSystemPages);
2101 EXPECT_EQ(2u, page->numUnprovisionedSlots);
2102 CheckPageInCore(ptr1 - kPointerOffset, true);
2103 CheckPageInCore(ptr1 - kPointerOffset + kSystemPageSize, true);
2104 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 2), false);
2105 CheckPageInCore(ptr1 - kPointerOffset + (kSystemPageSize * 3), false);
2106
2107 EXPECT_FALSE(page->freelistHead);
2108
2109 partitionFreeGeneric(genericAllocator.root(), ptr1);
2110 partitionFreeGeneric(genericAllocator.root(), ptr2);
2111 }
2112
2113 TestShutdown();
2114 }
2115
2116 } // namespace WTF
2117
2118 #endif // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698