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/heap/array-buffer-tracker.h"
6 : #include "src/heap/factory.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 : // Tests don't work when --optimize-for-size is set.
19 : #ifndef V8_LITE_MODE
20 :
21 : namespace {
22 :
23 25 : v8::Isolate* NewIsolateForPagePromotion(int min_semi_space_size = 8,
24 : int max_semi_space_size = 8) {
25 : // Parallel evacuation messes with fragmentation in a way that objects that
26 : // should be copied in semi space are promoted to old space because of
27 : // fragmentation.
28 25 : FLAG_parallel_compaction = false;
29 25 : FLAG_page_promotion = true;
30 25 : FLAG_page_promotion_threshold = 0;
31 : // Parallel scavenge introduces too much fragmentation.
32 25 : FLAG_parallel_scavenge = false;
33 25 : FLAG_min_semi_space_size = min_semi_space_size;
34 : // We cannot optimize for size as we require a new space with more than one
35 : // page.
36 25 : FLAG_optimize_for_size = false;
37 : // Set max_semi_space_size because it could've been initialized by an
38 : // implication of optimize_for_size.
39 25 : FLAG_max_semi_space_size = max_semi_space_size;
40 : v8::Isolate::CreateParams create_params;
41 25 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
42 25 : v8::Isolate* isolate = v8::Isolate::New(create_params);
43 25 : return isolate;
44 : }
45 :
46 : Page* FindLastPageInNewSpace(std::vector<Handle<FixedArray>>& handles) {
47 5 : for (auto rit = handles.rbegin(); rit != handles.rend(); ++rit) {
48 : // One deref gets the Handle, the second deref gets the FixedArray.
49 : Page* candidate = Page::FromHeapObject(**rit);
50 5 : if (candidate->InNewSpace()) return candidate;
51 : }
52 : return nullptr;
53 : }
54 :
55 : } // namespace
56 :
57 26644 : UNINITIALIZED_TEST(PagePromotion_NewToOld) {
58 5 : if (!i::FLAG_incremental_marking) return;
59 5 : if (!i::FLAG_page_promotion) return;
60 : ManualGCScope manual_gc_scope;
61 :
62 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
63 : i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
64 : {
65 : v8::Isolate::Scope isolate_scope(isolate);
66 10 : v8::HandleScope handle_scope(isolate);
67 10 : 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 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
73 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
74 :
75 : std::vector<Handle<FixedArray>> handles;
76 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
77 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
78 5 : CHECK_GT(handles.size(), 0u);
79 : Page* const to_be_promoted_page = FindLastPageInNewSpace(handles);
80 5 : CHECK_NOT_NULL(to_be_promoted_page);
81 5 : 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 5 : heap::SimulateIncrementalMarking(heap, true);
84 : // Sanity check that the page meets the requirements for promotion.
85 : const int threshold_bytes = static_cast<int>(
86 10 : FLAG_page_promotion_threshold *
87 10 : MemoryChunkLayout::AllocatableMemoryInDataPage() / 100);
88 5 : 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 10 : CHECK(heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
95 5 : CHECK(!heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
96 5 : heap::GcAndSweep(heap, OLD_SPACE);
97 5 : CHECK(!heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
98 5 : CHECK(heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
99 : }
100 5 : isolate->Dispose();
101 : }
102 :
103 26644 : UNINITIALIZED_TEST(PagePromotion_NewToNew) {
104 5 : if (!i::FLAG_page_promotion) return;
105 :
106 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
107 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
108 : {
109 : v8::Isolate::Scope isolate_scope(isolate);
110 10 : v8::HandleScope handle_scope(isolate);
111 10 : v8::Context::New(isolate)->Enter();
112 : Heap* heap = i_isolate->heap();
113 :
114 : std::vector<Handle<FixedArray>> handles;
115 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
116 5 : 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 5 : Handle<FixedArray> last_object = handles.back();
120 : Page* to_be_promoted_page = Page::FromHeapObject(*last_object);
121 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
122 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
123 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
124 5 : heap::GcAndSweep(heap, OLD_SPACE);
125 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
126 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
127 : }
128 5 : isolate->Dispose();
129 : }
130 :
131 26644 : UNINITIALIZED_TEST(PagePromotion_NewToNewJSArrayBuffer) {
132 5 : if (!i::FLAG_page_promotion) return;
133 :
134 : // Test makes sure JSArrayBuffer backing stores are still tracked after
135 : // new-to-new promotion.
136 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
137 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
138 : {
139 : v8::Isolate::Scope isolate_scope(isolate);
140 10 : v8::HandleScope handle_scope(isolate);
141 10 : v8::Context::New(isolate)->Enter();
142 : Heap* heap = i_isolate->heap();
143 :
144 : // Fill the current page which potentially contains the age mark.
145 5 : heap::FillCurrentPage(heap->new_space());
146 : // Allocate a buffer we would like to check against.
147 : Handle<JSArrayBuffer> buffer =
148 5 : i_isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared);
149 5 : CHECK(JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, 100));
150 : std::vector<Handle<FixedArray>> handles;
151 : // Simulate a full space, filling the interesting page with live objects.
152 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
153 5 : CHECK_GT(handles.size(), 0u);
154 : // First object in handles should be on the same page as the allocated
155 : // JSArrayBuffer.
156 5 : Handle<FixedArray> first_object = handles.front();
157 : Page* to_be_promoted_page = Page::FromHeapObject(*first_object);
158 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
159 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
160 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
161 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
162 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
163 5 : heap::GcAndSweep(heap, OLD_SPACE);
164 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
165 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
166 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
167 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
168 5 : CHECK(ArrayBufferTracker::IsTracked(*buffer));
169 : }
170 5 : isolate->Dispose();
171 : }
172 :
173 26644 : UNINITIALIZED_TEST(PagePromotion_NewToOldJSArrayBuffer) {
174 5 : if (!i::FLAG_page_promotion) return;
175 :
176 : // Test makes sure JSArrayBuffer backing stores are still tracked after
177 : // new-to-old promotion.
178 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
179 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
180 : {
181 : v8::Isolate::Scope isolate_scope(isolate);
182 10 : v8::HandleScope handle_scope(isolate);
183 10 : v8::Context::New(isolate)->Enter();
184 : Heap* heap = i_isolate->heap();
185 :
186 : // Fill the current page which potentially contains the age mark.
187 5 : heap::FillCurrentPage(heap->new_space());
188 : // Allocate a buffer we would like to check against.
189 : Handle<JSArrayBuffer> buffer =
190 5 : i_isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared);
191 5 : CHECK(JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, 100));
192 : std::vector<Handle<FixedArray>> handles;
193 : // Simulate a full space, filling the interesting page with live objects.
194 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
195 5 : CHECK_GT(handles.size(), 0u);
196 : // First object in handles should be on the same page as the allocated
197 : // JSArrayBuffer.
198 5 : Handle<FixedArray> first_object = handles.front();
199 : Page* to_be_promoted_page = Page::FromHeapObject(*first_object);
200 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
201 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
202 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
203 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
204 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
205 5 : heap::GcAndSweep(heap, OLD_SPACE);
206 5 : heap::GcAndSweep(heap, OLD_SPACE);
207 5 : CHECK(heap->old_space()->ContainsSlow(first_object->address()));
208 5 : CHECK(heap->old_space()->ContainsSlow(buffer->address()));
209 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
210 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
211 5 : CHECK(ArrayBufferTracker::IsTracked(*buffer));
212 : }
213 5 : isolate->Dispose();
214 : }
215 :
216 26644 : UNINITIALIZED_HEAP_TEST(Regress658718) {
217 5 : if (!i::FLAG_page_promotion) return;
218 :
219 5 : v8::Isolate* isolate = NewIsolateForPagePromotion(4, 8);
220 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
221 : {
222 : v8::Isolate::Scope isolate_scope(isolate);
223 10 : v8::HandleScope handle_scope(isolate);
224 10 : v8::Context::New(isolate)->Enter();
225 : Heap* heap = i_isolate->heap();
226 5 : heap->delay_sweeper_tasks_for_testing_ = true;
227 5 : heap->new_space()->Grow();
228 : {
229 10 : v8::HandleScope inner_handle_scope(isolate);
230 : std::vector<Handle<FixedArray>> handles;
231 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
232 5 : CHECK_GT(handles.size(), 0u);
233 : // Last object in handles should definitely be on a page that does not
234 : // contain the age mark, thus qualifying for moving.
235 5 : Handle<FixedArray> last_object = handles.back();
236 : Page* to_be_promoted_page = Page::FromHeapObject(*last_object);
237 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
238 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
239 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
240 5 : heap->CollectGarbage(OLD_SPACE, i::GarbageCollectionReason::kTesting);
241 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
242 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
243 : }
244 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
245 5 : heap->new_space()->Shrink();
246 5 : heap->memory_allocator()->unmapper()->EnsureUnmappingCompleted();
247 5 : heap->delay_sweeper_tasks_for_testing_ = false;
248 5 : heap->mark_compact_collector()->sweeper()->StartSweeperTasks();
249 5 : heap->mark_compact_collector()->EnsureSweepingCompleted();
250 : }
251 5 : isolate->Dispose();
252 : }
253 :
254 : #endif // V8_LITE_MODE
255 :
256 : } // namespace heap
257 : } // namespace internal
258 79917 : } // namespace v8
|