Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(505)

Side by Side Diff: test/cctest/heap/test-compaction.cc

Issue 1511933002: [cctest] Add tests for aborting compaction of pages (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Workaround FLAG_concurrent_sweeping not being respected after heap initialization Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « test/cctest/heap/heap-tester.h ('k') | test/cctest/heap/utils-inl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 "test/cctest/cctest.h"
6 #include "test/cctest/heap/heap-tester.h"
7 #include "test/cctest/heap/utils-inl.h"
8
9 namespace v8 {
10 namespace internal {
11
12 static std::vector<Handle<FixedArray>> FillUpFirstOldSpacePage(Heap* heap) {
13 // This functions assumes that old space top is still on the first page
14 heap->old_space()->EmptyAllocationInfo();
15 int free_on_first_page = static_cast<int>(heap->old_space()->Available());
16 return CreatePadding(heap, free_on_first_page, TENURED);
17 }
18
19
20 static void CheckInvariantsOfAbortedPage(Page* page) {
21 // Check invariants:
22 // 1) Markbits are cleared
23 // 2) The page is not marked as evacuation candidate anymore
24 // 3) The page is not marked as aborted compaction anymore.
25 CHECK(page->markbits()->IsClean());
26 CHECK(!page->IsEvacuationCandidate());
27 CHECK(!page->IsFlagSet(Page::COMPACTION_WAS_ABORTED));
28 }
29
30
31 HEAP_TEST(CompactionFullAbortedPage) {
32 // Test the scenario where we reach OOM during compaction and the whole page
33 // is aborted.
34
35 FLAG_manual_evacuation_candidates_selection = true;
36 CcTest::InitializeVM();
37 Isolate* isolate = CcTest::i_isolate();
38 Heap* heap = isolate->heap();
39 // Disable concurrent sweeping to ensure memory is in an expected state, i.e.,
40 // we can reach the state of a half aborted page. We cannot just set
41 // {FLAG_concurrent_sweeping} because the flag is cached in heap, which is
42 // initialized earlier.
43 heap->concurrent_sweeping_enabled_ = false;
44 {
45 HandleScope scope1(isolate);
46 // Fill up the first page since it cannot be evacuated.
47 auto first_page_handles = FillUpFirstOldSpacePage(heap);
48
49 {
50 HandleScope scope2(isolate);
51 heap->old_space()->EmptyAllocationInfo();
52 auto second_page_handles =
53 CreatePadding(heap, Page::kAllocatableMemory, TENURED);
54 Page* to_be_aborted_page =
55 Page::FromAddress(second_page_handles.front()->address());
56 to_be_aborted_page->SetFlag(
57 MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
58 heap->set_force_oom(true);
59 heap->CollectAllGarbage();
60
61 // Check that all handles still point to the same page, i.e., compaction
62 // has been aborted on the page.
63 for (Handle<FixedArray> object : second_page_handles) {
64 CHECK_EQ(to_be_aborted_page, Page::FromAddress(object->address()));
65 }
66 CheckInvariantsOfAbortedPage(to_be_aborted_page);
67 }
68 }
69 }
70
71
72 HEAP_TEST(CompactionPartiallyAbortedPage) {
73 // Test the scenario where we reach OOM during compaction and parts of the
74 // page have already been migrated to a new one.
75
76 FLAG_manual_evacuation_candidates_selection = true;
77
78 const int object_size = 128 * KB;
79
80 CcTest::InitializeVM();
81 Isolate* isolate = CcTest::i_isolate();
82 Heap* heap = isolate->heap();
83 // Disable concurrent sweeping to ensure memory is in an expected state, i.e.,
84 // we can reach the state of a half aborted page. We cannot just set
85 // {FLAG_concurrent_sweeping} because the flag is cached in heap, which is
86 // initialized earlier.
87 heap->concurrent_sweeping_enabled_ = false;
88 {
89 HandleScope scope1(isolate);
90 // Fill up the first page since it cannot be evacuated.
91 auto first_page_handles = FillUpFirstOldSpacePage(heap);
92
93 {
94 HandleScope scope2(isolate);
95 // Fill the second page with objects of size {object_size} (last one is
96 // properly adjusted).
97 heap->old_space()->EmptyAllocationInfo();
98 auto second_page_handles =
99 CreatePadding(heap, Page::kAllocatableMemory, TENURED, object_size);
100 // Mark the second page for evacuation.
101 Page* to_be_aborted_page =
102 Page::FromAddress(second_page_handles.front()->address());
103 to_be_aborted_page->SetFlag(
104 MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
105
106 {
107 // Add a third page that is filled with {num_objects} objects of size
108 // {object_size}.
109 HandleScope scope3(isolate);
110 heap->old_space()->EmptyAllocationInfo();
111 const int num_objects = 3;
112 std::vector<Handle<FixedArray>> third_page_handles = CreatePadding(
113 heap, object_size * num_objects, TENURED, object_size);
114 Page* third_page =
115 Page::FromAddress(third_page_handles.front()->address());
116 heap->set_force_oom(true);
117 heap->CollectAllGarbage();
118
119 bool migration_aborted = false;
120 for (Handle<FixedArray> object : second_page_handles) {
121 // Once compaction has been aborted, all following objects still have
122 // to be on the initial page.
123 CHECK(!migration_aborted ||
124 (Page::FromAddress(object->address()) == to_be_aborted_page));
125 if (Page::FromAddress(object->address()) == to_be_aborted_page) {
126 // This object has not been migrated.
127 migration_aborted = true;
128 } else {
129 CHECK_EQ(Page::FromAddress(object->address()), third_page);
130 }
131 }
132 // Check that we actually created a scenario with a partially aborted
133 // page.
134 CHECK(migration_aborted);
135 CheckInvariantsOfAbortedPage(to_be_aborted_page);
136 }
137 }
138 }
139 }
140
141
142 HEAP_TEST(CompactionPartiallyAbortedPageIntraAbortedPointers) {
143 // Test the scenario where we reach OOM during compaction and parts of the
144 // page have already been migrated to a new one. Objects on the aborted page
145 // are linked together. This test makes sure that intra-aborted page pointers
146 // get properly updated.
147
148 FLAG_manual_evacuation_candidates_selection = true;
149
150 const int object_size = 128 * KB;
151
152 CcTest::InitializeVM();
153 Isolate* isolate = CcTest::i_isolate();
154 Heap* heap = isolate->heap();
155 // Disable concurrent sweeping to ensure memory is in an expected state, i.e.,
156 // we can reach the state of a half aborted page. We cannot just set
157 // {FLAG_concurrent_sweeping} because the flag is cached in heap, which is
158 // initialized earlier.
159 heap->concurrent_sweeping_enabled_ = false;
160 {
161 HandleScope scope1(isolate);
162 // Fill up the first page since it cannot be evacuated.
163 auto first_page_handles = FillUpFirstOldSpacePage(heap);
164
165 Page* to_be_aborted_page = nullptr;
166 {
167 HandleScope temporary_scope(isolate);
168 // Fill the second page with objects of size {object_size} (last one is
169 // properly adjusted).
170 heap->old_space()->EmptyAllocationInfo();
171 const int free_on_second_page = Page::kAllocatableMemory;
172 std::vector<Handle<FixedArray>> second_page_handles =
173 CreatePadding(heap, free_on_second_page, TENURED, object_size);
174 // Mark the second page for evacuation.
175 to_be_aborted_page =
176 Page::FromAddress(second_page_handles.front()->address());
177 to_be_aborted_page->SetFlag(
178 MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
179
180 for (size_t i = second_page_handles.size() - 1; i > 0; i--) {
181 second_page_handles[i]->set(0, *second_page_handles[i - 1]);
182 }
183 first_page_handles.front()->set(0, *second_page_handles.back());
184 }
185
186 {
187 // Add a third page that is filled with {num_objects} objects of size
188 // {object_size}.
189 HandleScope scope3(isolate);
190 heap->old_space()->EmptyAllocationInfo();
191 const int num_objects = 2;
192 int used_memory = object_size * num_objects;
193 std::vector<Handle<FixedArray>> third_page_handles =
194 CreatePadding(heap, used_memory, TENURED, object_size);
195 Page* third_page =
196 Page::FromAddress(third_page_handles.front()->address());
197 heap->set_force_oom(true);
198 heap->CollectAllGarbage();
199
200 // The following check makes sure that we compacted "some" objects, while
201 // leaving others in place.
202 bool in_place = true;
203 Handle<FixedArray> current = first_page_handles.front();
204 while (current->get(0) != heap->undefined_value()) {
205 current = Handle<FixedArray>(FixedArray::cast(current->get(0)));
206 CHECK(current->IsFixedArray());
207 if (Page::FromAddress(current->address()) != to_be_aborted_page) {
208 in_place = false;
209 }
210 bool on_aborted_page =
211 Page::FromAddress(current->address()) == to_be_aborted_page;
212 bool on_third_page =
213 Page::FromAddress(current->address()) == third_page;
214 CHECK((in_place && on_aborted_page) || (!in_place && on_third_page));
215 }
216 // Check that we at least migrated one object, as otherwise the test would
217 // not trigger.
218 CHECK(!in_place);
219
220 CheckInvariantsOfAbortedPage(to_be_aborted_page);
221 }
222 }
223 }
224
225
226 HEAP_TEST(CompactionPartiallyAbortedPageWithStoreBufferEntries) {
227 // Test the scenario where we reach OOM during compaction and parts of the
228 // page have already been migrated to a new one. Objects on the aborted page
229 // are linked together and the very first object on the aborted page points
230 // into new space. The test verifies that the store buffer entries are
231 // properly cleared and rebuilt after aborting a page. Failing to do so can
232 // result in other objects being allocated in the free space where their
233 // payload looks like a valid new space pointer.
234
235 FLAG_manual_evacuation_candidates_selection = true;
236
237 const int object_size = 128 * KB;
238
239 CcTest::InitializeVM();
240 Isolate* isolate = CcTest::i_isolate();
241 Heap* heap = isolate->heap();
242 // Disable concurrent sweeping to ensure memory is in an expected state, i.e.,
243 // we can reach the state of a half aborted page. We cannot just set
244 // {FLAG_concurrent_sweeping} because the flag is cached in heap, which is
245 // initialized earlier.
246 heap->concurrent_sweeping_enabled_ = false;
247 {
248 HandleScope scope1(isolate);
249 // Fill up the first page since it cannot be evacuated.
250 auto first_page_handles = FillUpFirstOldSpacePage(heap);
251
252 Page* to_be_aborted_page = nullptr;
253 {
254 HandleScope temporary_scope(isolate);
255 // Fill the second page with objects of size {object_size} (last one is
256 // properly adjusted).
257 heap->old_space()->EmptyAllocationInfo();
258 auto second_page_handles =
259 CreatePadding(heap, Page::kAllocatableMemory, TENURED, object_size);
260 // Mark the second page for evacuation.
261 to_be_aborted_page =
262 Page::FromAddress(second_page_handles.front()->address());
263 to_be_aborted_page->SetFlag(
264 MemoryChunk::FORCE_EVACUATION_CANDIDATE_FOR_TESTING);
265
266 for (size_t i = second_page_handles.size() - 1; i > 0; i--) {
267 second_page_handles[i]->set(0, *second_page_handles[i - 1]);
268 }
269 first_page_handles.front()->set(0, *second_page_handles.back());
270 Handle<FixedArray> new_space_array =
271 isolate->factory()->NewFixedArray(1, NOT_TENURED);
272 CHECK(heap->InNewSpace(*new_space_array));
273 second_page_handles.front()->set(1, *new_space_array);
274 }
275
276 {
277 // Add a third page that is filled with {num_objects} objects of size
278 // {object_size}.
279 HandleScope scope3(isolate);
280 heap->old_space()->EmptyAllocationInfo();
281 const int num_objects = 2;
282 int used_memory = object_size * num_objects;
283 std::vector<Handle<FixedArray>> third_page_handles =
284 CreatePadding(heap, used_memory, TENURED, object_size);
285 Page* third_page =
286 Page::FromAddress(third_page_handles.front()->address());
287 heap->set_force_oom(true);
288 heap->CollectAllGarbage();
289
290 // The following check makes sure that we compacted "some" objects, while
291 // leaving others in place.
292 bool in_place = true;
293 Handle<FixedArray> current = first_page_handles.front();
294 while (current->get(0) != heap->undefined_value()) {
295 current = Handle<FixedArray>(FixedArray::cast(current->get(0)));
296 CHECK(!heap->InNewSpace(*current));
297 CHECK(current->IsFixedArray());
298 if (Page::FromAddress(current->address()) != to_be_aborted_page) {
299 in_place = false;
300 }
301 bool on_aborted_page =
302 Page::FromAddress(current->address()) == to_be_aborted_page;
303 bool on_third_page =
304 Page::FromAddress(current->address()) == third_page;
305 CHECK((in_place && on_aborted_page) || (!in_place && on_third_page));
306 }
307 // Check that we at least migrated one object, as otherwise the test would
308 // not trigger.
309 CHECK(!in_place);
310
311 CheckInvariantsOfAbortedPage(to_be_aborted_page);
312
313 // Allocate a new object in new space.
314 Handle<FixedArray> holder =
315 isolate->factory()->NewFixedArray(10, NOT_TENURED);
316 // Create a broken address that looks like a tagged pointer to a new space
317 // object.
318 Address broken_address = holder->address() + 2 * kPointerSize + 1;
319 // Convert it to a vector to create a string from it.
320 Vector<const uint8_t> string_to_broken_addresss(
321 reinterpret_cast<const uint8_t*>(&broken_address), 8);
322
323 Handle<String> string;
324 do {
325 // We know that the interesting slot will be on the aborted page and
326 // hence we allocate until we get our string on the aborted page.
327 // We used slot 1 in the fixed size array which corresponds to the
328 // the first word in the string. Since the first object definitely
329 // migrated we can just allocate until we hit the aborted page.
330 string = isolate->factory()
331 ->NewStringFromOneByte(string_to_broken_addresss, TENURED)
332 .ToHandleChecked();
333 } while (Page::FromAddress(string->address()) != to_be_aborted_page);
334
335 // If store buffer entries are not properly filtered/reset for aborted
336 // pages we have now a broken address at an object slot in old space and
337 // the following scavenge will crash.
338 heap->CollectGarbage(NEW_SPACE);
339 }
340 }
341 }
342
343 } // namespace internal
344 } // namespace v8
OLDNEW
« no previous file with comments | « test/cctest/heap/heap-tester.h ('k') | test/cctest/heap/utils-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698