Line data Source code
1 : // Copyright 2017 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 <stdlib.h>
6 :
7 : #include "src/v8.h"
8 :
9 : #include "src/heap/heap-inl.h"
10 : #include "src/heap/heap.h"
11 : #include "src/heap/invalidated-slots-inl.h"
12 : #include "src/heap/invalidated-slots.h"
13 : #include "test/cctest/cctest.h"
14 : #include "test/cctest/heap/heap-tester.h"
15 : #include "test/cctest/heap/heap-utils.h"
16 :
17 : namespace v8 {
18 : namespace internal {
19 : namespace heap {
20 :
21 30 : Page* HeapTester::AllocateByteArraysOnPage(
22 : Heap* heap, std::vector<ByteArray>* byte_arrays) {
23 60 : PauseAllocationObserversScope pause_observers(heap);
24 : const int kLength = 256 - ByteArray::kHeaderSize;
25 : const int kSize = ByteArray::SizeFor(kLength);
26 : CHECK_EQ(kSize, 256);
27 : Isolate* isolate = heap->isolate();
28 : PagedSpace* old_space = heap->old_space();
29 : Page* page;
30 : // Fill a page with byte arrays.
31 : {
32 : AlwaysAllocateScope always_allocate(isolate);
33 30 : heap::SimulateFullSpace(old_space);
34 30 : ByteArray byte_array;
35 60 : CHECK(AllocateByteArrayForTest(heap, kLength, AllocationType::kOld)
36 : .To(&byte_array));
37 30 : byte_arrays->push_back(byte_array);
38 : page = Page::FromHeapObject(byte_array);
39 30 : size_t n = page->area_size() / kSize;
40 61290 : for (size_t i = 1; i < n; i++) {
41 61260 : CHECK(AllocateByteArrayForTest(heap, kLength, AllocationType::kOld)
42 : .To(&byte_array));
43 30630 : byte_arrays->push_back(byte_array);
44 30630 : CHECK_EQ(page, Page::FromHeapObject(byte_array));
45 : }
46 : }
47 30 : CHECK_NULL(page->invalidated_slots());
48 30 : return page;
49 : }
50 :
51 26661 : HEAP_TEST(InvalidatedSlotsNoInvalidatedRanges) {
52 5 : CcTest::InitializeVM();
53 5 : Heap* heap = CcTest::heap();
54 : std::vector<ByteArray> byte_arrays;
55 5 : Page* page = AllocateByteArraysOnPage(heap, &byte_arrays);
56 5 : InvalidatedSlotsFilter filter(page);
57 5115 : for (ByteArray byte_array : byte_arrays) {
58 5110 : Address start = byte_array->address() + ByteArray::kHeaderSize;
59 5110 : Address end = byte_array->address() + byte_array->Size();
60 311710 : for (Address addr = start; addr < end; addr += kTaggedSize) {
61 153300 : CHECK(filter.IsValid(addr));
62 : }
63 : }
64 5 : }
65 :
66 26661 : HEAP_TEST(InvalidatedSlotsSomeInvalidatedRanges) {
67 5 : CcTest::InitializeVM();
68 5 : Heap* heap = CcTest::heap();
69 : std::vector<ByteArray> byte_arrays;
70 5 : Page* page = AllocateByteArraysOnPage(heap, &byte_arrays);
71 : // Register every second byte arrays as invalidated.
72 5115 : for (size_t i = 0; i < byte_arrays.size(); i += 2) {
73 2555 : page->RegisterObjectWithInvalidatedSlots(byte_arrays[i],
74 2555 : byte_arrays[i]->Size());
75 : }
76 5 : InvalidatedSlotsFilter filter(page);
77 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
78 5110 : ByteArray byte_array = byte_arrays[i];
79 5110 : Address start = byte_array->address() + ByteArray::kHeaderSize;
80 5110 : Address end = byte_array->address() + byte_array->Size();
81 311710 : for (Address addr = start; addr < end; addr += kTaggedSize) {
82 153300 : if (i % 2 == 0) {
83 76650 : CHECK(!filter.IsValid(addr));
84 : } else {
85 76650 : CHECK(filter.IsValid(addr));
86 : }
87 : }
88 : }
89 5 : }
90 :
91 26661 : HEAP_TEST(InvalidatedSlotsAllInvalidatedRanges) {
92 5 : CcTest::InitializeVM();
93 5 : Heap* heap = CcTest::heap();
94 : std::vector<ByteArray> byte_arrays;
95 5 : Page* page = AllocateByteArraysOnPage(heap, &byte_arrays);
96 : // Register the all byte arrays as invalidated.
97 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
98 5110 : page->RegisterObjectWithInvalidatedSlots(byte_arrays[i],
99 5110 : byte_arrays[i]->Size());
100 : }
101 5 : InvalidatedSlotsFilter filter(page);
102 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
103 5110 : ByteArray byte_array = byte_arrays[i];
104 5110 : Address start = byte_array->address() + ByteArray::kHeaderSize;
105 5110 : Address end = byte_array->address() + byte_array->Size();
106 311710 : for (Address addr = start; addr < end; addr += kTaggedSize) {
107 153300 : CHECK(!filter.IsValid(addr));
108 : }
109 : }
110 5 : }
111 :
112 26661 : HEAP_TEST(InvalidatedSlotsAfterTrimming) {
113 : ManualGCScope manual_gc_scope;
114 5 : CcTest::InitializeVM();
115 5 : Heap* heap = CcTest::heap();
116 : std::vector<ByteArray> byte_arrays;
117 5 : Page* page = AllocateByteArraysOnPage(heap, &byte_arrays);
118 : // Register the all byte arrays as invalidated.
119 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
120 5110 : page->RegisterObjectWithInvalidatedSlots(byte_arrays[i],
121 5110 : byte_arrays[i]->Size());
122 : }
123 : // Trim byte arrays and check that the slots outside the byte arrays are
124 : // considered invalid if the old space page was swept.
125 5 : InvalidatedSlotsFilter filter(page);
126 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
127 5110 : ByteArray byte_array = byte_arrays[i];
128 5110 : Address start = byte_array->address() + ByteArray::kHeaderSize;
129 5110 : Address end = byte_array->address() + byte_array->Size();
130 5110 : heap->RightTrimFixedArray(byte_array, byte_array->length());
131 311710 : for (Address addr = start; addr < end; addr += kTaggedSize) {
132 153300 : CHECK_EQ(filter.IsValid(addr), page->SweepingDone());
133 : }
134 : }
135 5 : }
136 :
137 26661 : HEAP_TEST(InvalidatedSlotsEvacuationCandidate) {
138 : ManualGCScope manual_gc_scope;
139 5 : CcTest::InitializeVM();
140 5 : Heap* heap = CcTest::heap();
141 : std::vector<ByteArray> byte_arrays;
142 5 : Page* page = AllocateByteArraysOnPage(heap, &byte_arrays);
143 5 : page->MarkEvacuationCandidate();
144 : // Register the all byte arrays as invalidated.
145 : // This should be no-op because the page is marked as evacuation
146 : // candidate.
147 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
148 5110 : page->RegisterObjectWithInvalidatedSlots(byte_arrays[i],
149 5110 : byte_arrays[i]->Size());
150 : }
151 : // All slots must still be valid.
152 5 : InvalidatedSlotsFilter filter(page);
153 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
154 5110 : ByteArray byte_array = byte_arrays[i];
155 5110 : Address start = byte_array->address() + ByteArray::kHeaderSize;
156 5110 : Address end = byte_array->address() + byte_array->Size();
157 311710 : for (Address addr = start; addr < end; addr += kTaggedSize) {
158 153300 : CHECK(filter.IsValid(addr));
159 : }
160 : }
161 5 : }
162 :
163 26661 : HEAP_TEST(InvalidatedSlotsResetObjectRegression) {
164 5 : CcTest::InitializeVM();
165 5 : Heap* heap = CcTest::heap();
166 : std::vector<ByteArray> byte_arrays;
167 5 : Page* page = AllocateByteArraysOnPage(heap, &byte_arrays);
168 : // Ensure that the first array has smaller size then the rest.
169 5 : heap->RightTrimFixedArray(byte_arrays[0], byte_arrays[0]->length() - 8);
170 : // Register the all byte arrays as invalidated.
171 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
172 5110 : page->RegisterObjectWithInvalidatedSlots(byte_arrays[i],
173 5110 : byte_arrays[i]->Size());
174 : }
175 : // All slots must still be invalid.
176 5 : InvalidatedSlotsFilter filter(page);
177 10225 : for (size_t i = 0; i < byte_arrays.size(); i++) {
178 5110 : ByteArray byte_array = byte_arrays[i];
179 5110 : Address start = byte_array->address() + ByteArray::kHeaderSize;
180 5110 : Address end = byte_array->address() + byte_array->Size();
181 311420 : for (Address addr = start; addr < end; addr += kTaggedSize) {
182 153155 : CHECK(!filter.IsValid(addr));
183 : }
184 : }
185 5 : }
186 :
187 55 : Handle<FixedArray> AllocateArrayOnFreshPage(Isolate* isolate,
188 : PagedSpace* old_space, int length) {
189 : AlwaysAllocateScope always_allocate(isolate);
190 55 : heap::SimulateFullSpace(old_space);
191 110 : return isolate->factory()->NewFixedArray(length, AllocationType::kOld);
192 : }
193 :
194 20 : Handle<FixedArray> AllocateArrayOnEvacuationCandidate(Isolate* isolate,
195 : PagedSpace* old_space,
196 : int length) {
197 : Handle<FixedArray> object =
198 20 : AllocateArrayOnFreshPage(isolate, old_space, length);
199 20 : heap::ForceEvacuationCandidate(Page::FromHeapObject(*object));
200 20 : return object;
201 : }
202 :
203 26661 : HEAP_TEST(InvalidatedSlotsRightTrimFixedArray) {
204 5 : FLAG_manual_evacuation_candidates_selection = true;
205 5 : FLAG_parallel_compaction = false;
206 : ManualGCScope manual_gc_scope;
207 5 : CcTest::InitializeVM();
208 : Isolate* isolate = CcTest::i_isolate();
209 : Factory* factory = isolate->factory();
210 5 : Heap* heap = CcTest::heap();
211 : HandleScope scope(isolate);
212 : PagedSpace* old_space = heap->old_space();
213 : // Allocate a dummy page to be swept be the sweeper during evacuation.
214 5 : AllocateArrayOnFreshPage(isolate, old_space, 1);
215 : Handle<FixedArray> evacuated =
216 5 : AllocateArrayOnEvacuationCandidate(isolate, old_space, 1);
217 5 : Handle<FixedArray> trimmed = AllocateArrayOnFreshPage(isolate, old_space, 10);
218 5 : heap::SimulateIncrementalMarking(heap);
219 95 : for (int i = 1; i < trimmed->length(); i++) {
220 45 : trimmed->set(i, *evacuated);
221 : }
222 : {
223 : HandleScope scope(isolate);
224 5 : Handle<HeapObject> dead = factory->NewFixedArray(1);
225 95 : for (int i = 1; i < trimmed->length(); i++) {
226 45 : trimmed->set(i, *dead);
227 : }
228 5 : heap->RightTrimFixedArray(*trimmed, trimmed->length() - 1);
229 : }
230 5 : CcTest::CollectGarbage(i::NEW_SPACE);
231 5 : CcTest::CollectGarbage(i::OLD_SPACE);
232 5 : }
233 :
234 26661 : HEAP_TEST(InvalidatedSlotsRightTrimLargeFixedArray) {
235 5 : FLAG_manual_evacuation_candidates_selection = true;
236 5 : FLAG_parallel_compaction = false;
237 : ManualGCScope manual_gc_scope;
238 5 : CcTest::InitializeVM();
239 : Isolate* isolate = CcTest::i_isolate();
240 : Factory* factory = isolate->factory();
241 5 : Heap* heap = CcTest::heap();
242 : HandleScope scope(isolate);
243 : PagedSpace* old_space = heap->old_space();
244 : // Allocate a dummy page to be swept be the sweeper during evacuation.
245 5 : AllocateArrayOnFreshPage(isolate, old_space, 1);
246 : Handle<FixedArray> evacuated =
247 5 : AllocateArrayOnEvacuationCandidate(isolate, old_space, 1);
248 : Handle<FixedArray> trimmed;
249 : {
250 : AlwaysAllocateScope always_allocate(isolate);
251 : trimmed = factory->NewFixedArray(
252 5 : kMaxRegularHeapObjectSize / kTaggedSize + 100, AllocationType::kOld);
253 : DCHECK(MemoryChunk::FromHeapObject(*trimmed)->InLargeObjectSpace());
254 : }
255 5 : heap::SimulateIncrementalMarking(heap);
256 164835 : for (int i = 1; i < trimmed->length(); i++) {
257 82415 : trimmed->set(i, *evacuated);
258 : }
259 : {
260 : HandleScope scope(isolate);
261 5 : Handle<HeapObject> dead = factory->NewFixedArray(1);
262 164835 : for (int i = 1; i < trimmed->length(); i++) {
263 82415 : trimmed->set(i, *dead);
264 : }
265 5 : heap->RightTrimFixedArray(*trimmed, trimmed->length() - 1);
266 : }
267 5 : CcTest::CollectGarbage(i::NEW_SPACE);
268 5 : CcTest::CollectGarbage(i::OLD_SPACE);
269 5 : }
270 :
271 26661 : HEAP_TEST(InvalidatedSlotsLeftTrimFixedArray) {
272 5 : FLAG_manual_evacuation_candidates_selection = true;
273 5 : FLAG_parallel_compaction = false;
274 : ManualGCScope manual_gc_scope;
275 5 : CcTest::InitializeVM();
276 : Isolate* isolate = CcTest::i_isolate();
277 : Factory* factory = isolate->factory();
278 5 : Heap* heap = CcTest::heap();
279 : HandleScope scope(isolate);
280 : PagedSpace* old_space = heap->old_space();
281 : // Allocate a dummy page to be swept be the sweeper during evacuation.
282 5 : AllocateArrayOnFreshPage(isolate, old_space, 1);
283 : Handle<FixedArray> evacuated =
284 5 : AllocateArrayOnEvacuationCandidate(isolate, old_space, 1);
285 5 : Handle<FixedArray> trimmed = AllocateArrayOnFreshPage(isolate, old_space, 10);
286 5 : heap::SimulateIncrementalMarking(heap);
287 145 : for (int i = 0; i + 1 < trimmed->length(); i++) {
288 45 : trimmed->set(i, *evacuated);
289 : }
290 : {
291 : HandleScope scope(isolate);
292 5 : Handle<HeapObject> dead = factory->NewFixedArray(1);
293 95 : for (int i = 1; i < trimmed->length(); i++) {
294 45 : trimmed->set(i, *dead);
295 : }
296 5 : heap->LeftTrimFixedArray(*trimmed, trimmed->length() - 1);
297 : }
298 5 : CcTest::CollectGarbage(i::NEW_SPACE);
299 5 : CcTest::CollectGarbage(i::OLD_SPACE);
300 5 : }
301 :
302 26661 : HEAP_TEST(InvalidatedSlotsFastToSlow) {
303 5 : FLAG_manual_evacuation_candidates_selection = true;
304 5 : FLAG_parallel_compaction = false;
305 : ManualGCScope manual_gc_scope;
306 5 : CcTest::InitializeVM();
307 : Isolate* isolate = CcTest::i_isolate();
308 : Factory* factory = isolate->factory();
309 5 : Heap* heap = CcTest::heap();
310 : PagedSpace* old_space = heap->old_space();
311 :
312 : HandleScope scope(isolate);
313 :
314 5 : Handle<String> name = factory->InternalizeUtf8String("TestObject");
315 5 : Handle<String> prop_name1 = factory->InternalizeUtf8String("prop1");
316 5 : Handle<String> prop_name2 = factory->InternalizeUtf8String("prop2");
317 5 : Handle<String> prop_name3 = factory->InternalizeUtf8String("prop3");
318 : // Allocate a dummy page to be swept be the sweeper during evacuation.
319 5 : AllocateArrayOnFreshPage(isolate, old_space, 1);
320 : Handle<FixedArray> evacuated =
321 5 : AllocateArrayOnEvacuationCandidate(isolate, old_space, 1);
322 : // Allocate a dummy page to ensure that the JSObject is allocated on
323 : // a fresh page.
324 5 : AllocateArrayOnFreshPage(isolate, old_space, 1);
325 : Handle<JSObject> obj;
326 : {
327 : AlwaysAllocateScope always_allocate(isolate);
328 5 : Handle<JSFunction> function = factory->NewFunctionForTest(name);
329 : function->shared()->set_expected_nof_properties(3);
330 5 : obj = factory->NewJSObject(function, AllocationType::kOld);
331 : }
332 : // Start incremental marking.
333 5 : heap::SimulateIncrementalMarking(heap);
334 : // Set properties to point to the evacuation candidate.
335 10 : Object::SetProperty(isolate, obj, prop_name1, evacuated).Check();
336 10 : Object::SetProperty(isolate, obj, prop_name2, evacuated).Check();
337 10 : Object::SetProperty(isolate, obj, prop_name3, evacuated).Check();
338 :
339 : {
340 : HandleScope scope(isolate);
341 5 : Handle<HeapObject> dead = factory->NewFixedArray(1);
342 10 : Object::SetProperty(isolate, obj, prop_name1, dead).Check();
343 10 : Object::SetProperty(isolate, obj, prop_name2, dead).Check();
344 10 : Object::SetProperty(isolate, obj, prop_name3, dead).Check();
345 : Handle<Map> map(obj->map(), isolate);
346 : Handle<Map> normalized_map =
347 5 : Map::Normalize(isolate, map, CLEAR_INOBJECT_PROPERTIES, "testing");
348 5 : JSObject::MigrateToMap(obj, normalized_map);
349 : }
350 5 : CcTest::CollectGarbage(i::NEW_SPACE);
351 5 : CcTest::CollectGarbage(i::OLD_SPACE);
352 5 : }
353 :
354 : } // namespace heap
355 : } // namespace internal
356 79968 : } // namespace v8
|