Line data Source code
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/factory.h"
6 : #include "src/heap/array-buffer-tracker.h"
7 : #include "src/heap/spaces-inl.h"
8 : #include "src/isolate.h"
9 : #include "src/objects-inl.h"
10 : #include "test/cctest/cctest.h"
11 : #include "test/cctest/heap/heap-tester.h"
12 : #include "test/cctest/heap/heap-utils.h"
13 :
14 : namespace v8 {
15 : namespace internal {
16 : namespace heap {
17 :
18 : namespace {
19 :
20 20 : v8::Isolate* NewIsolateForPagePromotion(int min_semi_space_size = 8,
21 : int max_semi_space_size = 8) {
22 : // Parallel evacuation messes with fragmentation in a way that objects that
23 : // should be copied in semi space are promoted to old space because of
24 : // fragmentation.
25 20 : FLAG_parallel_compaction = false;
26 20 : FLAG_page_promotion = true;
27 20 : FLAG_page_promotion_threshold = 0;
28 : // Parallel scavenge introduces too much fragmentation.
29 20 : FLAG_parallel_scavenge = false;
30 20 : FLAG_min_semi_space_size = min_semi_space_size;
31 : // We cannot optimize for size as we require a new space with more than one
32 : // page.
33 20 : FLAG_optimize_for_size = false;
34 : // Set max_semi_space_size because it could've been initialized by an
35 : // implication of optimize_for_size.
36 20 : FLAG_max_semi_space_size = max_semi_space_size;
37 : v8::Isolate::CreateParams create_params;
38 20 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
39 20 : v8::Isolate* isolate = v8::Isolate::New(create_params);
40 20 : return isolate;
41 : }
42 :
43 : Page* FindLastPageInNewSpace(std::vector<Handle<FixedArray>>& handles) {
44 15 : for (auto rit = handles.rbegin(); rit != handles.rend(); ++rit) {
45 15 : Page* candidate = Page::FromAddress((*rit)->address());
46 30 : if (candidate->InNewSpace()) return candidate;
47 : }
48 : return nullptr;
49 : }
50 :
51 : } // namespace
52 :
53 23723 : UNINITIALIZED_TEST(PagePromotion_NewToOld) {
54 5 : if (!i::FLAG_incremental_marking) return;
55 5 : if (!i::FLAG_page_promotion) return;
56 : ManualGCScope manual_gc_scope;
57 :
58 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
59 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
60 : {
61 : v8::Isolate::Scope isolate_scope(isolate);
62 10 : v8::HandleScope handle_scope(isolate);
63 10 : v8::Context::New(isolate)->Enter();
64 35 : Heap* heap = i_isolate->heap();
65 :
66 : std::vector<Handle<FixedArray>> handles;
67 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
68 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
69 10 : CHECK_GT(handles.size(), 0u);
70 5 : Page* const to_be_promoted_page = FindLastPageInNewSpace(handles);
71 5 : CHECK_NOT_NULL(to_be_promoted_page);
72 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
73 : // To perform a sanity check on live bytes we need to mark the heap.
74 5 : heap::SimulateIncrementalMarking(heap, true);
75 : // Sanity check that the page meets the requirements for promotion.
76 : const int threshold_bytes =
77 5 : FLAG_page_promotion_threshold * Page::kAllocatableMemory / 100;
78 5 : CHECK_GE(heap->incremental_marking()->marking_state()->live_bytes(
79 : to_be_promoted_page),
80 : threshold_bytes);
81 :
82 : // Actual checks: The page is in new space first, but is moved to old space
83 : // during a full GC.
84 10 : CHECK(heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
85 5 : CHECK(!heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
86 5 : heap::GcAndSweep(heap, OLD_SPACE);
87 5 : CHECK(!heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
88 5 : CHECK(heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
89 : }
90 5 : isolate->Dispose();
91 : }
92 :
93 23723 : UNINITIALIZED_TEST(PagePromotion_NewToNew) {
94 10 : if (!i::FLAG_page_promotion) return;
95 :
96 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
97 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
98 : {
99 : v8::Isolate::Scope isolate_scope(isolate);
100 10 : v8::HandleScope handle_scope(isolate);
101 10 : v8::Context::New(isolate)->Enter();
102 20 : Heap* heap = i_isolate->heap();
103 :
104 : std::vector<Handle<FixedArray>> handles;
105 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
106 10 : CHECK_GT(handles.size(), 0u);
107 : // Last object in handles should definitely be on a page that does not
108 : // contain the age mark, thus qualifying for moving.
109 5 : Handle<FixedArray> last_object = handles.back();
110 5 : Page* to_be_promoted_page = Page::FromAddress(last_object->address());
111 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
112 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
113 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
114 5 : heap::GcAndSweep(heap, OLD_SPACE);
115 10 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
116 10 : CHECK(to_be_promoted_page->Contains(last_object->address()));
117 : }
118 5 : isolate->Dispose();
119 : }
120 :
121 23723 : UNINITIALIZED_TEST(PagePromotion_NewToNewJSArrayBuffer) {
122 10 : if (!i::FLAG_page_promotion) return;
123 :
124 : // Test makes sure JSArrayBuffer backing stores are still tracked after
125 : // new-to-new promotion.
126 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
127 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
128 : {
129 : v8::Isolate::Scope isolate_scope(isolate);
130 10 : v8::HandleScope handle_scope(isolate);
131 10 : v8::Context::New(isolate)->Enter();
132 30 : Heap* heap = i_isolate->heap();
133 :
134 : // Fill the current page which potentially contains the age mark.
135 5 : heap::FillCurrentPage(heap->new_space());
136 : // Allocate a buffer we would like to check against.
137 : Handle<JSArrayBuffer> buffer =
138 5 : i_isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared);
139 5 : CHECK(JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, 100));
140 : std::vector<Handle<FixedArray>> handles;
141 : // Simulate a full space, filling the interesting page with live objects.
142 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
143 10 : CHECK_GT(handles.size(), 0u);
144 : // First object in handles should be on the same page as the allocated
145 : // JSArrayBuffer.
146 5 : Handle<FixedArray> first_object = handles.front();
147 5 : Page* to_be_promoted_page = Page::FromAddress(first_object->address());
148 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
149 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
150 10 : CHECK(to_be_promoted_page->Contains(buffer->address()));
151 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
152 10 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
153 5 : heap::GcAndSweep(heap, OLD_SPACE);
154 10 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
155 10 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
156 10 : CHECK(to_be_promoted_page->Contains(first_object->address()));
157 10 : CHECK(to_be_promoted_page->Contains(buffer->address()));
158 5 : CHECK(ArrayBufferTracker::IsTracked(*buffer));
159 : }
160 5 : isolate->Dispose();
161 : }
162 :
163 23723 : UNINITIALIZED_HEAP_TEST(Regress658718) {
164 10 : if (!i::FLAG_page_promotion) return;
165 :
166 5 : v8::Isolate* isolate = NewIsolateForPagePromotion(4, 8);
167 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
168 : {
169 : v8::Isolate::Scope isolate_scope(isolate);
170 10 : v8::HandleScope handle_scope(isolate);
171 10 : v8::Context::New(isolate)->Enter();
172 45 : Heap* heap = i_isolate->heap();
173 5 : heap->delay_sweeper_tasks_for_testing_ = true;
174 5 : heap->new_space()->Grow();
175 : {
176 5 : v8::HandleScope inner_handle_scope(isolate);
177 : std::vector<Handle<FixedArray>> handles;
178 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
179 10 : CHECK_GT(handles.size(), 0u);
180 : // Last object in handles should definitely be on a page that does not
181 : // contain the age mark, thus qualifying for moving.
182 5 : Handle<FixedArray> last_object = handles.back();
183 5 : Page* to_be_promoted_page = Page::FromAddress(last_object->address());
184 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
185 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
186 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
187 5 : heap->CollectGarbage(OLD_SPACE, i::GarbageCollectionReason::kTesting);
188 10 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
189 15 : CHECK(to_be_promoted_page->Contains(last_object->address()));
190 : }
191 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
192 5 : heap->new_space()->Shrink();
193 5 : heap->memory_allocator()->unmapper()->WaitUntilCompleted();
194 5 : heap->delay_sweeper_tasks_for_testing_ = false;
195 5 : heap->mark_compact_collector()->sweeper().StartSweeperTasks();
196 5 : heap->mark_compact_collector()->EnsureSweepingCompleted();
197 : }
198 5 : isolate->Dispose();
199 : }
200 :
201 : } // namespace heap
202 : } // namespace internal
203 71154 : } // namespace v8
|