• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
37 namespace v8 {
38 namespace internal {
39 
40 #if 0
41 static 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
71 TEST(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.
107 class TestMemoryAllocatorScope {
108  public:
TestMemoryAllocatorScope(Isolate * isolate,MemoryAllocator * allocator)109   TestMemoryAllocatorScope(Isolate* isolate, MemoryAllocator* allocator)
110       : isolate_(isolate),
111         old_allocator_(isolate->memory_allocator_) {
112     isolate->memory_allocator_ = allocator;
113   }
114 
~TestMemoryAllocatorScope()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.
128 class TestCodeRangeScope {
129  public:
TestCodeRangeScope(Isolate * isolate,CodeRange * code_range)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 
~TestCodeRangeScope()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 
VerifyMemoryChunk(Isolate * isolate,Heap * heap,CodeRange * code_range,size_t reserve_area_size,size_t commit_area_size,size_t second_commit_area_size,Executability executable)148 static 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 
TEST(Regress3540)205 TEST(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 
Pseudorandom()246 static unsigned int Pseudorandom() {
247   static uint32_t lo = 2345;
248   lo = 18273 * (lo & 0xFFFFF) + (lo >> 16);
249   return lo & 0xFFFFF;
250 }
251 
252 
TEST(MemoryChunk)253 TEST(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 
TEST(MemoryAllocator)307 TEST(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 
TEST(NewSpace)355 TEST(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 
TEST(OldSpace)382 TEST(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 
TEST(CompactionSpace)405 TEST(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 
TEST(CompactionSpaceUsingExternalMemory)451 TEST(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 
InitializeCompactionSpaces(Heap * heap,int num_spaces)530 CompactionSpaceCollection** 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 
DestroyCompactionSpaces(CompactionSpaceCollection ** spaces,int num_spaces)541 void 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 
MergeCompactionSpaces(PagedSpace * space,CompactionSpaceCollection ** spaces,int num_spaces)550 void 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 
AllocateInCompactionSpaces(CompactionSpaceCollection ** spaces,AllocationSpace id,int num_spaces,int num_objects,int object_size)563 void 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 
CompactionStats(CompactionSpaceCollection ** spaces,AllocationSpace id,int num_spaces,intptr_t * capacity,intptr_t * size)579 void 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 
TestCompactionSpaceDivide(int num_additional_objects,int object_size,int num_compaction_spaces,int additional_capacity_in_bytes)591 void 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 
HEAP_TEST(CompactionSpaceDivideSinglePage)654 HEAP_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 
HEAP_TEST(CompactionSpaceDivideMultiplePages)668 HEAP_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 
TEST(LargeObjectSpace)681 TEST(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 
TEST(SizeOfFirstPageIsLargeEnough)716 TEST(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 
UNINITIALIZED_TEST(NewSpaceGrowsToTargetCapacity)748 UNINITIALIZED_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 
AllocateUnaligned(NewSpace * space,int size)791 static 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 
800 class Observer : public InlineAllocationObserver {
801  public:
Observer(intptr_t step_size)802   explicit Observer(intptr_t step_size)
803       : InlineAllocationObserver(step_size), count_(0) {}
804 
Step(int bytes_allocated,Address,size_t)805   void Step(int bytes_allocated, Address, size_t) override { count_++; }
806 
count() const807   int count() const { return count_; }
808 
809  private:
810   int count_;
811 };
812 
813 
UNINITIALIZED_TEST(InlineAllocationObserver)814 UNINITIALIZED_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 
UNINITIALIZED_TEST(InlineAllocationObserverCadence)897 UNINITIALIZED_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
930