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 5 : 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 10 : if (candidate->InNewSpace()) return candidate;
51 : }
52 : return nullptr;
53 : }
54 :
55 : } // namespace
56 :
57 28342 : 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 35 : Heap* heap = i_isolate->heap();
69 :
70 : std::vector<Handle<FixedArray>> handles;
71 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
72 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
73 10 : CHECK_GT(handles.size(), 0u);
74 5 : Page* const to_be_promoted_page = FindLastPageInNewSpace(handles);
75 5 : CHECK_NOT_NULL(to_be_promoted_page);
76 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
77 : // To perform a sanity check on live bytes we need to mark the heap.
78 5 : heap::SimulateIncrementalMarking(heap, true);
79 : // Sanity check that the page meets the requirements for promotion.
80 : const int threshold_bytes = static_cast<int>(
81 10 : FLAG_page_promotion_threshold *
82 10 : MemoryChunkLayout::AllocatableMemoryInDataPage() / 100);
83 5 : CHECK_GE(heap->incremental_marking()->marking_state()->live_bytes(
84 : to_be_promoted_page),
85 : threshold_bytes);
86 :
87 : // Actual checks: The page is in new space first, but is moved to old space
88 : // during a full GC.
89 10 : CHECK(heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
90 5 : CHECK(!heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
91 5 : heap::GcAndSweep(heap, OLD_SPACE);
92 5 : CHECK(!heap->new_space()->ContainsSlow(to_be_promoted_page->address()));
93 5 : CHECK(heap->old_space()->ContainsSlow(to_be_promoted_page->address()));
94 : }
95 5 : isolate->Dispose();
96 : }
97 :
98 28342 : UNINITIALIZED_TEST(PagePromotion_NewToNew) {
99 10 : if (!i::FLAG_page_promotion) return;
100 :
101 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
102 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
103 : {
104 : v8::Isolate::Scope isolate_scope(isolate);
105 10 : v8::HandleScope handle_scope(isolate);
106 10 : v8::Context::New(isolate)->Enter();
107 25 : Heap* heap = i_isolate->heap();
108 :
109 : std::vector<Handle<FixedArray>> handles;
110 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
111 10 : CHECK_GT(handles.size(), 0u);
112 : // Last object in handles should definitely be on a page that does not
113 : // contain the age mark, thus qualifying for moving.
114 5 : Handle<FixedArray> last_object = handles.back();
115 : Page* to_be_promoted_page = Page::FromHeapObject(*last_object);
116 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
117 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
118 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
119 5 : heap::GcAndSweep(heap, OLD_SPACE);
120 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
121 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
122 : }
123 5 : isolate->Dispose();
124 : }
125 :
126 28342 : UNINITIALIZED_TEST(PagePromotion_NewToNewJSArrayBuffer) {
127 10 : if (!i::FLAG_page_promotion) return;
128 :
129 : // Test makes sure JSArrayBuffer backing stores are still tracked after
130 : // new-to-new promotion.
131 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
132 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
133 : {
134 : v8::Isolate::Scope isolate_scope(isolate);
135 10 : v8::HandleScope handle_scope(isolate);
136 10 : v8::Context::New(isolate)->Enter();
137 35 : Heap* heap = i_isolate->heap();
138 :
139 : // Fill the current page which potentially contains the age mark.
140 5 : heap::FillCurrentPage(heap->new_space());
141 : // Allocate a buffer we would like to check against.
142 : Handle<JSArrayBuffer> buffer =
143 5 : i_isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared);
144 5 : CHECK(JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, 100));
145 : std::vector<Handle<FixedArray>> handles;
146 : // Simulate a full space, filling the interesting page with live objects.
147 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
148 10 : CHECK_GT(handles.size(), 0u);
149 : // First object in handles should be on the same page as the allocated
150 : // JSArrayBuffer.
151 5 : Handle<FixedArray> first_object = handles.front();
152 : Page* to_be_promoted_page = Page::FromHeapObject(*first_object);
153 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
154 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
155 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
156 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
157 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
158 5 : heap::GcAndSweep(heap, OLD_SPACE);
159 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
160 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
161 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
162 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
163 5 : CHECK(ArrayBufferTracker::IsTracked(*buffer));
164 : }
165 5 : isolate->Dispose();
166 : }
167 :
168 28342 : UNINITIALIZED_TEST(PagePromotion_NewToOldJSArrayBuffer) {
169 10 : if (!i::FLAG_page_promotion) return;
170 :
171 : // Test makes sure JSArrayBuffer backing stores are still tracked after
172 : // new-to-old promotion.
173 5 : v8::Isolate* isolate = NewIsolateForPagePromotion();
174 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
175 : {
176 : v8::Isolate::Scope isolate_scope(isolate);
177 10 : v8::HandleScope handle_scope(isolate);
178 10 : v8::Context::New(isolate)->Enter();
179 35 : Heap* heap = i_isolate->heap();
180 :
181 : // Fill the current page which potentially contains the age mark.
182 5 : heap::FillCurrentPage(heap->new_space());
183 : // Allocate a buffer we would like to check against.
184 : Handle<JSArrayBuffer> buffer =
185 5 : i_isolate->factory()->NewJSArrayBuffer(SharedFlag::kNotShared);
186 5 : CHECK(JSArrayBuffer::SetupAllocatingData(buffer, i_isolate, 100));
187 : std::vector<Handle<FixedArray>> handles;
188 : // Simulate a full space, filling the interesting page with live objects.
189 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
190 10 : CHECK_GT(handles.size(), 0u);
191 : // First object in handles should be on the same page as the allocated
192 : // JSArrayBuffer.
193 5 : Handle<FixedArray> first_object = handles.front();
194 : Page* to_be_promoted_page = Page::FromHeapObject(*first_object);
195 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
196 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
197 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
198 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(first_object->address()));
199 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(buffer->address()));
200 5 : heap::GcAndSweep(heap, OLD_SPACE);
201 5 : heap::GcAndSweep(heap, OLD_SPACE);
202 5 : CHECK(heap->old_space()->ContainsSlow(first_object->address()));
203 5 : CHECK(heap->old_space()->ContainsSlow(buffer->address()));
204 5 : CHECK(to_be_promoted_page->Contains(first_object->address()));
205 5 : CHECK(to_be_promoted_page->Contains(buffer->address()));
206 5 : CHECK(ArrayBufferTracker::IsTracked(*buffer));
207 : }
208 5 : isolate->Dispose();
209 : }
210 :
211 28342 : UNINITIALIZED_HEAP_TEST(Regress658718) {
212 10 : if (!i::FLAG_page_promotion) return;
213 :
214 5 : v8::Isolate* isolate = NewIsolateForPagePromotion(4, 8);
215 : Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
216 : {
217 : v8::Isolate::Scope isolate_scope(isolate);
218 10 : v8::HandleScope handle_scope(isolate);
219 10 : v8::Context::New(isolate)->Enter();
220 50 : Heap* heap = i_isolate->heap();
221 5 : heap->delay_sweeper_tasks_for_testing_ = true;
222 5 : heap->new_space()->Grow();
223 : {
224 5 : v8::HandleScope inner_handle_scope(isolate);
225 : std::vector<Handle<FixedArray>> handles;
226 5 : heap::SimulateFullSpace(heap->new_space(), &handles);
227 10 : CHECK_GT(handles.size(), 0u);
228 : // Last object in handles should definitely be on a page that does not
229 : // contain the age mark, thus qualifying for moving.
230 5 : Handle<FixedArray> last_object = handles.back();
231 : Page* to_be_promoted_page = Page::FromHeapObject(*last_object);
232 5 : CHECK(!to_be_promoted_page->Contains(heap->new_space()->age_mark()));
233 5 : CHECK(to_be_promoted_page->Contains(last_object->address()));
234 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
235 5 : heap->CollectGarbage(OLD_SPACE, i::GarbageCollectionReason::kTesting);
236 5 : CHECK(heap->new_space()->ToSpaceContainsSlow(last_object->address()));
237 10 : CHECK(to_be_promoted_page->Contains(last_object->address()));
238 : }
239 5 : heap->CollectGarbage(NEW_SPACE, i::GarbageCollectionReason::kTesting);
240 5 : heap->new_space()->Shrink();
241 5 : heap->memory_allocator()->unmapper()->EnsureUnmappingCompleted();
242 5 : heap->delay_sweeper_tasks_for_testing_ = false;
243 5 : heap->mark_compact_collector()->sweeper()->StartSweeperTasks();
244 5 : heap->mark_compact_collector()->EnsureSweepingCompleted();
245 : }
246 5 : isolate->Dispose();
247 : }
248 :
249 : #endif // V8_LITE_MODE
250 :
251 : } // namespace heap
252 : } // namespace internal
253 85011 : } // namespace v8
|