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