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