Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / v8 / test / cctest / test-mark-compact.cc
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 <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #endif
37
38 #include <utility>
39
40 #include "v8.h"
41
42 #include "global-handles.h"
43 #include "snapshot.h"
44 #include "cctest.h"
45
46 using namespace v8::internal;
47
48
49 TEST(MarkingDeque) {
50   CcTest::InitializeVM();
51   int mem_size = 20 * kPointerSize;
52   byte* mem = NewArray<byte>(20*kPointerSize);
53   Address low = reinterpret_cast<Address>(mem);
54   Address high = low + mem_size;
55   MarkingDeque s;
56   s.Initialize(low, high);
57
58   Address original_address = reinterpret_cast<Address>(&s);
59   Address current_address = original_address;
60   while (!s.IsFull()) {
61     s.PushBlack(HeapObject::FromAddress(current_address));
62     current_address += kPointerSize;
63   }
64
65   while (!s.IsEmpty()) {
66     Address value = s.Pop()->address();
67     current_address -= kPointerSize;
68     CHECK_EQ(current_address, value);
69   }
70
71   CHECK_EQ(original_address, current_address);
72   DeleteArray(mem);
73 }
74
75
76 TEST(Promotion) {
77   CcTest::InitializeVM();
78   Heap* heap = CcTest::heap();
79   heap->ConfigureHeap(2*256*KB, 1*MB, 1*MB);
80
81   v8::HandleScope sc(CcTest::isolate());
82
83   // Allocate a fixed array in the new space.
84   int array_length =
85       (Page::kMaxRegularHeapObjectSize - FixedArray::kHeaderSize) /
86       (4 * kPointerSize);
87   Object* obj = heap->AllocateFixedArray(array_length)->ToObjectChecked();
88   Handle<FixedArray> array(FixedArray::cast(obj));
89
90   // Array should be in the new space.
91   CHECK(heap->InSpace(*array, NEW_SPACE));
92
93   // Call mark compact GC, so array becomes an old object.
94   heap->CollectGarbage(OLD_POINTER_SPACE);
95
96   // Array now sits in the old space
97   CHECK(heap->InSpace(*array, OLD_POINTER_SPACE));
98 }
99
100
101 TEST(NoPromotion) {
102   CcTest::InitializeVM();
103   Heap* heap = CcTest::heap();
104   heap->ConfigureHeap(2*256*KB, 1*MB, 1*MB);
105
106   v8::HandleScope sc(CcTest::isolate());
107
108   // Allocate a big fixed array in the new space.
109   int array_length =
110       (Page::kMaxRegularHeapObjectSize - FixedArray::kHeaderSize) /
111       (2 * kPointerSize);
112   Object* obj = heap->AllocateFixedArray(array_length)->ToObjectChecked();
113   Handle<FixedArray> array(FixedArray::cast(obj));
114
115   // Array should be in the new space.
116   CHECK(heap->InSpace(*array, NEW_SPACE));
117
118   // Simulate a full old space to make promotion fail.
119   SimulateFullSpace(heap->old_pointer_space());
120
121   // Call mark compact GC, and it should pass.
122   heap->CollectGarbage(OLD_POINTER_SPACE);
123 }
124
125
126 TEST(MarkCompactCollector) {
127   FLAG_incremental_marking = false;
128   CcTest::InitializeVM();
129   Isolate* isolate = CcTest::i_isolate();
130   Heap* heap = isolate->heap();
131
132   v8::HandleScope sc(CcTest::isolate());
133   Handle<GlobalObject> global(isolate->context()->global_object());
134
135   // call mark-compact when heap is empty
136   heap->CollectGarbage(OLD_POINTER_SPACE, "trigger 1");
137
138   // keep allocating garbage in new space until it fails
139   const int ARRAY_SIZE = 100;
140   Object* array;
141   MaybeObject* maybe_array;
142   do {
143     maybe_array = heap->AllocateFixedArray(ARRAY_SIZE);
144   } while (maybe_array->ToObject(&array));
145   heap->CollectGarbage(NEW_SPACE, "trigger 2");
146
147   array = heap->AllocateFixedArray(ARRAY_SIZE)->ToObjectChecked();
148
149   // keep allocating maps until it fails
150   Object* mapp;
151   MaybeObject* maybe_mapp;
152   do {
153     maybe_mapp = heap->AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
154   } while (maybe_mapp->ToObject(&mapp));
155   heap->CollectGarbage(MAP_SPACE, "trigger 3");
156   mapp = heap->AllocateMap(JS_OBJECT_TYPE,
157                            JSObject::kHeaderSize)->ToObjectChecked();
158
159   // allocate a garbage
160   String* func_name = String::cast(
161       heap->InternalizeUtf8String("theFunction")->ToObjectChecked());
162   SharedFunctionInfo* function_share = SharedFunctionInfo::cast(
163       heap->AllocateSharedFunctionInfo(func_name)->ToObjectChecked());
164   JSFunction* function = JSFunction::cast(
165       heap->AllocateFunction(*isolate->sloppy_function_map(),
166                              function_share,
167                              heap->undefined_value())->ToObjectChecked());
168   Map* initial_map =
169       Map::cast(heap->AllocateMap(JS_OBJECT_TYPE,
170                                   JSObject::kHeaderSize)->ToObjectChecked());
171   function->set_initial_map(initial_map);
172   JSReceiver::SetProperty(
173       global, handle(func_name), handle(function), NONE, SLOPPY);
174
175   JSObject* obj = JSObject::cast(
176       heap->AllocateJSObject(function)->ToObjectChecked());
177   heap->CollectGarbage(OLD_POINTER_SPACE, "trigger 4");
178
179   func_name = String::cast(
180       heap->InternalizeUtf8String("theFunction")->ToObjectChecked());
181   CHECK(JSReceiver::HasLocalProperty(global, handle(func_name)));
182   Object* func_value = isolate->context()->global_object()->
183       GetProperty(func_name)->ToObjectChecked();
184   CHECK(func_value->IsJSFunction());
185   function = JSFunction::cast(func_value);
186
187   obj = JSObject::cast(heap->AllocateJSObject(function)->ToObjectChecked());
188   String* obj_name =
189       String::cast(heap->InternalizeUtf8String("theObject")->ToObjectChecked());
190   JSReceiver::SetProperty(global, handle(obj_name), handle(obj), NONE, SLOPPY);
191   String* prop_name =
192       String::cast(heap->InternalizeUtf8String("theSlot")->ToObjectChecked());
193   Handle<Smi> twenty_three(Smi::FromInt(23), isolate);
194   JSReceiver::SetProperty(
195       handle(obj), handle(prop_name), twenty_three, NONE, SLOPPY);
196
197   heap->CollectGarbage(OLD_POINTER_SPACE, "trigger 5");
198
199   obj_name =
200       String::cast(heap->InternalizeUtf8String("theObject")->ToObjectChecked());
201   CHECK(JSReceiver::HasLocalProperty(global, handle(obj_name)));
202   CHECK(isolate->context()->global_object()->
203         GetProperty(obj_name)->ToObjectChecked()->IsJSObject());
204   obj = JSObject::cast(isolate->context()->global_object()->
205                        GetProperty(obj_name)->ToObjectChecked());
206   prop_name =
207       String::cast(heap->InternalizeUtf8String("theSlot")->ToObjectChecked());
208   CHECK(obj->GetProperty(prop_name) == Smi::FromInt(23));
209 }
210
211
212 // TODO(1600): compaction of map space is temporary removed from GC.
213 #if 0
214 static Handle<Map> CreateMap(Isolate* isolate) {
215   return isolate->factory()->NewMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
216 }
217
218
219 TEST(MapCompact) {
220   FLAG_max_map_space_pages = 16;
221   CcTest::InitializeVM();
222   Isolate* isolate = CcTest::i_isolate();
223   Factory* factory = isolate->factory();
224
225   {
226     v8::HandleScope sc;
227     // keep allocating maps while pointers are still encodable and thus
228     // mark compact is permitted.
229     Handle<JSObject> root = factory->NewJSObjectFromMap(CreateMap());
230     do {
231       Handle<Map> map = CreateMap();
232       map->set_prototype(*root);
233       root = factory->NewJSObjectFromMap(map);
234     } while (CcTest::heap()->map_space()->MapPointersEncodable());
235   }
236   // Now, as we don't have any handles to just allocated maps, we should
237   // be able to trigger map compaction.
238   // To give an additional chance to fail, try to force compaction which
239   // should be impossible right now.
240   CcTest::heap()->CollectAllGarbage(Heap::kForceCompactionMask);
241   // And now map pointers should be encodable again.
242   CHECK(CcTest::heap()->map_space()->MapPointersEncodable());
243 }
244 #endif
245
246
247 static int NumberOfWeakCalls = 0;
248 static void WeakPointerCallback(
249     const v8::WeakCallbackData<v8::Value, void>& data) {
250   std::pair<v8::Persistent<v8::Value>*, int>* p =
251       reinterpret_cast<std::pair<v8::Persistent<v8::Value>*, int>*>(
252           data.GetParameter());
253   ASSERT_EQ(1234, p->second);
254   NumberOfWeakCalls++;
255   p->first->Reset();
256 }
257
258
259 TEST(ObjectGroups) {
260   FLAG_incremental_marking = false;
261   CcTest::InitializeVM();
262   GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
263   Heap* heap = CcTest::heap();
264   NumberOfWeakCalls = 0;
265   v8::HandleScope handle_scope(CcTest::isolate());
266
267   Handle<Object> g1s1 =
268       global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
269   Handle<Object> g1s2 =
270       global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
271   Handle<Object> g1c1 =
272       global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
273   std::pair<Handle<Object>*, int> g1s1_and_id(&g1s1, 1234);
274   GlobalHandles::MakeWeak(g1s1.location(),
275                           reinterpret_cast<void*>(&g1s1_and_id),
276                           &WeakPointerCallback);
277   std::pair<Handle<Object>*, int> g1s2_and_id(&g1s2, 1234);
278   GlobalHandles::MakeWeak(g1s2.location(),
279                           reinterpret_cast<void*>(&g1s2_and_id),
280                           &WeakPointerCallback);
281   std::pair<Handle<Object>*, int> g1c1_and_id(&g1c1, 1234);
282   GlobalHandles::MakeWeak(g1c1.location(),
283                           reinterpret_cast<void*>(&g1c1_and_id),
284                           &WeakPointerCallback);
285
286   Handle<Object> g2s1 =
287       global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
288   Handle<Object> g2s2 =
289     global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
290   Handle<Object> g2c1 =
291     global_handles->Create(heap->AllocateFixedArray(1)->ToObjectChecked());
292   std::pair<Handle<Object>*, int> g2s1_and_id(&g2s1, 1234);
293   GlobalHandles::MakeWeak(g2s1.location(),
294                           reinterpret_cast<void*>(&g2s1_and_id),
295                           &WeakPointerCallback);
296   std::pair<Handle<Object>*, int> g2s2_and_id(&g2s2, 1234);
297   GlobalHandles::MakeWeak(g2s2.location(),
298                           reinterpret_cast<void*>(&g2s2_and_id),
299                           &WeakPointerCallback);
300   std::pair<Handle<Object>*, int> g2c1_and_id(&g2c1, 1234);
301   GlobalHandles::MakeWeak(g2c1.location(),
302                           reinterpret_cast<void*>(&g2c1_and_id),
303                           &WeakPointerCallback);
304
305   Handle<Object> root = global_handles->Create(*g1s1);  // make a root.
306
307   // Connect group 1 and 2, make a cycle.
308   Handle<FixedArray>::cast(g1s2)->set(0, *g2s2);
309   Handle<FixedArray>::cast(g2s1)->set(0, *g1s1);
310
311   {
312     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
313     Object** g1_children[] = { g1c1.location() };
314     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
315     Object** g2_children[] = { g2c1.location() };
316     global_handles->AddObjectGroup(g1_objects, 2, NULL);
317     global_handles->AddImplicitReferences(
318         Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
319     global_handles->AddObjectGroup(g2_objects, 2, NULL);
320     global_handles->AddImplicitReferences(
321         Handle<HeapObject>::cast(g2s1).location(), g2_children, 1);
322   }
323   // Do a full GC
324   heap->CollectGarbage(OLD_POINTER_SPACE);
325
326   // All object should be alive.
327   CHECK_EQ(0, NumberOfWeakCalls);
328
329   // Weaken the root.
330   std::pair<Handle<Object>*, int> root_and_id(&root, 1234);
331   GlobalHandles::MakeWeak(root.location(),
332                           reinterpret_cast<void*>(&root_and_id),
333                           &WeakPointerCallback);
334   // But make children strong roots---all the objects (except for children)
335   // should be collectable now.
336   global_handles->ClearWeakness(g1c1.location());
337   global_handles->ClearWeakness(g2c1.location());
338
339   // Groups are deleted, rebuild groups.
340   {
341     Object** g1_objects[] = { g1s1.location(), g1s2.location() };
342     Object** g1_children[] = { g1c1.location() };
343     Object** g2_objects[] = { g2s1.location(), g2s2.location() };
344     Object** g2_children[] = { g2c1.location() };
345     global_handles->AddObjectGroup(g1_objects, 2, NULL);
346     global_handles->AddImplicitReferences(
347         Handle<HeapObject>::cast(g1s1).location(), g1_children, 1);
348     global_handles->AddObjectGroup(g2_objects, 2, NULL);
349     global_handles->AddImplicitReferences(
350         Handle<HeapObject>::cast(g2s1).location(), g2_children, 1);
351   }
352
353   heap->CollectGarbage(OLD_POINTER_SPACE);
354
355   // All objects should be gone. 5 global handles in total.
356   CHECK_EQ(5, NumberOfWeakCalls);
357
358   // And now make children weak again and collect them.
359   GlobalHandles::MakeWeak(g1c1.location(),
360                           reinterpret_cast<void*>(&g1c1_and_id),
361                           &WeakPointerCallback);
362   GlobalHandles::MakeWeak(g2c1.location(),
363                           reinterpret_cast<void*>(&g2c1_and_id),
364                           &WeakPointerCallback);
365
366   heap->CollectGarbage(OLD_POINTER_SPACE);
367   CHECK_EQ(7, NumberOfWeakCalls);
368 }
369
370
371 class TestRetainedObjectInfo : public v8::RetainedObjectInfo {
372  public:
373   TestRetainedObjectInfo() : has_been_disposed_(false) {}
374
375   bool has_been_disposed() { return has_been_disposed_; }
376
377   virtual void Dispose() {
378     ASSERT(!has_been_disposed_);
379     has_been_disposed_ = true;
380   }
381
382   virtual bool IsEquivalent(v8::RetainedObjectInfo* other) {
383     return other == this;
384   }
385
386   virtual intptr_t GetHash() { return 0; }
387
388   virtual const char* GetLabel() { return "whatever"; }
389
390  private:
391   bool has_been_disposed_;
392 };
393
394
395 TEST(EmptyObjectGroups) {
396   CcTest::InitializeVM();
397   GlobalHandles* global_handles = CcTest::i_isolate()->global_handles();
398
399   v8::HandleScope handle_scope(CcTest::isolate());
400
401   Handle<Object> object = global_handles->Create(
402       CcTest::heap()->AllocateFixedArray(1)->ToObjectChecked());
403
404   TestRetainedObjectInfo info;
405   global_handles->AddObjectGroup(NULL, 0, &info);
406   ASSERT(info.has_been_disposed());
407
408   global_handles->AddImplicitReferences(
409         Handle<HeapObject>::cast(object).location(), NULL, 0);
410 }
411
412
413 #if defined(__has_feature)
414 #if __has_feature(address_sanitizer)
415 #define V8_WITH_ASAN 1
416 #endif
417 #endif
418
419
420 // Here is a memory use test that uses /proc, and is therefore Linux-only.  We
421 // do not care how much memory the simulator uses, since it is only there for
422 // debugging purposes. Testing with ASAN doesn't make sense, either.
423 #if defined(__linux__) && !defined(USE_SIMULATOR) && !defined(V8_WITH_ASAN)
424
425
426 static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) {
427   char* end_address = buffer + *position;
428   uintptr_t result = strtoul(buffer + *position, &end_address, base);
429   CHECK(result != ULONG_MAX || errno != ERANGE);
430   CHECK(end_address > buffer + *position);
431   *position = end_address - buffer;
432   return result;
433 }
434
435
436 // The memory use computed this way is not entirely accurate and depends on
437 // the way malloc allocates memory.  That's why the memory use may seem to
438 // increase even though the sum of the allocated object sizes decreases.  It
439 // also means that the memory use depends on the kernel and stdlib.
440 static intptr_t MemoryInUse() {
441   intptr_t memory_use = 0;
442
443   int fd = open("/proc/self/maps", O_RDONLY);
444   if (fd < 0) return -1;
445
446   const int kBufSize = 10000;
447   char buffer[kBufSize];
448   int length = read(fd, buffer, kBufSize);
449   intptr_t line_start = 0;
450   CHECK_LT(length, kBufSize);  // Make the buffer bigger.
451   CHECK_GT(length, 0);  // We have to find some data in the file.
452   while (line_start < length) {
453     if (buffer[line_start] == '\n') {
454       line_start++;
455       continue;
456     }
457     intptr_t position = line_start;
458     uintptr_t start = ReadLong(buffer, &position, 16);
459     CHECK_EQ(buffer[position++], '-');
460     uintptr_t end = ReadLong(buffer, &position, 16);
461     CHECK_EQ(buffer[position++], ' ');
462     CHECK(buffer[position] == '-' || buffer[position] == 'r');
463     bool read_permission = (buffer[position++] == 'r');
464     CHECK(buffer[position] == '-' || buffer[position] == 'w');
465     bool write_permission = (buffer[position++] == 'w');
466     CHECK(buffer[position] == '-' || buffer[position] == 'x');
467     bool execute_permission = (buffer[position++] == 'x');
468     CHECK(buffer[position] == '-' || buffer[position] == 'p');
469     bool private_mapping = (buffer[position++] == 'p');
470     CHECK_EQ(buffer[position++], ' ');
471     uintptr_t offset = ReadLong(buffer, &position, 16);
472     USE(offset);
473     CHECK_EQ(buffer[position++], ' ');
474     uintptr_t major = ReadLong(buffer, &position, 16);
475     USE(major);
476     CHECK_EQ(buffer[position++], ':');
477     uintptr_t minor = ReadLong(buffer, &position, 16);
478     USE(minor);
479     CHECK_EQ(buffer[position++], ' ');
480     uintptr_t inode = ReadLong(buffer, &position, 10);
481     while (position < length && buffer[position] != '\n') position++;
482     if ((read_permission || write_permission || execute_permission) &&
483         private_mapping && inode == 0) {
484       memory_use += (end - start);
485     }
486
487     line_start = position;
488   }
489   close(fd);
490   return memory_use;
491 }
492
493
494 TEST(BootUpMemoryUse) {
495   intptr_t initial_memory = MemoryInUse();
496   // Avoid flakiness.
497   FLAG_crankshaft = false;
498   FLAG_concurrent_osr = false;
499   FLAG_concurrent_recompilation = false;
500
501   // Only Linux has the proc filesystem and only if it is mapped.  If it's not
502   // there we just skip the test.
503   if (initial_memory >= 0) {
504     CcTest::InitializeVM();
505     intptr_t delta = MemoryInUse() - initial_memory;
506     printf("delta: %" V8_PTR_PREFIX "d kB\n", delta / 1024);
507     if (sizeof(initial_memory) == 8) {  // 64-bit.
508       if (v8::internal::Snapshot::IsEnabled()) {
509         CHECK_LE(delta, 4000 * 1024);
510       } else {
511         CHECK_LE(delta, 4500 * 1024);
512       }
513     } else {                            // 32-bit.
514       if (v8::internal::Snapshot::IsEnabled()) {
515         CHECK_LE(delta, 3100 * 1024);
516       } else {
517         CHECK_LE(delta, 3450 * 1024);
518       }
519     }
520   }
521 }
522
523
524 intptr_t ShortLivingIsolate() {
525   v8::Isolate* isolate = v8::Isolate::New();
526   { v8::Isolate::Scope isolate_scope(isolate);
527     v8::Locker lock(isolate);
528     v8::HandleScope handle_scope(isolate);
529     v8::Local<v8::Context> context = v8::Context::New(isolate);
530     CHECK(!context.IsEmpty());
531   }
532   isolate->Dispose();
533   return MemoryInUse();
534 }
535
536
537 TEST(RegressJoinThreadsOnIsolateDeinit) {
538   intptr_t size_limit = ShortLivingIsolate() * 2;
539   for (int i = 0; i < 10; i++) {
540     CHECK_GT(size_limit, ShortLivingIsolate());
541   }
542 }
543
544 #endif  // __linux__ and !USE_SIMULATOR