1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/execution/isolate.h"
6 #include "src/heap/factory.h"
7 #include "src/heap/spaces-inl.h"
8 #include "src/objects/objects-inl.h"
9 #include "test/cctest/cctest.h"
10 #include "test/cctest/heap/heap-tester.h"
11 #include "test/cctest/heap/heap-utils.h"
12 
13 namespace v8 {
14 namespace internal {
15 namespace heap {
16 
17 // Tests don't work when --optimize-for-size is set.
18 #ifndef V8_LITE_MODE
19 
20 namespace {
21 
NewIsolateForPagePromotion(int min_semi_space_size=8,int max_semi_space_size=8)22 v8::Isolate* NewIsolateForPagePromotion(int min_semi_space_size = 8,
23                                         int max_semi_space_size = 8) {
24   // Parallel evacuation messes with fragmentation in a way that objects that
25   // should be copied in semi space are promoted to old space because of
26   // fragmentation.
27   FLAG_parallel_compaction = false;
28   FLAG_page_promotion = true;
29   FLAG_page_promotion_threshold = 0;
30   // Parallel scavenge introduces too much fragmentation.
31   FLAG_parallel_scavenge = false;
32   FLAG_min_semi_space_size = min_semi_space_size;
33   // We cannot optimize for size as we require a new space with more than one
34   // page.
35   FLAG_optimize_for_size = false;
36   // Set max_semi_space_size because it could've been initialized by an
37   // implication of optimize_for_size.
38   FLAG_max_semi_space_size = max_semi_space_size;
39   v8::Isolate::CreateParams create_params;
40   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
41   v8::Isolate* isolate = v8::Isolate::New(create_params);
42   return isolate;
43 }
44 
FindLastPageInNewSpace(const std::vector<Handle<FixedArray>> & handles)45 Page* FindLastPageInNewSpace(const std::vector<Handle<FixedArray>>& handles) {
46   for (auto rit = handles.rbegin(); rit != handles.rend(); ++rit) {
47     // One deref gets the Handle, the second deref gets the FixedArray.
48     Page* candidate = Page::FromHeapObject(**rit);
49     if (candidate->InNewSpace()) return candidate;
50   }
51   return nullptr;
52 }
53 
54 }  // namespace
55 
UNINITIALIZED_TEST(PagePromotion_NewToOld)56 UNINITIALIZED_TEST(PagePromotion_NewToOld) {
57   if (i::FLAG_single_generation) return;
58   if (!i::FLAG_incremental_marking) return;
59   if (!i::FLAG_page_promotion) return;
60   ManualGCScope manual_gc_scope;
61 
62   v8::Isolate* isolate = NewIsolateForPagePromotion();
63   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
64   {
65     v8::Isolate::Scope isolate_scope(isolate);
66     v8::HandleScope handle_scope(isolate);
67     v8::Context::New(isolate)->Enter();
68     Heap* heap = i_isolate->heap();
69 
70     // Ensure that the new space is empty so that the page to be promoted
71     // does not contain the age mark.
72     heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
73     heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
74 
75     std::vector<Handle<FixedArray>> handles;
76     heap::SimulateFullSpace(heap->new_space(), &handles);
77     heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
78     CHECK_GT(handles.size(), 0u);
79     Page* const to_be_promoted_page = FindLastPageInNewSpace(handles);
80     CHECK_NOT_NULL(to_be_promoted_page);
81     CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
82     // To perform a sanity check on live bytes we need to mark the heap.
83     heap::SimulateIncrementalMarking(heap, true);
84     // Sanity check that the page meets the requirements for promotion.
85     const int threshold_bytes = static_cast<int>(
86         FLAG_page_promotion_threshold *
87         MemoryChunkLayout::AllocatableMemoryInDataPage() / 100);
88     CHECK_GE(heap->incremental_marking()->marking_state()->live_bytes(
89                  to_be_promoted_page),
90              threshold_bytes);
91 
92     // Actual checks: The page is in new space first, but is moved to old space
93     // during a full GC.
94     CHECK(heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
95     CHECK(!heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
96     heap::GcAndSweep(heap, OLD_SPACE);
97     CHECK(!heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
98     CHECK(heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
99   }
100   isolate->Dispose();
101 }
102 
UNINITIALIZED_TEST(PagePromotion_NewToNew)103 UNINITIALIZED_TEST(PagePromotion_NewToNew) {
104   if (!i::FLAG_page_promotion || FLAG_always_promote_young_mc) return;
105 
106   v8::Isolate* isolate = NewIsolateForPagePromotion();
107   Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
108   {
109     v8::Isolate::Scope isolate_scope(isolate);
110     v8::HandleScope handle_scope(isolate);
111     v8::Context::New(isolate)->Enter();
112     Heap* heap = i_isolate->heap();
113 
114     std::vector<Handle<FixedArray>> handles;
115     heap::SimulateFullSpace(heap->new_space(), &handles);
116     CHECK_GT(handles.size(), 0u);
117     // Last object in handles should definitely be on a page that does not
118     // contain the age mark, thus qualifying for moving.
119     Handle<FixedArray> last_object = handles.back();
120     Page* to_be_promoted_page = Page::FromHeapObject(*last_object);
121     CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
122     CHECK(to_be_promoted_page->Contains(last_object->address()));
123     CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
124     heap::GcAndSweep(heap, OLD_SPACE);
125     CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
126     CHECK(to_be_promoted_page->Contains(last_object->address()));
127   }
128   isolate->Dispose();
129 }
130 
UNINITIALIZED_HEAP_TEST(Regress658718)131 UNINITIALIZED_HEAP_TEST(Regress658718) {
132   if (!i::FLAG_page_promotion || FLAG_always_promote_young_mc) return;
133 
134   v8::Isolate* isolate = NewIsolateForPagePromotion(4, 8);
135   Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
136   {
137     v8::Isolate::Scope isolate_scope(isolate);
138     v8::HandleScope handle_scope(isolate);
139     v8::Context::New(isolate)->Enter();
140     Heap* heap = i_isolate->heap();
141     heap->delay_sweeper_tasks_for_testing_ = true;
142     GrowNewSpace(heap);
143     {
144       v8::HandleScope inner_handle_scope(isolate);
145       std::vector<Handle<FixedArray>> handles;
146       heap::SimulateFullSpace(heap->new_space(), &handles);
147       CHECK_GT(handles.size(), 0u);
148       // Last object in handles should definitely be on a page that does not
149       // contain the age mark, thus qualifying for moving.
150       Handle<FixedArray> last_object = handles.back();
151       Page* to_be_promoted_page = Page::FromHeapObject(*last_object);
152       CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
153       CHECK(to_be_promoted_page->Contains(last_object->address()));
154       CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
155       heap->CollectGarbage(OLD_SPACE, i::GarbageCollectionReason::kTesting);
156       CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
157       CHECK(to_be_promoted_page->Contains(last_object->address()));
158     }
159     heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
160     heap->new_space()->Shrink();
161     heap->memory_allocator()->unmapper()->EnsureUnmappingCompleted();
162     heap->delay_sweeper_tasks_for_testing_ = false;
163     heap->mark_compact_collector()->sweeper()->StartSweeperTasks();
164     heap->mark_compact_collector()->EnsureSweepingCompleted();
165   }
166   isolate->Dispose();
167 }
168 
169 #endif  // V8_LITE_MODE
170 
171 }  // namespace heap
172 }  // namespace internal
173 }  // namespace v8
174