• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IsTracked(i::JSArrayBuffer * buf)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 
TEST(ArrayBuffer_OnlyMC)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 
TEST(ArrayBuffer_OnlyScavenge)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 
TEST(ArrayBuffer_ScavengeAndMC)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 
TEST(ArrayBuffer_Compaction)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 
TEST(ArrayBuffer_UnregisterDuringSweep)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 
TEST(ArrayBuffer_NonLivePromotion)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 
TEST(ArrayBuffer_LivePromotion)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 
TEST(ArrayBuffer_SemiSpaceCopyThenPagePromotion)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 
UNINITIALIZED_TEST(ArrayBuffer_SemiSpaceCopyMultipleTasks)290 UNINITIALIZED_TEST(ArrayBuffer_SemiSpaceCopyMultipleTasks) {
291   if (FLAG_optimize_for_size) return;
292   // Test allocates JSArrayBuffer on different pages before triggering a
293   // full GC that performs the semispace copy. If parallelized, this test
294   // ensures proper synchronization in TSAN configurations.
295   FLAG_min_semi_space_size = 2 * Page::kPageSize / MB;
296   v8::Isolate::CreateParams create_params;
297   create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
298   v8::Isolate* isolate = v8::Isolate::New(create_params);
299   i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
300   {
301     v8::Isolate::Scope isolate_scope(isolate);
302     v8::HandleScope handle_scope(isolate);
303     v8::Context::New(isolate)->Enter();
304     Heap* heap = i_isolate->heap();
305 
306     Local<v8::ArrayBuffer> ab1 = v8::ArrayBuffer::New(isolate, 100);
307     Handle<JSArrayBuffer> buf1 = v8::Utils::OpenHandle(*ab1);
308     heap::FillCurrentPage(heap->new_space());
309     Local<v8::ArrayBuffer> ab2 = v8::ArrayBuffer::New(isolate, 100);
310     Handle<JSArrayBuffer> buf2 = v8::Utils::OpenHandle(*ab2);
311     CHECK_NE(Page::FromAddress(buf1->address()),
312              Page::FromAddress(buf2->address()));
313     heap::GcAndSweep(heap, OLD_SPACE);
314   }
315 }
316 
317 }  // namespace internal
318 }  // namespace v8
319