blob: 2fe099d2e3b19af8268f268c670e0df36247f507 [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// Copyright 2011 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include <stdlib.h>
29
30#include "src/base/platform/platform.h"
31#include "src/snapshot/snapshot.h"
32#include "src/v8.h"
33#include "test/cctest/cctest.h"
34#include "test/cctest/heap/heap-tester.h"
35#include "test/cctest/heap/utils-inl.h"
36
37namespace v8 {
38namespace internal {
39
40#if 0
41static void VerifyRegionMarking(Address page_start) {
42#ifdef ENABLE_CARDMARKING_WRITE_BARRIER
43 Page* p = Page::FromAddress(page_start);
44
45 p->SetRegionMarks(Page::kAllRegionsCleanMarks);
46
47 for (Address addr = p->ObjectAreaStart();
48 addr < p->ObjectAreaEnd();
49 addr += kPointerSize) {
50 CHECK(!Page::FromAddress(addr)->IsRegionDirty(addr));
51 }
52
53 for (Address addr = p->ObjectAreaStart();
54 addr < p->ObjectAreaEnd();
55 addr += kPointerSize) {
56 Page::FromAddress(addr)->MarkRegionDirty(addr);
57 }
58
59 for (Address addr = p->ObjectAreaStart();
60 addr < p->ObjectAreaEnd();
61 addr += kPointerSize) {
62 CHECK(Page::FromAddress(addr)->IsRegionDirty(addr));
63 }
64#endif
65}
66#endif
67
68
69// TODO(gc) you can no longer allocate pages like this. Details are hidden.
70#if 0
71TEST(Page) {
72 byte* mem = NewArray<byte>(2*Page::kPageSize);
73 CHECK(mem != NULL);
74
75 Address start = reinterpret_cast<Address>(mem);
76 Address page_start = RoundUp(start, Page::kPageSize);
77
78 Page* p = Page::FromAddress(page_start);
79 // Initialized Page has heap pointer, normally set by memory_allocator.
80 p->heap_ = CcTest::heap();
81 CHECK(p->address() == page_start);
82 CHECK(p->is_valid());
83
84 p->opaque_header = 0;
85 p->SetIsLargeObjectPage(false);
86 CHECK(!p->next_page()->is_valid());
87
88 CHECK(p->ObjectAreaStart() == page_start + Page::kObjectStartOffset);
89 CHECK(p->ObjectAreaEnd() == page_start + Page::kPageSize);
90
91 CHECK(p->Offset(page_start + Page::kObjectStartOffset) ==
92 Page::kObjectStartOffset);
93 CHECK(p->Offset(page_start + Page::kPageSize) == Page::kPageSize);
94
95 CHECK(p->OffsetToAddress(Page::kObjectStartOffset) == p->ObjectAreaStart());
96 CHECK(p->OffsetToAddress(Page::kPageSize) == p->ObjectAreaEnd());
97
98 // test region marking
99 VerifyRegionMarking(page_start);
100
101 DeleteArray(mem);
102}
103#endif
104
105
106// Temporarily sets a given allocator in an isolate.
107class TestMemoryAllocatorScope {
108 public:
109 TestMemoryAllocatorScope(Isolate* isolate, MemoryAllocator* allocator)
110 : isolate_(isolate),
111 old_allocator_(isolate->memory_allocator_) {
112 isolate->memory_allocator_ = allocator;
113 }
114
115 ~TestMemoryAllocatorScope() {
116 isolate_->memory_allocator_ = old_allocator_;
117 }
118
119 private:
120 Isolate* isolate_;
121 MemoryAllocator* old_allocator_;
122
123 DISALLOW_COPY_AND_ASSIGN(TestMemoryAllocatorScope);
124};
125
126
127// Temporarily sets a given code range in an isolate.
128class TestCodeRangeScope {
129 public:
130 TestCodeRangeScope(Isolate* isolate, CodeRange* code_range)
131 : isolate_(isolate),
132 old_code_range_(isolate->code_range_) {
133 isolate->code_range_ = code_range;
134 }
135
136 ~TestCodeRangeScope() {
137 isolate_->code_range_ = old_code_range_;
138 }
139
140 private:
141 Isolate* isolate_;
142 CodeRange* old_code_range_;
143
144 DISALLOW_COPY_AND_ASSIGN(TestCodeRangeScope);
145};
146
147
148static void VerifyMemoryChunk(Isolate* isolate,
149 Heap* heap,
150 CodeRange* code_range,
151 size_t reserve_area_size,
152 size_t commit_area_size,
153 size_t second_commit_area_size,
154 Executability executable) {
155 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
156 CHECK(memory_allocator->SetUp(heap->MaxReserved(),
157 heap->MaxExecutableSize()));
158 TestMemoryAllocatorScope test_allocator_scope(isolate, memory_allocator);
159 TestCodeRangeScope test_code_range_scope(isolate, code_range);
160
161 size_t header_size = (executable == EXECUTABLE)
162 ? MemoryAllocator::CodePageGuardStartOffset()
163 : MemoryChunk::kObjectStartOffset;
164 size_t guard_size = (executable == EXECUTABLE)
165 ? MemoryAllocator::CodePageGuardSize()
166 : 0;
167
168 MemoryChunk* memory_chunk = memory_allocator->AllocateChunk(reserve_area_size,
169 commit_area_size,
170 executable,
171 NULL);
172 size_t alignment = code_range != NULL && code_range->valid()
173 ? MemoryChunk::kAlignment
174 : base::OS::CommitPageSize();
175 size_t reserved_size =
176 ((executable == EXECUTABLE))
177 ? RoundUp(header_size + guard_size + reserve_area_size + guard_size,
178 alignment)
179 : RoundUp(header_size + reserve_area_size,
180 base::OS::CommitPageSize());
181 CHECK(memory_chunk->size() == reserved_size);
182 CHECK(memory_chunk->area_start() < memory_chunk->address() +
183 memory_chunk->size());
184 CHECK(memory_chunk->area_end() <= memory_chunk->address() +
185 memory_chunk->size());
186 CHECK(static_cast<size_t>(memory_chunk->area_size()) == commit_area_size);
187
188 Address area_start = memory_chunk->area_start();
189
190 memory_chunk->CommitArea(second_commit_area_size);
191 CHECK(area_start == memory_chunk->area_start());
192 CHECK(memory_chunk->area_start() < memory_chunk->address() +
193 memory_chunk->size());
194 CHECK(memory_chunk->area_end() <= memory_chunk->address() +
195 memory_chunk->size());
196 CHECK(static_cast<size_t>(memory_chunk->area_size()) ==
197 second_commit_area_size);
198
199 memory_allocator->Free(memory_chunk);
200 memory_allocator->TearDown();
201 delete memory_allocator;
202}
203
204
205TEST(Regress3540) {
206 Isolate* isolate = CcTest::i_isolate();
207 Heap* heap = isolate->heap();
208 const int pageSize = Page::kPageSize;
209 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
210 CHECK(
211 memory_allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize()));
212 TestMemoryAllocatorScope test_allocator_scope(isolate, memory_allocator);
213 CodeRange* code_range = new CodeRange(isolate);
214 const size_t code_range_size = 4 * pageSize;
215 if (!code_range->SetUp(
216 code_range_size +
217 RoundUp(v8::base::OS::CommitPageSize() * kReservedCodeRangePages,
218 MemoryChunk::kAlignment) +
219 v8::internal::MemoryAllocator::CodePageAreaSize())) {
220 return;
221 }
222
223 Address address;
224 size_t size;
225 size_t request_size = code_range_size - 2 * pageSize;
226 address = code_range->AllocateRawMemory(
227 request_size, request_size - (2 * MemoryAllocator::CodePageGuardSize()),
228 &size);
229 CHECK(address != NULL);
230
231 Address null_address;
232 size_t null_size;
233 request_size = code_range_size - pageSize;
234 null_address = code_range->AllocateRawMemory(
235 request_size, request_size - (2 * MemoryAllocator::CodePageGuardSize()),
236 &null_size);
237 CHECK(null_address == NULL);
238
239 code_range->FreeRawMemory(address, size);
240 delete code_range;
241 memory_allocator->TearDown();
242 delete memory_allocator;
243}
244
245
246static unsigned int Pseudorandom() {
247 static uint32_t lo = 2345;
248 lo = 18273 * (lo & 0xFFFFF) + (lo >> 16);
249 return lo & 0xFFFFF;
250}
251
252
253TEST(MemoryChunk) {
254 Isolate* isolate = CcTest::i_isolate();
255 Heap* heap = isolate->heap();
256
257 size_t reserve_area_size = 1 * MB;
258 size_t initial_commit_area_size, second_commit_area_size;
259
260 for (int i = 0; i < 100; i++) {
261 initial_commit_area_size = Pseudorandom();
262 second_commit_area_size = Pseudorandom();
263
264 // With CodeRange.
265 CodeRange* code_range = new CodeRange(isolate);
266 const size_t code_range_size = 32 * MB;
267 if (!code_range->SetUp(code_range_size)) return;
268
269 VerifyMemoryChunk(isolate,
270 heap,
271 code_range,
272 reserve_area_size,
273 initial_commit_area_size,
274 second_commit_area_size,
275 EXECUTABLE);
276
277 VerifyMemoryChunk(isolate,
278 heap,
279 code_range,
280 reserve_area_size,
281 initial_commit_area_size,
282 second_commit_area_size,
283 NOT_EXECUTABLE);
284 delete code_range;
285
286 // Without CodeRange.
287 code_range = NULL;
288 VerifyMemoryChunk(isolate,
289 heap,
290 code_range,
291 reserve_area_size,
292 initial_commit_area_size,
293 second_commit_area_size,
294 EXECUTABLE);
295
296 VerifyMemoryChunk(isolate,
297 heap,
298 code_range,
299 reserve_area_size,
300 initial_commit_area_size,
301 second_commit_area_size,
302 NOT_EXECUTABLE);
303 }
304}
305
306
307TEST(MemoryAllocator) {
308 Isolate* isolate = CcTest::i_isolate();
309 Heap* heap = isolate->heap();
310
311 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
312 CHECK(memory_allocator != nullptr);
313 CHECK(memory_allocator->SetUp(heap->MaxReserved(),
314 heap->MaxExecutableSize()));
315 TestMemoryAllocatorScope test_scope(isolate, memory_allocator);
316
317 {
318 int total_pages = 0;
319 OldSpace faked_space(heap, OLD_SPACE, NOT_EXECUTABLE);
320 Page* first_page = memory_allocator->AllocatePage(
321 faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE);
322
323 first_page->InsertAfter(faked_space.anchor()->prev_page());
324 CHECK(first_page->is_valid());
325 CHECK(first_page->next_page() == faked_space.anchor());
326 total_pages++;
327
328 for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) {
329 CHECK(p->owner() == &faked_space);
330 }
331
332 // Again, we should get n or n - 1 pages.
333 Page* other = memory_allocator->AllocatePage(faked_space.AreaSize(),
334 &faked_space, NOT_EXECUTABLE);
335 CHECK(other->is_valid());
336 total_pages++;
337 other->InsertAfter(first_page);
338 int page_count = 0;
339 for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) {
340 CHECK(p->owner() == &faked_space);
341 page_count++;
342 }
343 CHECK(total_pages == page_count);
344
345 Page* second_page = first_page->next_page();
346 CHECK(second_page->is_valid());
347
348 // OldSpace's destructor will tear down the space and free up all pages.
349 }
350 memory_allocator->TearDown();
351 delete memory_allocator;
352}
353
354
355TEST(NewSpace) {
356 Isolate* isolate = CcTest::i_isolate();
357 Heap* heap = isolate->heap();
358 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
359 CHECK(memory_allocator->SetUp(heap->MaxReserved(),
360 heap->MaxExecutableSize()));
361 TestMemoryAllocatorScope test_scope(isolate, memory_allocator);
362
363 NewSpace new_space(heap);
364
365 CHECK(new_space.SetUp(CcTest::heap()->ReservedSemiSpaceSize(),
366 CcTest::heap()->ReservedSemiSpaceSize()));
367 CHECK(new_space.HasBeenSetUp());
368
369 while (new_space.Available() >= Page::kMaxRegularHeapObjectSize) {
370 Object* obj =
371 new_space.AllocateRawUnaligned(Page::kMaxRegularHeapObjectSize)
372 .ToObjectChecked();
373 CHECK(new_space.Contains(HeapObject::cast(obj)));
374 }
375
376 new_space.TearDown();
377 memory_allocator->TearDown();
378 delete memory_allocator;
379}
380
381
382TEST(OldSpace) {
383 Isolate* isolate = CcTest::i_isolate();
384 Heap* heap = isolate->heap();
385 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
386 CHECK(memory_allocator->SetUp(heap->MaxReserved(),
387 heap->MaxExecutableSize()));
388 TestMemoryAllocatorScope test_scope(isolate, memory_allocator);
389
390 OldSpace* s = new OldSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
391 CHECK(s != NULL);
392
393 CHECK(s->SetUp());
394
395 while (s->Available() > 0) {
396 s->AllocateRawUnaligned(Page::kMaxRegularHeapObjectSize).ToObjectChecked();
397 }
398
399 delete s;
400 memory_allocator->TearDown();
401 delete memory_allocator;
402}
403
404
405TEST(CompactionSpace) {
406 Isolate* isolate = CcTest::i_isolate();
407 Heap* heap = isolate->heap();
408 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate);
409 CHECK(memory_allocator != nullptr);
410 CHECK(
411 memory_allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize()));
412 TestMemoryAllocatorScope test_scope(isolate, memory_allocator);
413
414 CompactionSpace* compaction_space =
415 new CompactionSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
416 CHECK(compaction_space != NULL);
417 CHECK(compaction_space->SetUp());
418
419 OldSpace* old_space = new OldSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
420 CHECK(old_space != NULL);
421 CHECK(old_space->SetUp());
422
423 // Cannot loop until "Available()" since we initially have 0 bytes available
424 // and would thus neither grow, nor be able to allocate an object.
425 const int kNumObjects = 100;
426 const int kNumObjectsPerPage =
427 compaction_space->AreaSize() / Page::kMaxRegularHeapObjectSize;
428 const int kExpectedPages =
429 (kNumObjects + kNumObjectsPerPage - 1) / kNumObjectsPerPage;
430 for (int i = 0; i < kNumObjects; i++) {
431 compaction_space->AllocateRawUnaligned(Page::kMaxRegularHeapObjectSize)
432 .ToObjectChecked();
433 }
434 int pages_in_old_space = old_space->CountTotalPages();
435 int pages_in_compaction_space = compaction_space->CountTotalPages();
436 CHECK_EQ(pages_in_compaction_space, kExpectedPages);
437 CHECK_LE(pages_in_old_space, 1);
438
439 old_space->MergeCompactionSpace(compaction_space);
440 CHECK_EQ(old_space->CountTotalPages(),
441 pages_in_old_space + pages_in_compaction_space);
442
443 delete compaction_space;
444 delete old_space;
445
446 memory_allocator->TearDown();
447 delete memory_allocator;
448}
449
450
451TEST(CompactionSpaceUsingExternalMemory) {
452 const int kObjectSize = 512;
453
454 Isolate* isolate = CcTest::i_isolate();
455 Heap* heap = isolate->heap();
456 MemoryAllocator* allocator = new MemoryAllocator(isolate);
457 CHECK(allocator != nullptr);
458 CHECK(allocator->SetUp(heap->MaxReserved(), heap->MaxExecutableSize()));
459 TestMemoryAllocatorScope test_scope(isolate, allocator);
460
461 CompactionSpaceCollection* collection = new CompactionSpaceCollection(heap);
462 CompactionSpace* compaction_space = collection->Get(OLD_SPACE);
463 CHECK(compaction_space != NULL);
464 CHECK(compaction_space->SetUp());
465
466 OldSpace* old_space = new OldSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
467 CHECK(old_space != NULL);
468 CHECK(old_space->SetUp());
469
470 // The linear allocation area already counts as used bytes, making
471 // exact testing impossible.
472 heap->DisableInlineAllocation();
473
474 // Test:
475 // * Allocate a backing store in old_space.
476 // * Compute the number num_rest_objects of kObjectSize objects that fit into
477 // of available memory.
478 // kNumRestObjects.
479 // * Add the rest of available memory to the compaction space.
480 // * Allocate kNumRestObjects in the compaction space.
481 // * Allocate one object more.
482 // * Merge the compaction space and compare the expected number of pages.
483
484 // Allocate a single object in old_space to initialize a backing page.
485 old_space->AllocateRawUnaligned(kObjectSize).ToObjectChecked();
486 // Compute the number of objects that fit into the rest in old_space.
487 intptr_t rest = static_cast<int>(old_space->Available());
488 CHECK_GT(rest, 0);
489 intptr_t num_rest_objects = rest / kObjectSize;
490 // After allocating num_rest_objects in compaction_space we allocate a bit
491 // more.
492 const intptr_t kAdditionalCompactionMemory = kObjectSize;
493 // We expect a single old_space page.
494 const intptr_t kExpectedInitialOldSpacePages = 1;
495 // We expect a single additional page in compaction space because we mostly
496 // use external memory.
497 const intptr_t kExpectedCompactionPages = 1;
498 // We expect two pages to be reachable from old_space in the end.
499 const intptr_t kExpectedOldSpacePagesAfterMerge = 2;
500
501 CHECK_EQ(old_space->CountTotalPages(), kExpectedInitialOldSpacePages);
502 CHECK_EQ(compaction_space->CountTotalPages(), 0);
503 CHECK_EQ(compaction_space->Capacity(), 0);
504 // Make the rest of memory available for compaction.
505 old_space->DivideUponCompactionSpaces(&collection, 1, rest);
506 CHECK_EQ(compaction_space->CountTotalPages(), 0);
507 CHECK_EQ(compaction_space->Capacity(), rest);
508 while (num_rest_objects-- > 0) {
509 compaction_space->AllocateRawUnaligned(kObjectSize).ToObjectChecked();
510 }
511 // We only used external memory so far.
512 CHECK_EQ(compaction_space->CountTotalPages(), 0);
513 // Additional allocation.
514 compaction_space->AllocateRawUnaligned(kAdditionalCompactionMemory)
515 .ToObjectChecked();
516 // Now the compaction space shouldve also acquired a page.
517 CHECK_EQ(compaction_space->CountTotalPages(), kExpectedCompactionPages);
518
519 old_space->MergeCompactionSpace(compaction_space);
520 CHECK_EQ(old_space->CountTotalPages(), kExpectedOldSpacePagesAfterMerge);
521
522 delete collection;
523 delete old_space;
524
525 allocator->TearDown();
526 delete allocator;
527}
528
529
530CompactionSpaceCollection** HeapTester::InitializeCompactionSpaces(
531 Heap* heap, int num_spaces) {
532 CompactionSpaceCollection** spaces =
533 new CompactionSpaceCollection*[num_spaces];
534 for (int i = 0; i < num_spaces; i++) {
535 spaces[i] = new CompactionSpaceCollection(heap);
536 }
537 return spaces;
538}
539
540
541void HeapTester::DestroyCompactionSpaces(CompactionSpaceCollection** spaces,
542 int num_spaces) {
543 for (int i = 0; i < num_spaces; i++) {
544 delete spaces[i];
545 }
546 delete[] spaces;
547}
548
549
550void HeapTester::MergeCompactionSpaces(PagedSpace* space,
551 CompactionSpaceCollection** spaces,
552 int num_spaces) {
553 AllocationSpace id = space->identity();
554 for (int i = 0; i < num_spaces; i++) {
555 space->MergeCompactionSpace(spaces[i]->Get(id));
556 CHECK_EQ(spaces[i]->Get(id)->accounting_stats_.Size(), 0);
557 CHECK_EQ(spaces[i]->Get(id)->accounting_stats_.Capacity(), 0);
558 CHECK_EQ(spaces[i]->Get(id)->Waste(), 0);
559 }
560}
561
562
563void HeapTester::AllocateInCompactionSpaces(CompactionSpaceCollection** spaces,
564 AllocationSpace id, int num_spaces,
565 int num_objects, int object_size) {
566 for (int i = 0; i < num_spaces; i++) {
567 for (int j = 0; j < num_objects; j++) {
568 spaces[i]->Get(id)->AllocateRawUnaligned(object_size).ToObjectChecked();
569 }
570 spaces[i]->Get(id)->EmptyAllocationInfo();
571 CHECK_EQ(spaces[i]->Get(id)->accounting_stats_.Size(),
572 num_objects * object_size);
573 CHECK_GE(spaces[i]->Get(id)->accounting_stats_.Capacity(),
574 spaces[i]->Get(id)->accounting_stats_.Size());
575 }
576}
577
578
579void HeapTester::CompactionStats(CompactionSpaceCollection** spaces,
580 AllocationSpace id, int num_spaces,
581 intptr_t* capacity, intptr_t* size) {
582 *capacity = 0;
583 *size = 0;
584 for (int i = 0; i < num_spaces; i++) {
585 *capacity += spaces[i]->Get(id)->accounting_stats_.Capacity();
586 *size += spaces[i]->Get(id)->accounting_stats_.Size();
587 }
588}
589
590
591void HeapTester::TestCompactionSpaceDivide(int num_additional_objects,
592 int object_size,
593 int num_compaction_spaces,
594 int additional_capacity_in_bytes) {
595 Isolate* isolate = CcTest::i_isolate();
596 Heap* heap = isolate->heap();
597 OldSpace* old_space = new OldSpace(heap, OLD_SPACE, NOT_EXECUTABLE);
598 CHECK(old_space != nullptr);
599 CHECK(old_space->SetUp());
600 old_space->AllocateRawUnaligned(object_size).ToObjectChecked();
601 old_space->EmptyAllocationInfo();
602
603 intptr_t rest_capacity = old_space->accounting_stats_.Capacity() -
604 old_space->accounting_stats_.Size();
605 intptr_t capacity_for_compaction_space =
606 rest_capacity / num_compaction_spaces;
607 int num_objects_in_compaction_space =
608 static_cast<int>(capacity_for_compaction_space) / object_size +
609 num_additional_objects;
610 CHECK_GT(num_objects_in_compaction_space, 0);
611 intptr_t initial_old_space_capacity = old_space->accounting_stats_.Capacity();
612
613 CompactionSpaceCollection** spaces =
614 InitializeCompactionSpaces(heap, num_compaction_spaces);
615 old_space->DivideUponCompactionSpaces(spaces, num_compaction_spaces,
616 capacity_for_compaction_space);
617
618 intptr_t compaction_capacity = 0;
619 intptr_t compaction_size = 0;
620 CompactionStats(spaces, OLD_SPACE, num_compaction_spaces,
621 &compaction_capacity, &compaction_size);
622
623 intptr_t old_space_capacity = old_space->accounting_stats_.Capacity();
624 intptr_t old_space_size = old_space->accounting_stats_.Size();
625 // Compaction space memory is subtracted from the original space's capacity.
626 CHECK_EQ(old_space_capacity,
627 initial_old_space_capacity - compaction_capacity);
628 CHECK_EQ(compaction_size, 0);
629
630 AllocateInCompactionSpaces(spaces, OLD_SPACE, num_compaction_spaces,
631 num_objects_in_compaction_space, object_size);
632
633 // Old space size and capacity should be the same as after dividing.
634 CHECK_EQ(old_space->accounting_stats_.Size(), old_space_size);
635 CHECK_EQ(old_space->accounting_stats_.Capacity(), old_space_capacity);
636
637 CompactionStats(spaces, OLD_SPACE, num_compaction_spaces,
638 &compaction_capacity, &compaction_size);
639 MergeCompactionSpaces(old_space, spaces, num_compaction_spaces);
640
641 CHECK_EQ(old_space->accounting_stats_.Capacity(),
642 old_space_capacity + compaction_capacity);
643 CHECK_EQ(old_space->accounting_stats_.Size(),
644 old_space_size + compaction_size);
645 // We check against the expected end capacity.
646 CHECK_EQ(old_space->accounting_stats_.Capacity(),
647 initial_old_space_capacity + additional_capacity_in_bytes);
648
649 DestroyCompactionSpaces(spaces, num_compaction_spaces);
650 delete old_space;
651}
652
653
654HEAP_TEST(CompactionSpaceDivideSinglePage) {
655 const int kObjectSize = KB;
656 const int kCompactionSpaces = 4;
657 // Since the bound for objects is tight and the dividing is best effort, we
658 // subtract some objects to make sure we still fit in the initial page.
659 // A CHECK makes sure that the overall number of allocated objects stays
660 // > 0.
661 const int kAdditionalObjects = -10;
662 const int kAdditionalCapacityRequired = 0;
663 TestCompactionSpaceDivide(kAdditionalObjects, kObjectSize, kCompactionSpaces,
664 kAdditionalCapacityRequired);
665}
666
667
668HEAP_TEST(CompactionSpaceDivideMultiplePages) {
669 const int kObjectSize = KB;
670 const int kCompactionSpaces = 4;
671 // Allocate half a page of objects to ensure that we need one more page per
672 // compaction space.
673 const int kAdditionalObjects = (Page::kPageSize / kObjectSize / 2);
674 const int kAdditionalCapacityRequired =
675 Page::kAllocatableMemory * kCompactionSpaces;
676 TestCompactionSpaceDivide(kAdditionalObjects, kObjectSize, kCompactionSpaces,
677 kAdditionalCapacityRequired);
678}
679
680
681TEST(LargeObjectSpace) {
682 v8::V8::Initialize();
683
684 LargeObjectSpace* lo = CcTest::heap()->lo_space();
685 CHECK(lo != NULL);
686
687 int lo_size = Page::kPageSize;
688
689 Object* obj = lo->AllocateRaw(lo_size, NOT_EXECUTABLE).ToObjectChecked();
690 CHECK(obj->IsHeapObject());
691
692 HeapObject* ho = HeapObject::cast(obj);
693
694 CHECK(lo->Contains(HeapObject::cast(obj)));
695
696 CHECK(lo->FindObject(ho->address()) == obj);
697
698 CHECK(lo->Contains(ho));
699
700 while (true) {
701 intptr_t available = lo->Available();
702 { AllocationResult allocation = lo->AllocateRaw(lo_size, NOT_EXECUTABLE);
703 if (allocation.IsRetry()) break;
704 }
705 // The available value is conservative such that it may report
706 // zero prior to heap exhaustion.
707 CHECK(lo->Available() < available || available == 0);
708 }
709
710 CHECK(!lo->IsEmpty());
711
712 CHECK(lo->AllocateRaw(lo_size, NOT_EXECUTABLE).IsRetry());
713}
714
715
716TEST(SizeOfFirstPageIsLargeEnough) {
717 if (i::FLAG_always_opt) return;
718 // Bootstrapping without a snapshot causes more allocations.
719 CcTest::InitializeVM();
720 Isolate* isolate = CcTest::i_isolate();
721 if (!isolate->snapshot_available()) return;
722 if (Snapshot::EmbedsScript(isolate)) return;
723
724 // If this test fails due to enabling experimental natives that are not part
725 // of the snapshot, we may need to adjust CalculateFirstPageSizes.
726
727 // Freshly initialized VM gets by with one page per space.
728 for (int i = FIRST_PAGED_SPACE; i <= LAST_PAGED_SPACE; i++) {
729 // Debug code can be very large, so skip CODE_SPACE if we are generating it.
730 if (i == CODE_SPACE && i::FLAG_debug_code) continue;
731 CHECK_EQ(1, isolate->heap()->paged_space(i)->CountTotalPages());
732 }
733
734 // Executing the empty script gets by with one page per space.
735 HandleScope scope(isolate);
736 CompileRun("/*empty*/");
737 for (int i = FIRST_PAGED_SPACE; i <= LAST_PAGED_SPACE; i++) {
738 // Debug code can be very large, so skip CODE_SPACE if we are generating it.
739 if (i == CODE_SPACE && i::FLAG_debug_code) continue;
740 CHECK_EQ(1, isolate->heap()->paged_space(i)->CountTotalPages());
741 }
742
743 // No large objects required to perform the above steps.
744 CHECK(isolate->heap()->lo_space()->IsEmpty());
745}
746
747
748UNINITIALIZED_TEST(NewSpaceGrowsToTargetCapacity) {
749 FLAG_target_semi_space_size = 2 * (Page::kPageSize / MB);
750 if (FLAG_optimize_for_size) return;
751
752 v8::Isolate::CreateParams create_params;
753 create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
754 v8::Isolate* isolate = v8::Isolate::New(create_params);
755 {
756 v8::Isolate::Scope isolate_scope(isolate);
757 v8::HandleScope handle_scope(isolate);
758 v8::Context::New(isolate)->Enter();
759
760 Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
761
762 NewSpace* new_space = i_isolate->heap()->new_space();
763
764 // This test doesn't work if we start with a non-default new space
765 // configuration.
766 if (new_space->InitialTotalCapacity() == Page::kPageSize) {
767 CHECK_EQ(new_space->CommittedMemory(), new_space->InitialTotalCapacity());
768
769 // Fill up the first (and only) page of the semi space.
770 FillCurrentPage(new_space);
771
772 // Try to allocate out of the new space. A new page should be added and
773 // the
774 // allocation should succeed.
775 v8::internal::AllocationResult allocation =
776 new_space->AllocateRawUnaligned(80);
777 CHECK(!allocation.IsRetry());
778 CHECK_EQ(new_space->CommittedMemory(), 2 * Page::kPageSize);
779
780 // Turn the allocation into a proper object so isolate teardown won't
781 // crash.
782 HeapObject* free_space = NULL;
783 CHECK(allocation.To(&free_space));
784 new_space->heap()->CreateFillerObjectAt(free_space->address(), 80);
785 }
786 }
787 isolate->Dispose();
788}
789
790
791static HeapObject* AllocateUnaligned(NewSpace* space, int size) {
792 AllocationResult allocation = space->AllocateRawUnaligned(size);
793 CHECK(!allocation.IsRetry());
794 HeapObject* filler = NULL;
795 CHECK(allocation.To(&filler));
796 space->heap()->CreateFillerObjectAt(filler->address(), size);
797 return filler;
798}
799
800class Observer : public InlineAllocationObserver {
801 public:
802 explicit Observer(intptr_t step_size)
803 : InlineAllocationObserver(step_size), count_(0) {}
804
805 void Step(int bytes_allocated, Address, size_t) override { count_++; }
806
807 int count() const { return count_; }
808
809 private:
810 int count_;
811};
812
813
814UNINITIALIZED_TEST(InlineAllocationObserver) {
815 v8::Isolate::CreateParams create_params;
816 create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
817 v8::Isolate* isolate = v8::Isolate::New(create_params);
818 {
819 v8::Isolate::Scope isolate_scope(isolate);
820 v8::HandleScope handle_scope(isolate);
821 v8::Context::New(isolate)->Enter();
822
823 Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
824
825 NewSpace* new_space = i_isolate->heap()->new_space();
826
827 Observer observer1(128);
828 new_space->AddInlineAllocationObserver(&observer1);
829
830 // The observer should not get notified if we have only allocated less than
831 // 128 bytes.
832 AllocateUnaligned(new_space, 64);
833 CHECK_EQ(observer1.count(), 0);
834
835 // The observer should get called when we have allocated exactly 128 bytes.
836 AllocateUnaligned(new_space, 64);
837 CHECK_EQ(observer1.count(), 1);
838
839 // Another >128 bytes should get another notification.
840 AllocateUnaligned(new_space, 136);
841 CHECK_EQ(observer1.count(), 2);
842
843 // Allocating a large object should get only one notification.
844 AllocateUnaligned(new_space, 1024);
845 CHECK_EQ(observer1.count(), 3);
846
847 // Allocating another 2048 bytes in small objects should get 16
848 // notifications.
849 for (int i = 0; i < 64; ++i) {
850 AllocateUnaligned(new_space, 32);
851 }
852 CHECK_EQ(observer1.count(), 19);
853
854 // Multiple observers should work.
855 Observer observer2(96);
856 new_space->AddInlineAllocationObserver(&observer2);
857
858 AllocateUnaligned(new_space, 2048);
859 CHECK_EQ(observer1.count(), 20);
860 CHECK_EQ(observer2.count(), 1);
861
862 AllocateUnaligned(new_space, 104);
863 CHECK_EQ(observer1.count(), 20);
864 CHECK_EQ(observer2.count(), 2);
865
866 // Callback should stop getting called after an observer is removed.
867 new_space->RemoveInlineAllocationObserver(&observer1);
868
869 AllocateUnaligned(new_space, 384);
870 CHECK_EQ(observer1.count(), 20); // no more notifications.
871 CHECK_EQ(observer2.count(), 3); // this one is still active.
872
873 // Ensure that PauseInlineAllocationObserversScope work correctly.
874 AllocateUnaligned(new_space, 48);
875 CHECK_EQ(observer2.count(), 3);
876 {
877 PauseInlineAllocationObserversScope pause_observers(new_space);
878 CHECK_EQ(observer2.count(), 3);
879 AllocateUnaligned(new_space, 384);
880 CHECK_EQ(observer2.count(), 3);
881 }
882 CHECK_EQ(observer2.count(), 3);
883 // Coupled with the 48 bytes allocated before the pause, another 48 bytes
884 // allocated here should trigger a notification.
885 AllocateUnaligned(new_space, 48);
886 CHECK_EQ(observer2.count(), 4);
887
888 new_space->RemoveInlineAllocationObserver(&observer2);
889 AllocateUnaligned(new_space, 384);
890 CHECK_EQ(observer1.count(), 20);
891 CHECK_EQ(observer2.count(), 4);
892 }
893 isolate->Dispose();
894}
895
896
897UNINITIALIZED_TEST(InlineAllocationObserverCadence) {
898 v8::Isolate::CreateParams create_params;
899 create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
900 v8::Isolate* isolate = v8::Isolate::New(create_params);
901 {
902 v8::Isolate::Scope isolate_scope(isolate);
903 v8::HandleScope handle_scope(isolate);
904 v8::Context::New(isolate)->Enter();
905
906 Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
907
908 NewSpace* new_space = i_isolate->heap()->new_space();
909
910 Observer observer1(512);
911 new_space->AddInlineAllocationObserver(&observer1);
912 Observer observer2(576);
913 new_space->AddInlineAllocationObserver(&observer2);
914
915 for (int i = 0; i < 512; ++i) {
916 AllocateUnaligned(new_space, 32);
917 }
918
919 new_space->RemoveInlineAllocationObserver(&observer1);
920 new_space->RemoveInlineAllocationObserver(&observer2);
921
922 CHECK_EQ(observer1.count(), 32);
923 CHECK_EQ(observer2.count(), 28);
924 }
925 isolate->Dispose();
926}
927
928} // namespace internal
929} // namespace v8