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 23724 : TEST(Promotion) {
55 6 : CcTest::InitializeVM();
56 : Isolate* isolate = CcTest::i_isolate();
57 : {
58 6 : v8::HandleScope sc(CcTest::isolate());
59 6 : Heap* heap = isolate->heap();
60 :
61 6 : heap::SealCurrentObjects(heap);
62 :
63 6 : int array_length = heap::FixedArrayLenFromSize(kMaxRegularHeapObjectSize);
64 6 : Handle<FixedArray> array = isolate->factory()->NewFixedArray(array_length);
65 :
66 : // Array should be in the new space.
67 6 : CHECK(heap->InSpace(*array, NEW_SPACE));
68 6 : CcTest::CollectAllGarbage();
69 6 : CcTest::CollectAllGarbage();
70 6 : CHECK(heap->InSpace(*array, OLD_SPACE));
71 : }
72 6 : }
73 :
74 23724 : HEAP_TEST(NoPromotion) {
75 : // Page promotion allows pages to be moved to old space even in the case of
76 : // OOM scenarios.
77 6 : FLAG_page_promotion = false;
78 :
79 6 : CcTest::InitializeVM();
80 : Isolate* isolate = CcTest::i_isolate();
81 : {
82 6 : v8::HandleScope sc(CcTest::isolate());
83 6 : Heap* heap = isolate->heap();
84 :
85 6 : heap::SealCurrentObjects(heap);
86 :
87 6 : int array_length = heap::FixedArrayLenFromSize(kMaxRegularHeapObjectSize);
88 6 : Handle<FixedArray> array = isolate->factory()->NewFixedArray(array_length);
89 :
90 : heap->set_force_oom(true);
91 : // Array should be in the new space.
92 6 : CHECK(heap->InSpace(*array, NEW_SPACE));
93 6 : CcTest::CollectAllGarbage();
94 6 : CcTest::CollectAllGarbage();
95 6 : CHECK(heap->InSpace(*array, NEW_SPACE));
96 : }
97 6 : }
98 :
99 23724 : HEAP_TEST(MarkCompactCollector) {
100 6 : FLAG_incremental_marking = false;
101 6 : FLAG_retain_maps_for_n_gc = 0;
102 6 : CcTest::InitializeVM();
103 6 : Isolate* isolate = CcTest::i_isolate();
104 6 : Heap* heap = CcTest::heap();
105 : Factory* factory = isolate->factory();
106 :
107 6 : v8::HandleScope sc(CcTest::isolate());
108 6 : Handle<JSGlobalObject> global(isolate->context()->global_object());
109 :
110 : // call mark-compact when heap is empty
111 6 : CcTest::CollectGarbage(OLD_SPACE);
112 :
113 : // keep allocating garbage in new space until it fails
114 : const int arraysize = 100;
115 : AllocationResult allocation;
116 7482 : do {
117 : allocation = heap->AllocateFixedArray(arraysize);
118 : } while (!allocation.IsRetry());
119 6 : CcTest::CollectGarbage(NEW_SPACE);
120 6 : heap->AllocateFixedArray(arraysize).ToObjectChecked();
121 :
122 : // keep allocating maps until it fails
123 732708 : do {
124 732708 : allocation = heap->AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
125 : } while (!allocation.IsRetry());
126 6 : CcTest::CollectGarbage(MAP_SPACE);
127 6 : heap->AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize).ToObjectChecked();
128 :
129 : { HandleScope scope(isolate);
130 : // allocate a garbage
131 6 : Handle<String> func_name = factory->InternalizeUtf8String("theFunction");
132 6 : Handle<JSFunction> function = factory->NewFunction(func_name);
133 6 : JSReceiver::SetProperty(global, func_name, function, LanguageMode::kSloppy)
134 12 : .Check();
135 :
136 6 : factory->NewJSObject(function);
137 : }
138 :
139 6 : CcTest::CollectGarbage(OLD_SPACE);
140 :
141 : { HandleScope scope(isolate);
142 6 : Handle<String> func_name = factory->InternalizeUtf8String("theFunction");
143 12 : CHECK(Just(true) == JSReceiver::HasOwnProperty(global, func_name));
144 : Handle<Object> func_value =
145 12 : Object::GetProperty(global, func_name).ToHandleChecked();
146 6 : CHECK(func_value->IsJSFunction());
147 6 : Handle<JSFunction> function = Handle<JSFunction>::cast(func_value);
148 6 : Handle<JSObject> obj = factory->NewJSObject(function);
149 :
150 6 : Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
151 6 : JSReceiver::SetProperty(global, obj_name, obj, LanguageMode::kSloppy)
152 12 : .Check();
153 6 : Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
154 : Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
155 6 : JSReceiver::SetProperty(obj, prop_name, twenty_three, LanguageMode::kSloppy)
156 12 : .Check();
157 : }
158 :
159 6 : CcTest::CollectGarbage(OLD_SPACE);
160 :
161 : { HandleScope scope(isolate);
162 6 : Handle<String> obj_name = factory->InternalizeUtf8String("theObject");
163 12 : CHECK(Just(true) == JSReceiver::HasOwnProperty(global, obj_name));
164 : Handle<Object> object =
165 12 : Object::GetProperty(global, obj_name).ToHandleChecked();
166 6 : CHECK(object->IsJSObject());
167 6 : Handle<String> prop_name = factory->InternalizeUtf8String("theSlot");
168 12 : CHECK_EQ(*Object::GetProperty(object, prop_name).ToHandleChecked(),
169 : Smi::FromInt(23));
170 6 : }
171 6 : }
172 :
173 :
174 : // TODO(1600): compaction of map space is temporary removed from GC.
175 : #if 0
176 : static Handle<Map> CreateMap(Isolate* isolate) {
177 : return isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
178 : }
179 :
180 :
181 : TEST(MapCompact) {
182 : FLAG_max_map_space_pages = 16;
183 : CcTest::InitializeVM();
184 : Isolate* isolate = CcTest::i_isolate();
185 : Factory* factory = isolate->factory();
186 :
187 : {
188 : v8::HandleScope sc;
189 : // keep allocating maps while pointers are still encodable and thus
190 : // mark compact is permitted.
191 : Handle<JSObject> root = factory->NewJSObjectFromMap(CreateMap());
192 : do {
193 : Handle<Map> map = CreateMap();
194 : map->set_prototype(*root);
195 : root = factory->NewJSObjectFromMap(map);
196 : } while (CcTest::heap()->map_space()->MapPointersEncodable());
197 : }
198 : // Now, as we don't have any handles to just allocated maps, we should
199 : // be able to trigger map compaction.
200 : // To give an additional chance to fail, try to force compaction which
201 : // should be impossible right now.
202 : CcTest::CollectAllGarbage(Heap::kForceCompactionMask);
203 : // And now map pointers should be encodable again.
204 : CHECK(CcTest::heap()->map_space()->MapPointersEncodable());
205 : }
206 : #endif
207 :
208 : #if defined(__has_feature)
209 : #if __has_feature(address_sanitizer)
210 : #define V8_WITH_ASAN 1
211 : #endif
212 : #endif
213 :
214 : // Here is a memory use test that uses /proc, and is therefore Linux-only. We
215 : // do not care how much memory the simulator uses, since it is only there for
216 : // debugging purposes. Testing with ASAN doesn't make sense, either.
217 : #if defined(__linux__) && !defined(USE_SIMULATOR) && !defined(V8_WITH_ASAN)
218 :
219 :
220 23808 : static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) {
221 23808 : char* end_address = buffer + *position;
222 23808 : uintptr_t result = strtoul(buffer + *position, &end_address, base);
223 23808 : CHECK(result != ULONG_MAX || errno != ERANGE);
224 23808 : CHECK(end_address > buffer + *position);
225 23808 : *position = end_address - buffer;
226 23808 : return result;
227 : }
228 :
229 :
230 : // The memory use computed this way is not entirely accurate and depends on
231 : // the way malloc allocates memory. That's why the memory use may seem to
232 : // increase even though the sum of the allocated object sizes decreases. It
233 : // also means that the memory use depends on the kernel and stdlib.
234 66 : static intptr_t MemoryInUse() {
235 : intptr_t memory_use = 0;
236 :
237 : int fd = open("/proc/self/maps", O_RDONLY);
238 66 : if (fd < 0) return -1;
239 :
240 : const int kBufSize = 20000;
241 : char buffer[kBufSize];
242 : ssize_t length = read(fd, buffer, kBufSize);
243 : intptr_t line_start = 0;
244 66 : CHECK_LT(length, kBufSize); // Make the buffer bigger.
245 66 : CHECK_GT(length, 0); // We have to find some data in the file.
246 8002 : while (line_start < length) {
247 7936 : if (buffer[line_start] == '\n') {
248 3968 : line_start++;
249 3968 : continue;
250 : }
251 3968 : intptr_t position = line_start;
252 3968 : uintptr_t start = ReadLong(buffer, &position, 16);
253 3968 : CHECK_EQ(buffer[position++], '-');
254 3968 : uintptr_t end = ReadLong(buffer, &position, 16);
255 3968 : CHECK_EQ(buffer[position++], ' ');
256 3968 : CHECK(buffer[position] == '-' || buffer[position] == 'r');
257 3968 : bool read_permission = (buffer[position++] == 'r');
258 3968 : CHECK(buffer[position] == '-' || buffer[position] == 'w');
259 3968 : bool write_permission = (buffer[position++] == 'w');
260 3968 : CHECK(buffer[position] == '-' || buffer[position] == 'x');
261 3968 : bool execute_permission = (buffer[position++] == 'x');
262 3968 : CHECK(buffer[position] == 's' || buffer[position] == 'p');
263 3968 : bool private_mapping = (buffer[position++] == 'p');
264 3968 : CHECK_EQ(buffer[position++], ' ');
265 3968 : uintptr_t offset = ReadLong(buffer, &position, 16);
266 : USE(offset);
267 3968 : CHECK_EQ(buffer[position++], ' ');
268 3968 : uintptr_t major = ReadLong(buffer, &position, 16);
269 : USE(major);
270 3968 : CHECK_EQ(buffer[position++], ':');
271 3968 : uintptr_t minor = ReadLong(buffer, &position, 16);
272 : USE(minor);
273 3968 : CHECK_EQ(buffer[position++], ' ');
274 3968 : uintptr_t inode = ReadLong(buffer, &position, 10);
275 3968 : while (position < length && buffer[position] != '\n') position++;
276 3968 : if ((read_permission || write_permission || execute_permission) &&
277 2321 : private_mapping && inode == 0) {
278 1645 : memory_use += (end - start);
279 : }
280 :
281 : line_start = position;
282 : }
283 66 : close(fd);
284 66 : return memory_use;
285 : }
286 :
287 :
288 66 : intptr_t ShortLivingIsolate() {
289 : v8::Isolate::CreateParams create_params;
290 66 : create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
291 66 : v8::Isolate* isolate = v8::Isolate::New(create_params);
292 : { v8::Isolate::Scope isolate_scope(isolate);
293 66 : v8::Locker lock(isolate);
294 132 : v8::HandleScope handle_scope(isolate);
295 66 : v8::Local<v8::Context> context = v8::Context::New(isolate);
296 66 : CHECK(!context.IsEmpty());
297 : }
298 66 : isolate->Dispose();
299 66 : return MemoryInUse();
300 : }
301 :
302 :
303 23724 : TEST(RegressJoinThreadsOnIsolateDeinit) {
304 6 : intptr_t size_limit = ShortLivingIsolate() * 2;
305 66 : for (int i = 0; i < 10; i++) {
306 60 : CHECK_GT(size_limit, ShortLivingIsolate());
307 : }
308 6 : }
309 :
310 23724 : TEST(Regress5829) {
311 6 : CcTest::InitializeVM();
312 : Isolate* isolate = CcTest::i_isolate();
313 6 : v8::HandleScope sc(CcTest::isolate());
314 18 : Heap* heap = isolate->heap();
315 6 : heap::SealCurrentObjects(heap);
316 : i::MarkCompactCollector* collector = heap->mark_compact_collector();
317 : i::IncrementalMarking* marking = heap->incremental_marking();
318 6 : if (collector->sweeping_in_progress()) {
319 0 : collector->EnsureSweepingCompleted();
320 : }
321 6 : CHECK(marking->IsMarking() || marking->IsStopped());
322 6 : if (marking->IsStopped()) {
323 : heap->StartIncrementalMarking(i::Heap::kNoGCFlags,
324 6 : i::GarbageCollectionReason::kTesting);
325 : }
326 6 : CHECK(marking->IsMarking());
327 : marking->StartBlackAllocationForTesting();
328 6 : Handle<FixedArray> array = isolate->factory()->NewFixedArray(10, TENURED);
329 6 : Address old_end = array->address() + array->Size();
330 : // Right trim the array without clearing the mark bits.
331 : array->set_length(9);
332 : heap->CreateFillerObjectAt(old_end - kPointerSize, kPointerSize,
333 6 : ClearRecordedSlots::kNo);
334 6 : heap->old_space()->EmptyAllocationInfo();
335 6 : Page* page = Page::FromAddress(array->address());
336 : IncrementalMarking::MarkingState* marking_state = marking->marking_state();
337 6 : for (auto object_and_size :
338 12 : LiveObjectRange<kGreyObjects>(page, marking_state->bitmap(page))) {
339 0 : CHECK(!object_and_size.first->IsFiller());
340 6 : }
341 6 : }
342 :
343 : #endif // __linux__ and !USE_SIMULATOR
344 :
345 : } // namespace heap
346 : } // namespace internal
347 71154 : } // namespace v8
|