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