Line data Source code
1 : // Copyright 2012 the V8 project authors. All rights reserved.
2 : // Redistribution and use in source and binary forms, with or without
3 : // modification, are permitted provided that the following conditions are
4 : // met:
5 : //
6 : // * Redistributions of source code must retain the above copyright
7 : // notice, this list of conditions and the following disclaimer.
8 : // * Redistributions in binary form must reproduce the above
9 : // copyright notice, this list of conditions and the following
10 : // disclaimer in the documentation and/or other materials provided
11 : // with the distribution.
12 : // * Neither the name of Google Inc. nor the names of its
13 : // contributors may be used to endorse or promote products derived
14 : // from this software without specific prior written permission.
15 : //
16 : // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 : // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 : // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 : // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 : // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 : // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 : // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 : // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 :
28 : #include <stdlib.h>
29 :
30 : #ifdef __linux__
31 : #include <errno.h>
32 : #include <fcntl.h>
33 : #include <sys/stat.h>
34 : #include <sys/types.h>
35 : #include <unistd.h>
36 : #endif
37 :
38 : #include <utility>
39 :
40 : #include "src/v8.h"
41 :
42 : #include "src/global-handles.h"
43 : #include "src/heap/mark-compact-inl.h"
44 : #include "src/heap/mark-compact.h"
45 : #include "src/objects-inl.h"
46 : #include "test/cctest/cctest.h"
47 : #include "test/cctest/heap/heap-tester.h"
48 : #include "test/cctest/heap/heap-utils.h"
49 :
50 : namespace v8 {
51 : namespace internal {
52 : namespace heap {
53 :
54 28342 : TEST(Promotion) {
55 5 : CcTest::InitializeVM();
56 : Isolate* isolate = CcTest::i_isolate();
57 : {
58 5 : v8::HandleScope sc(CcTest::isolate());
59 5 : Heap* heap = isolate->heap();
60 :
61 5 : heap::SealCurrentObjects(heap);
62 :
63 5 : int array_length = heap::FixedArrayLenFromSize(kMaxRegularHeapObjectSize);
64 5 : Handle<FixedArray> array = isolate->factory()->NewFixedArray(array_length);
65 :
66 : // Array should be in the new space.
67 5 : CHECK(heap->InSpace(*array, NEW_SPACE));
68 5 : CcTest::CollectAllGarbage();
69 5 : CcTest::CollectAllGarbage();
70 5 : CHECK(heap->InSpace(*array, OLD_SPACE));
71 : }
72 5 : }
73 :
74 28342 : HEAP_TEST(NoPromotion) {
75 : // Page promotion allows pages to be moved to old space even in the case of
76 : // OOM scenarios.
77 5 : FLAG_page_promotion = false;
78 :
79 5 : CcTest::InitializeVM();
80 : Isolate* isolate = CcTest::i_isolate();
81 : {
82 5 : v8::HandleScope sc(CcTest::isolate());
83 5 : Heap* heap = isolate->heap();
84 :
85 5 : heap::SealCurrentObjects(heap);
86 :
87 5 : int array_length = heap::FixedArrayLenFromSize(kMaxRegularHeapObjectSize);
88 5 : Handle<FixedArray> array = isolate->factory()->NewFixedArray(array_length);
89 :
90 : heap->set_force_oom(true);
91 : // Array should be in the new space.
92 5 : CHECK(heap->InSpace(*array, NEW_SPACE));
93 5 : CcTest::CollectAllGarbage();
94 5 : CcTest::CollectAllGarbage();
95 5 : CHECK(heap->InSpace(*array, NEW_SPACE));
96 : }
97 5 : }
98 :
99 : // This is the same as Factory::NewMap, except it doesn't retry on
100 : // allocation failure.
101 620585 : AllocationResult HeapTester::AllocateMapForTest(Isolate* isolate) {
102 620585 : Heap* heap = isolate->heap();
103 620585 : HeapObject obj;
104 620585 : AllocationResult alloc = heap->AllocateRaw(Map::kSize, MAP_SPACE);
105 620585 : if (!alloc.To(&obj)) return alloc;
106 : obj->set_map_after_allocation(ReadOnlyRoots(heap).meta_map(),
107 620580 : SKIP_WRITE_BARRIER);
108 : return isolate->factory()->InitializeMap(Map::cast(obj), JS_OBJECT_TYPE,
109 : JSObject::kHeaderSize,
110 620580 : TERMINAL_FAST_ELEMENTS_KIND, 0);
111 : }
112 :
113 : // This is the same as Factory::NewFixedArray, except it doesn't retry
114 : // on allocation failure.
115 6420 : AllocationResult HeapTester::AllocateFixedArrayForTest(
116 : Heap* heap, int length, PretenureFlag pretenure) {
117 : DCHECK(length >= 0 && length <= FixedArray::kMaxLength);
118 : int size = FixedArray::SizeFor(length);
119 6420 : AllocationSpace space = heap->SelectSpace(pretenure);
120 6420 : HeapObject obj;
121 : {
122 6420 : AllocationResult result = heap->AllocateRaw(size, space);
123 6420 : if (!result.To(&obj)) return result;
124 : }
125 : obj->set_map_after_allocation(ReadOnlyRoots(heap).fixed_array_map(),
126 6405 : SKIP_WRITE_BARRIER);
127 : FixedArray array = FixedArray::cast(obj);
128 : array->set_length(length);
129 : MemsetTagged(array->data_start(), ReadOnlyRoots(heap).undefined_value(),
130 : length);
131 6405 : return array;
132 : }
133 :
134 28342 : HEAP_TEST(MarkCompactCollector) {
135 5 : FLAG_incremental_marking = false;
136 5 : FLAG_retain_maps_for_n_gc = 0;
137 5 : CcTest::InitializeVM();
138 : Isolate* isolate = CcTest::i_isolate();
139 5 : Heap* heap = CcTest::heap();
140 : Factory* factory = isolate->factory();
141 :
142 5 : v8::HandleScope sc(CcTest::isolate());
143 10 : Handle<JSGlobalObject> global(isolate->context()->global_object(), isolate);
144 :
145 : // call mark-compact when heap is empty
146 5 : CcTest::CollectGarbage(OLD_SPACE);
147 :
148 : // keep allocating garbage in new space until it fails
149 : const int arraysize = 100;
150 : AllocationResult allocation;
151 6355 : do {
152 6355 : allocation = AllocateFixedArrayForTest(heap, arraysize, NOT_TENURED);
153 : } while (!allocation.IsRetry());
154 5 : CcTest::CollectGarbage(NEW_SPACE);
155 5 : AllocateFixedArrayForTest(heap, arraysize, NOT_TENURED).ToObjectChecked();
156 :
157 : // keep allocating maps until it fails
158 620580 : do {
159 620580 : allocation = AllocateMapForTest(isolate);
160 : } while (!allocation.IsRetry());
161 5 : CcTest::CollectGarbage(MAP_SPACE);
162 5 : AllocateMapForTest(isolate).ToObjectChecked();
163 :
164 : { HandleScope scope(isolate);
165 : // allocate a garbage
166 5 : Handle<String> func_name = factory->InternalizeUtf8String("theFunction");
167 5 : Handle<JSFunction> function = factory->NewFunctionForTest(func_name);
168 : Object::SetProperty(isolate, global, func_name, function,
169 5 : LanguageMode::kSloppy)
170 10 : .Check();
171 :
172 5 : factory->NewJSObject(function);
173 : }
174 :
175 5 : CcTest::CollectGarbage(OLD_SPACE);
176 :
177 : { HandleScope scope(isolate);
178 5 : Handle<String> func_name = factory->InternalizeUtf8String("theFunction");
179 10 : CHECK(Just(true) == JSReceiver::HasOwnProperty(global, func_name));
180 : Handle<Object> func_value =
181 10 : Object::GetProperty(isolate, global, func_name).ToHandleChecked();
182 10 : CHECK(func_value->IsJSFunction());
183 5 : Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
184 5 : Handle<JSObject> obj = factory->NewJSObject(function);
185 :
186 5 : Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
187 5 : Object::SetProperty(isolate, global, obj_name, obj, LanguageMode::kSloppy)
188 10 : .Check();
189 5 : Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
190 : Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
191 : Object::SetProperty(isolate, obj, prop_name, twenty_three,
192 5 : LanguageMode::kSloppy)
193 10 : .Check();
194 : }
195 :
196 5 : CcTest::CollectGarbage(OLD_SPACE);
197 :
198 : { HandleScope scope(isolate);
199 5 : Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
200 10 : CHECK(Just(true) == JSReceiver::HasOwnProperty(global, obj_name));
201 : Handle<Object> object =
202 10 : Object::GetProperty(isolate, global, obj_name).ToHandleChecked();
203 10 : CHECK(object->IsJSObject());
204 5 : Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
205 20 : CHECK_EQ(*Object::GetProperty(isolate, object, prop_name).ToHandleChecked(),
206 : Smi::FromInt(23));
207 5 : }
208 5 : }
209 :
210 :
211 : // TODO(1600): compaction of map space is temporary removed from GC.
212 : #if 0
213 : static Handle<Map> CreateMap(Isolate* isolate) {
214 : return isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
215 : }
216 :
217 :
218 : TEST(MapCompact) {
219 : FLAG_max_map_space_pages = 16;
220 : CcTest::InitializeVM();
221 : Isolate* isolate = CcTest::i_isolate();
222 : Factory* factory = isolate->factory();
223 :
224 : {
225 : v8::HandleScope sc;
226 : // keep allocating maps while pointers are still encodable and thus
227 : // mark compact is permitted.
228 : Handle<JSObject> root = factory->NewJSObjectFromMap(CreateMap());
229 : do {
230 : Handle<Map> map = CreateMap();
231 : map->set_prototype(*root);
232 : root = factory->NewJSObjectFromMap(map);
233 : } while (CcTest::heap()->map_space()->MapPointersEncodable());
234 : }
235 : // Now, as we don't have any handles to just allocated maps, we should
236 : // be able to trigger map compaction.
237 : // To give an additional chance to fail, try to force compaction which
238 : // should be impossible right now.
239 : CcTest::CollectAllGarbage(Heap::kForceCompactionMask);
240 : // And now map pointers should be encodable again.
241 : CHECK(CcTest::heap()->map_space()->MapPointersEncodable());
242 : }
243 : #endif
244 :
245 : #if defined(__has_feature)
246 : #if __has_feature(address_sanitizer)
247 : #define V8_WITH_ASAN 1
248 : #endif
249 : #endif
250 :
251 : // Here is a memory use test that uses /proc, and is therefore Linux-only. We
252 : // do not care how much memory the simulator uses, since it is only there for
253 : // debugging purposes. Testing with ASAN doesn't make sense, either.
254 : #if defined(__linux__) && !defined(USE_SIMULATOR) && !defined(V8_WITH_ASAN)
255 :
256 :
257 17736 : static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) {
258 17736 : char* end_address = buffer + *position;
259 17736 : uintptr_t result = strtoul(buffer + *position, &end_address, base);
260 17736 : CHECK(result != ULONG_MAX || errno != ERANGE);
261 17736 : CHECK(end_address > buffer + *position);
262 17736 : *position = end_address - buffer;
263 17736 : return result;
264 : }
265 :
266 :
267 : // The memory use computed this way is not entirely accurate and depends on
268 : // the way malloc allocates memory. That's why the memory use may seem to
269 : // increase even though the sum of the allocated object sizes decreases. It
270 : // also means that the memory use depends on the kernel and stdlib.
271 55 : static intptr_t MemoryInUse() {
272 : intptr_t memory_use = 0;
273 :
274 : int fd = open("/proc/self/maps", O_RDONLY);
275 55 : if (fd < 0) return -1;
276 :
277 : const int kBufSize = 20000;
278 : char buffer[kBufSize];
279 : ssize_t length = read(fd, buffer, kBufSize);
280 : intptr_t line_start = 0;
281 55 : CHECK_LT(length, kBufSize); // Make the buffer bigger.
282 55 : CHECK_GT(length, 0); // We have to find some data in the file.
283 5967 : while (line_start < length) {
284 5912 : if (buffer[line_start] == '\n') {
285 2956 : line_start++;
286 2956 : continue;
287 : }
288 2956 : intptr_t position = line_start;
289 2956 : uintptr_t start = ReadLong(buffer, &position, 16);
290 2956 : CHECK_EQ(buffer[position++], '-');
291 2956 : uintptr_t end = ReadLong(buffer, &position, 16);
292 2956 : CHECK_EQ(buffer[position++], ' ');
293 2956 : CHECK(buffer[position] == '-' || buffer[position] == 'r');
294 2956 : bool read_permission = (buffer[position++] == 'r');
295 2956 : CHECK(buffer[position] == '-' || buffer[position] == 'w');
296 2956 : bool write_permission = (buffer[position++] == 'w');
297 2956 : CHECK(buffer[position] == '-' || buffer[position] == 'x');
298 2956 : bool execute_permission = (buffer[position++] == 'x');
299 2956 : CHECK(buffer[position] == 's' || buffer[position] == 'p');
300 2956 : bool private_mapping = (buffer[position++] == 'p');
301 2956 : CHECK_EQ(buffer[position++], ' ');
302 2956 : uintptr_t offset = ReadLong(buffer, &position, 16);
303 : USE(offset);
304 2956 : CHECK_EQ(buffer[position++], ' ');
305 2956 : uintptr_t major = ReadLong(buffer, &position, 16);
306 : USE(major);
307 2956 : CHECK_EQ(buffer[position++], ':');
308 2956 : uintptr_t minor = ReadLong(buffer, &position, 16);
309 : USE(minor);
310 2956 : CHECK_EQ(buffer[position++], ' ');
311 2956 : uintptr_t inode = ReadLong(buffer, &position, 10);
312 2956 : while (position < length && buffer[position] != '\n') position++;
313 2956 : if ((read_permission || write_permission || execute_permission) &&
314 2047 : private_mapping && inode == 0) {
315 1134 : memory_use += (end - start);
316 : }
317 :
318 : line_start = position;
319 : }
320 55 : close(fd);
321 55 : return memory_use;
322 : }
323 :
324 :
325 55 : intptr_t ShortLivingIsolate() {
326 : v8::Isolate::CreateParams create_params;
327 55 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
328 55 : v8::Isolate* isolate = v8::Isolate::New(create_params);
329 : { v8::Isolate::Scope isolate_scope(isolate);
330 55 : v8::Locker lock(isolate);
331 110 : v8::HandleScope handle_scope(isolate);
332 55 : v8::Local<v8::Context> context = v8::Context::New(isolate);
333 55 : CHECK(!context.IsEmpty());
334 : }
335 55 : isolate->Dispose();
336 55 : return MemoryInUse();
337 : }
338 :
339 :
340 28342 : TEST(RegressJoinThreadsOnIsolateDeinit) {
341 5 : intptr_t size_limit = ShortLivingIsolate() * 2;
342 55 : for (int i = 0; i < 10; i++) {
343 50 : CHECK_GT(size_limit, ShortLivingIsolate());
344 : }
345 5 : }
346 :
347 28342 : TEST(Regress5829) {
348 5 : CcTest::InitializeVM();
349 : Isolate* isolate = CcTest::i_isolate();
350 5 : v8::HandleScope sc(CcTest::isolate());
351 15 : Heap* heap = isolate->heap();
352 5 : heap::SealCurrentObjects(heap);
353 5 : i::MarkCompactCollector* collector = heap->mark_compact_collector();
354 : i::IncrementalMarking* marking = heap->incremental_marking();
355 5 : if (collector->sweeping_in_progress()) {
356 0 : collector->EnsureSweepingCompleted();
357 : }
358 5 : CHECK(marking->IsMarking() || marking->IsStopped());
359 5 : if (marking->IsStopped()) {
360 : heap->StartIncrementalMarking(i::Heap::kNoGCFlags,
361 5 : i::GarbageCollectionReason::kTesting);
362 : }
363 5 : CHECK(marking->IsMarking());
364 : marking->StartBlackAllocationForTesting();
365 5 : Handle<FixedArray> array = isolate->factory()->NewFixedArray(10, TENURED);
366 5 : Address old_end = array->address() + array->Size();
367 : // Right trim the array without clearing the mark bits.
368 : array->set_length(9);
369 : heap->CreateFillerObjectAt(old_end - kTaggedSize, kTaggedSize,
370 5 : ClearRecordedSlots::kNo);
371 5 : heap->old_space()->FreeLinearAllocationArea();
372 5 : Page* page = Page::FromAddress(array->address());
373 : IncrementalMarking::MarkingState* marking_state = marking->marking_state();
374 5 : for (auto object_and_size :
375 5 : LiveObjectRange<kGreyObjects>(page, marking_state->bitmap(page))) {
376 0 : CHECK(!object_and_size.first->IsFiller());
377 5 : }
378 5 : }
379 :
380 : #endif // __linux__ and !USE_SIMULATOR
381 :
382 : } // namespace heap
383 : } // namespace internal
384 85011 : } // namespace v8
|