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 26661 : TEST(Promotion) {
55 5 : CcTest::InitializeVM();
56 : Isolate* isolate = CcTest::i_isolate();
57 : {
58 10 : v8::HandleScope sc(CcTest::isolate());
59 : 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 26661 : 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 10 : v8::HandleScope sc(CcTest::isolate());
83 : 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 603872 : AllocationResult HeapTester::AllocateMapForTest(Isolate* isolate) {
102 : Heap* heap = isolate->heap();
103 : HeapObject obj;
104 603872 : AllocationResult alloc = heap->AllocateRaw(Map::kSize, AllocationType::kMap);
105 603872 : if (!alloc.To(&obj)) return alloc;
106 : obj->set_map_after_allocation(ReadOnlyRoots(heap).meta_map(),
107 : SKIP_WRITE_BARRIER);
108 1207734 : return isolate->factory()->InitializeMap(Map::cast(obj), JS_OBJECT_TYPE,
109 : JSObject::kHeaderSize,
110 603867 : 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 6370 : AllocationResult HeapTester::AllocateFixedArrayForTest(
116 : Heap* heap, int length, AllocationType allocation) {
117 : DCHECK(length >= 0 && length <= FixedArray::kMaxLength);
118 : int size = FixedArray::SizeFor(length);
119 : HeapObject obj;
120 : {
121 6370 : AllocationResult result = heap->AllocateRaw(size, allocation);
122 6370 : if (!result.To(&obj)) return result;
123 : }
124 : obj->set_map_after_allocation(ReadOnlyRoots(heap).fixed_array_map(),
125 : SKIP_WRITE_BARRIER);
126 : FixedArray array = FixedArray::cast(obj);
127 : array->set_length(length);
128 : MemsetTagged(array->data_start(), ReadOnlyRoots(heap).undefined_value(),
129 : length);
130 6355 : return array;
131 : }
132 :
133 26661 : HEAP_TEST(MarkCompactCollector) {
134 5 : FLAG_incremental_marking = false;
135 5 : FLAG_retain_maps_for_n_gc = 0;
136 5 : CcTest::InitializeVM();
137 : Isolate* isolate = CcTest::i_isolate();
138 5 : Heap* heap = CcTest::heap();
139 : Factory* factory = isolate->factory();
140 :
141 10 : v8::HandleScope sc(CcTest::isolate());
142 10 : Handle<JSGlobalObject> global(isolate->context()->global_object(), isolate);
143 :
144 : // call mark-compact when heap is empty
145 5 : CcTest::CollectGarbage(OLD_SPACE);
146 :
147 : // keep allocating garbage in new space until it fails
148 : const int arraysize = 100;
149 : AllocationResult allocation;
150 6345 : do {
151 : allocation =
152 6345 : AllocateFixedArrayForTest(heap, arraysize, AllocationType::kYoung);
153 : } while (!allocation.IsRetry());
154 5 : CcTest::CollectGarbage(NEW_SPACE);
155 10 : AllocateFixedArrayForTest(heap, arraysize, AllocationType::kYoung)
156 : .ToObjectChecked();
157 :
158 : // keep allocating maps until it fails
159 603867 : do {
160 603867 : allocation = AllocateMapForTest(isolate);
161 : } while (!allocation.IsRetry());
162 5 : CcTest::CollectGarbage(MAP_SPACE);
163 10 : AllocateMapForTest(isolate).ToObjectChecked();
164 :
165 : { HandleScope scope(isolate);
166 : // allocate a garbage
167 5 : Handle<String> func_name = factory->InternalizeUtf8String("theFunction");
168 5 : Handle<JSFunction> function = factory->NewFunctionForTest(func_name);
169 10 : Object::SetProperty(isolate, global, func_name, function).Check();
170 :
171 5 : factory->NewJSObject(function);
172 : }
173 :
174 5 : CcTest::CollectGarbage(OLD_SPACE);
175 :
176 : { HandleScope scope(isolate);
177 5 : Handle<String> func_name = factory->InternalizeUtf8String("theFunction");
178 10 : CHECK(Just(true) == JSReceiver::HasOwnProperty(global, func_name));
179 : Handle<Object> func_value =
180 10 : Object::GetProperty(isolate, global, func_name).ToHandleChecked();
181 5 : CHECK(func_value->IsJSFunction());
182 5 : Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
183 5 : Handle<JSObject> obj = factory->NewJSObject(function);
184 :
185 5 : Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
186 10 : Object::SetProperty(isolate, global, obj_name, obj).Check();
187 5 : Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
188 : Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
189 10 : Object::SetProperty(isolate, obj, prop_name, twenty_three).Check();
190 : }
191 :
192 5 : CcTest::CollectGarbage(OLD_SPACE);
193 :
194 : { HandleScope scope(isolate);
195 5 : Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
196 10 : CHECK(Just(true) == JSReceiver::HasOwnProperty(global, obj_name));
197 : Handle<Object> object =
198 10 : Object::GetProperty(isolate, global, obj_name).ToHandleChecked();
199 5 : CHECK(object->IsJSObject());
200 5 : Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
201 10 : CHECK_EQ(*Object::GetProperty(isolate, object, prop_name).ToHandleChecked(),
202 : Smi::FromInt(23));
203 : }
204 5 : }
205 :
206 :
207 : // TODO(1600): compaction of map space is temporary removed from GC.
208 : #if 0
209 : static Handle<Map> CreateMap(Isolate* isolate) {
210 : return isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
211 : }
212 :
213 :
214 : TEST(MapCompact) {
215 : FLAG_max_map_space_pages = 16;
216 : CcTest::InitializeVM();
217 : Isolate* isolate = CcTest::i_isolate();
218 : Factory* factory = isolate->factory();
219 :
220 : {
221 : v8::HandleScope sc;
222 : // keep allocating maps while pointers are still encodable and thus
223 : // mark compact is permitted.
224 : Handle<JSObject> root = factory->NewJSObjectFromMap(CreateMap());
225 : do {
226 : Handle<Map> map = CreateMap();
227 : map->set_prototype(*root);
228 : root = factory->NewJSObjectFromMap(map);
229 : } while (CcTest::heap()->map_space()->MapPointersEncodable());
230 : }
231 : // Now, as we don't have any handles to just allocated maps, we should
232 : // be able to trigger map compaction.
233 : // To give an additional chance to fail, try to force compaction which
234 : // should be impossible right now.
235 : CcTest::CollectAllGarbage(Heap::kForceCompactionMask);
236 : // And now map pointers should be encodable again.
237 : CHECK(CcTest::heap()->map_space()->MapPointersEncodable());
238 : }
239 : #endif
240 :
241 : #if defined(__has_feature)
242 : #if __has_feature(address_sanitizer)
243 : #define V8_WITH_ASAN 1
244 : #endif
245 : #endif
246 :
247 : // Here is a memory use test that uses /proc, and is therefore Linux-only. We
248 : // do not care how much memory the simulator uses, since it is only there for
249 : // debugging purposes. Testing with ASAN doesn't make sense, either.
250 : #if defined(__linux__) && !defined(USE_SIMULATOR) && !defined(V8_WITH_ASAN)
251 :
252 :
253 18150 : static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) {
254 18150 : char* end_address = buffer + *position;
255 18150 : uintptr_t result = strtoul(buffer + *position, &end_address, base);
256 18150 : CHECK(result != ULONG_MAX || errno != ERANGE);
257 18150 : CHECK(end_address > buffer + *position);
258 18150 : *position = end_address - buffer;
259 18150 : return result;
260 : }
261 :
262 :
263 : // The memory use computed this way is not entirely accurate and depends on
264 : // the way malloc allocates memory. That's why the memory use may seem to
265 : // increase even though the sum of the allocated object sizes decreases. It
266 : // also means that the memory use depends on the kernel and stdlib.
267 55 : static intptr_t MemoryInUse() {
268 : intptr_t memory_use = 0;
269 :
270 : int fd = open("/proc/self/maps", O_RDONLY);
271 55 : if (fd < 0) return -1;
272 :
273 : const int kBufSize = 20000;
274 : char buffer[kBufSize];
275 : ssize_t length = read(fd, buffer, kBufSize);
276 : intptr_t line_start = 0;
277 55 : CHECK_LT(length, kBufSize); // Make the buffer bigger.
278 55 : CHECK_GT(length, 0); // We have to find some data in the file.
279 6105 : while (line_start < length) {
280 6050 : if (buffer[line_start] == '\n') {
281 3025 : line_start++;
282 3025 : continue;
283 : }
284 3025 : intptr_t position = line_start;
285 3025 : uintptr_t start = ReadLong(buffer, &position, 16);
286 3025 : CHECK_EQ(buffer[position++], '-');
287 3025 : uintptr_t end = ReadLong(buffer, &position, 16);
288 3025 : CHECK_EQ(buffer[position++], ' ');
289 3025 : CHECK(buffer[position] == '-' || buffer[position] == 'r');
290 3025 : bool read_permission = (buffer[position++] == 'r');
291 3025 : CHECK(buffer[position] == '-' || buffer[position] == 'w');
292 3025 : bool write_permission = (buffer[position++] == 'w');
293 3025 : CHECK(buffer[position] == '-' || buffer[position] == 'x');
294 3025 : bool execute_permission = (buffer[position++] == 'x');
295 3025 : CHECK(buffer[position] == 's' || buffer[position] == 'p');
296 3025 : bool private_mapping = (buffer[position++] == 'p');
297 3025 : CHECK_EQ(buffer[position++], ' ');
298 3025 : uintptr_t offset = ReadLong(buffer, &position, 16);
299 : USE(offset);
300 3025 : CHECK_EQ(buffer[position++], ' ');
301 3025 : uintptr_t major = ReadLong(buffer, &position, 16);
302 : USE(major);
303 3025 : CHECK_EQ(buffer[position++], ':');
304 3025 : uintptr_t minor = ReadLong(buffer, &position, 16);
305 : USE(minor);
306 3025 : CHECK_EQ(buffer[position++], ' ');
307 3025 : uintptr_t inode = ReadLong(buffer, &position, 10);
308 77890 : while (position < length && buffer[position] != '\n') position++;
309 3025 : if ((read_permission || write_permission || execute_permission) &&
310 2100 : private_mapping && inode == 0) {
311 1201 : memory_use += (end - start);
312 : }
313 :
314 : line_start = position;
315 : }
316 55 : close(fd);
317 55 : return memory_use;
318 : }
319 :
320 :
321 55 : intptr_t ShortLivingIsolate() {
322 : v8::Isolate::CreateParams create_params;
323 55 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
324 55 : v8::Isolate* isolate = v8::Isolate::New(create_params);
325 : { v8::Isolate::Scope isolate_scope(isolate);
326 55 : v8::Locker lock(isolate);
327 110 : v8::HandleScope handle_scope(isolate);
328 55 : v8::Local<v8::Context> context = v8::Context::New(isolate);
329 55 : CHECK(!context.IsEmpty());
330 : }
331 55 : isolate->Dispose();
332 55 : return MemoryInUse();
333 : }
334 :
335 :
336 26661 : TEST(RegressJoinThreadsOnIsolateDeinit) {
337 5 : intptr_t size_limit = ShortLivingIsolate() * 2;
338 105 : for (int i = 0; i < 10; i++) {
339 50 : CHECK_GT(size_limit, ShortLivingIsolate());
340 : }
341 5 : }
342 :
343 26661 : TEST(Regress5829) {
344 5 : CcTest::InitializeVM();
345 : Isolate* isolate = CcTest::i_isolate();
346 10 : v8::HandleScope sc(CcTest::isolate());
347 : Heap* heap = isolate->heap();
348 5 : heap::SealCurrentObjects(heap);
349 : i::MarkCompactCollector* collector = heap->mark_compact_collector();
350 : i::IncrementalMarking* marking = heap->incremental_marking();
351 5 : if (collector->sweeping_in_progress()) {
352 0 : collector->EnsureSweepingCompleted();
353 : }
354 5 : CHECK(marking->IsMarking() || marking->IsStopped());
355 5 : if (marking->IsStopped()) {
356 : heap->StartIncrementalMarking(i::Heap::kNoGCFlags,
357 5 : i::GarbageCollectionReason::kTesting);
358 : }
359 5 : CHECK(marking->IsMarking());
360 : marking->StartBlackAllocationForTesting();
361 : Handle<FixedArray> array =
362 5 : isolate->factory()->NewFixedArray(10, AllocationType::kOld);
363 5 : Address old_end = array->address() + array->Size();
364 : // Right trim the array without clearing the mark bits.
365 : array->set_length(9);
366 : heap->CreateFillerObjectAt(old_end - kTaggedSize, kTaggedSize,
367 5 : ClearRecordedSlots::kNo);
368 5 : heap->old_space()->FreeLinearAllocationArea();
369 : Page* page = Page::FromAddress(array->address());
370 : IncrementalMarking::MarkingState* marking_state = marking->marking_state();
371 5 : for (auto object_and_size :
372 : LiveObjectRange<kGreyObjects>(page, marking_state->bitmap(page))) {
373 0 : CHECK(!object_and_size.first->IsFiller());
374 : }
375 5 : }
376 :
377 : #endif // __linux__ and !USE_SIMULATOR
378 :
379 : } // namespace heap
380 : } // namespace internal
381 79968 : } // namespace v8
|