OLD | NEW |
| (Empty) |
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 "test/cctest/cctest.h" | |
7 #include "test/cctest/heap/heap-utils.h" | |
8 | |
9 namespace { | |
10 | |
11 typedef i::LocalArrayBufferTracker LocalTracker; | |
12 | |
13 bool IsTracked(i::JSArrayBuffer* buf) { | |
14 return i::ArrayBufferTracker::IsTracked(buf); | |
15 } | |
16 | |
17 } // namespace | |
18 | |
19 namespace v8 { | |
20 namespace internal { | |
21 | |
22 // The following tests make sure that JSArrayBuffer tracking works expected when | |
23 // moving the objects through various spaces during GC phases. | |
24 | |
25 TEST(ArrayBuffer_OnlyMC) { | |
26 CcTest::InitializeVM(); | |
27 LocalContext env; | |
28 v8::Isolate* isolate = env->GetIsolate(); | |
29 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
30 | |
31 JSArrayBuffer* raw_ab = nullptr; | |
32 { | |
33 v8::HandleScope handle_scope(isolate); | |
34 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
35 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
36 CHECK(IsTracked(*buf)); | |
37 heap::GcAndSweep(heap, OLD_SPACE); | |
38 CHECK(IsTracked(*buf)); | |
39 heap::GcAndSweep(heap, OLD_SPACE); | |
40 CHECK(IsTracked(*buf)); | |
41 raw_ab = *buf; | |
42 // Prohibit page from being released. | |
43 Page::FromAddress(buf->address())->MarkNeverEvacuate(); | |
44 } | |
45 // 2 GCs are needed because we promote to old space as live, meaning that | |
46 // we will survive one GC. | |
47 heap::GcAndSweep(heap, OLD_SPACE); | |
48 heap::GcAndSweep(heap, OLD_SPACE); | |
49 CHECK(!IsTracked(raw_ab)); | |
50 } | |
51 | |
52 TEST(ArrayBuffer_OnlyScavenge) { | |
53 CcTest::InitializeVM(); | |
54 LocalContext env; | |
55 v8::Isolate* isolate = env->GetIsolate(); | |
56 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
57 | |
58 JSArrayBuffer* raw_ab = nullptr; | |
59 { | |
60 v8::HandleScope handle_scope(isolate); | |
61 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
62 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
63 CHECK(IsTracked(*buf)); | |
64 heap::GcAndSweep(heap, NEW_SPACE); | |
65 CHECK(IsTracked(*buf)); | |
66 heap::GcAndSweep(heap, NEW_SPACE); | |
67 CHECK(IsTracked(*buf)); | |
68 heap::GcAndSweep(heap, NEW_SPACE); | |
69 CHECK(IsTracked(*buf)); | |
70 raw_ab = *buf; | |
71 // Prohibit page from being released. | |
72 Page::FromAddress(buf->address())->MarkNeverEvacuate(); | |
73 } | |
74 // 2 GCs are needed because we promote to old space as live, meaning that | |
75 // we will survive one GC. | |
76 heap::GcAndSweep(heap, OLD_SPACE); | |
77 heap::GcAndSweep(heap, OLD_SPACE); | |
78 CHECK(!IsTracked(raw_ab)); | |
79 } | |
80 | |
81 TEST(ArrayBuffer_ScavengeAndMC) { | |
82 CcTest::InitializeVM(); | |
83 LocalContext env; | |
84 v8::Isolate* isolate = env->GetIsolate(); | |
85 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
86 | |
87 JSArrayBuffer* raw_ab = nullptr; | |
88 { | |
89 v8::HandleScope handle_scope(isolate); | |
90 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
91 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
92 CHECK(IsTracked(*buf)); | |
93 heap::GcAndSweep(heap, NEW_SPACE); | |
94 CHECK(IsTracked(*buf)); | |
95 heap::GcAndSweep(heap, NEW_SPACE); | |
96 CHECK(IsTracked(*buf)); | |
97 heap::GcAndSweep(heap, OLD_SPACE); | |
98 CHECK(IsTracked(*buf)); | |
99 heap::GcAndSweep(heap, NEW_SPACE); | |
100 CHECK(IsTracked(*buf)); | |
101 raw_ab = *buf; | |
102 // Prohibit page from being released. | |
103 Page::FromAddress(buf->address())->MarkNeverEvacuate(); | |
104 } | |
105 // 2 GCs are needed because we promote to old space as live, meaning that | |
106 // we will survive one GC. | |
107 heap::GcAndSweep(heap, OLD_SPACE); | |
108 heap::GcAndSweep(heap, OLD_SPACE); | |
109 CHECK(!IsTracked(raw_ab)); | |
110 } | |
111 | |
112 TEST(ArrayBuffer_Compaction) { | |
113 FLAG_manual_evacuation_candidates_selection = true; | |
114 CcTest::InitializeVM(); | |
115 LocalContext env; | |
116 v8::Isolate* isolate = env->GetIsolate(); | |
117 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
118 heap::AbandonCurrentlyFreeMemory(heap->old_space()); | |
119 | |
120 v8::HandleScope handle_scope(isolate); | |
121 Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::New(isolate, 100); | |
122 Handle<JSArrayBuffer> buf1 = v8::Utils::OpenHandle(*ab1); | |
123 CHECK(IsTracked(*buf1)); | |
124 heap::GcAndSweep(heap, NEW_SPACE); | |
125 heap::GcAndSweep(heap, NEW_SPACE); | |
126 | |
127 Page* page_before_gc = Page::FromAddress(buf1->address()); | |
128 page_before_gc->SetFlag(MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING); | |
129 CHECK(IsTracked(*buf1)); | |
130 | |
131 heap->CollectAllGarbage(); | |
132 | |
133 Page* page_after_gc = Page::FromAddress(buf1->address()); | |
134 CHECK(IsTracked(*buf1)); | |
135 | |
136 CHECK_NE(page_before_gc, page_after_gc); | |
137 } | |
138 | |
139 TEST(ArrayBuffer_UnregisterDuringSweep) { | |
140 // Regular pages in old space (without compaction) are processed concurrently | |
141 // in the sweeper. If we happen to unregister a buffer (either explicitly, or | |
142 // implicitly through e.g. |Externalize|) we need to sync with the sweeper | |
143 // task. | |
144 // | |
145 // Note: This test will will only fail on TSAN configurations. | |
146 | |
147 // Disable verify-heap since it forces sweeping to be completed in the | |
148 // epilogue of the GC. | |
149 #ifdef VERIFY_HEAP | |
150 i::FLAG_verify_heap = false; | |
151 #endif // VERIFY_HEAP | |
152 | |
153 CcTest::InitializeVM(); | |
154 LocalContext env; | |
155 v8::Isolate* isolate = env->GetIsolate(); | |
156 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
157 { | |
158 v8::HandleScope handle_scope(isolate); | |
159 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
160 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
161 | |
162 { | |
163 v8::HandleScope handle_scope(isolate); | |
164 // Allocate another buffer on the same page to force processing a | |
165 // non-empty set of buffers in the last GC. | |
166 Local<v8::ArrayBuffer> ab2 = v8::ArrayBuffer::New(isolate, 100); | |
167 Handle<JSArrayBuffer> buf2 = v8::Utils::OpenHandle(*ab2); | |
168 CHECK(IsTracked(*buf)); | |
169 CHECK(IsTracked(*buf)); | |
170 heap::GcAndSweep(heap, NEW_SPACE); | |
171 CHECK(IsTracked(*buf)); | |
172 CHECK(IsTracked(*buf)); | |
173 heap::GcAndSweep(heap, NEW_SPACE); | |
174 CHECK(IsTracked(*buf)); | |
175 CHECK(IsTracked(*buf2)); | |
176 } | |
177 | |
178 heap->CollectGarbage(OLD_SPACE); | |
179 // |Externalize| will cause the buffer to be |Unregister|ed. Without | |
180 // barriers and proper synchronization this will trigger a data race on | |
181 // TSAN. | |
182 v8::ArrayBuffer::Contents contents = ab->Externalize(); | |
183 heap->isolate()->array_buffer_allocator()->Free(contents.Data(), | |
184 contents.ByteLength()); | |
185 } | |
186 } | |
187 | |
188 TEST(ArrayBuffer_NonLivePromotion) { | |
189 // The test verifies that the marking state is preserved when promoting | |
190 // a buffer to old space. | |
191 CcTest::InitializeVM(); | |
192 LocalContext env; | |
193 v8::Isolate* isolate = env->GetIsolate(); | |
194 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
195 | |
196 JSArrayBuffer* raw_ab = nullptr; | |
197 { | |
198 v8::HandleScope handle_scope(isolate); | |
199 Handle<FixedArray> root = | |
200 heap->isolate()->factory()->NewFixedArray(1, TENURED); | |
201 { | |
202 v8::HandleScope handle_scope(isolate); | |
203 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
204 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
205 root->set(0, *buf); // Buffer that should not be promoted as live. | |
206 } | |
207 heap::SimulateIncrementalMarking(heap, false); | |
208 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
209 heap::GcAndSweep(heap, NEW_SPACE); | |
210 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
211 heap::GcAndSweep(heap, NEW_SPACE); | |
212 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
213 raw_ab = JSArrayBuffer::cast(root->get(0)); | |
214 root->set(0, heap->undefined_value()); | |
215 heap::SimulateIncrementalMarking(heap, true); | |
216 // Prohibit page from being released. | |
217 Page::FromAddress(raw_ab->address())->MarkNeverEvacuate(); | |
218 heap::GcAndSweep(heap, OLD_SPACE); | |
219 CHECK(!IsTracked(raw_ab)); | |
220 } | |
221 } | |
222 | |
223 TEST(ArrayBuffer_LivePromotion) { | |
224 // The test verifies that the marking state is preserved when promoting | |
225 // a buffer to old space. | |
226 CcTest::InitializeVM(); | |
227 LocalContext env; | |
228 v8::Isolate* isolate = env->GetIsolate(); | |
229 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
230 | |
231 JSArrayBuffer* raw_ab = nullptr; | |
232 { | |
233 v8::HandleScope handle_scope(isolate); | |
234 Handle<FixedArray> root = | |
235 heap->isolate()->factory()->NewFixedArray(1, TENURED); | |
236 { | |
237 v8::HandleScope handle_scope(isolate); | |
238 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
239 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
240 root->set(0, *buf); // Buffer that should be promoted as live. | |
241 } | |
242 heap::SimulateIncrementalMarking(heap, true); | |
243 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
244 heap::GcAndSweep(heap, NEW_SPACE); | |
245 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
246 heap::GcAndSweep(heap, NEW_SPACE); | |
247 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
248 raw_ab = JSArrayBuffer::cast(root->get(0)); | |
249 root->set(0, heap->undefined_value()); | |
250 // Prohibit page from being released. | |
251 Page::FromAddress(raw_ab->address())->MarkNeverEvacuate(); | |
252 heap::GcAndSweep(heap, OLD_SPACE); | |
253 CHECK(IsTracked(raw_ab)); | |
254 } | |
255 } | |
256 | |
257 TEST(ArrayBuffer_SemiSpaceCopyThenPagePromotion) { | |
258 // The test verifies that the marking state is preserved across semispace | |
259 // copy. | |
260 CcTest::InitializeVM(); | |
261 LocalContext env; | |
262 v8::Isolate* isolate = env->GetIsolate(); | |
263 Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap(); | |
264 | |
265 heap::SealCurrentObjects(heap); | |
266 { | |
267 v8::HandleScope handle_scope(isolate); | |
268 Handle<FixedArray> root = | |
269 heap->isolate()->factory()->NewFixedArray(1, TENURED); | |
270 { | |
271 v8::HandleScope handle_scope(isolate); | |
272 Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 100); | |
273 Handle<JSArrayBuffer> buf = v8::Utils::OpenHandle(*ab); | |
274 root->set(0, *buf); // Buffer that should be promoted as live. | |
275 Page::FromAddress(buf->address())->MarkNeverEvacuate(); | |
276 } | |
277 std::vector<Handle<FixedArray>> handles; | |
278 // Make the whole page transition from new->old, getting the buffers | |
279 // processed in the sweeper (relying on marking information) instead of | |
280 // processing during newspace evacuation. | |
281 heap::FillCurrentPage(heap->new_space(), &handles); | |
282 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
283 heap::GcAndSweep(heap, NEW_SPACE); | |
284 heap::SimulateIncrementalMarking(heap, true); | |
285 heap::GcAndSweep(heap, OLD_SPACE); | |
286 CHECK(IsTracked(JSArrayBuffer::cast(root->get(0)))); | |
287 } | |
288 } | |
289 | |
290 } // namespace internal | |
291 } // namespace v8 | |
OLD | NEW |