LCOV - code coverage report
Current view: top level - test/cctest/heap - test-mark-compact.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 143 145 98.6 %
Date: 2019-04-17 Functions: 12 12 100.0 %

          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       26644 : 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       26644 : 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       26644 : 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       18000 : static uintptr_t ReadLong(char* buffer, intptr_t* position, int base) {
     254       18000 :   char* end_address = buffer + *position;
     255       18000 :   uintptr_t result = strtoul(buffer + *position, &end_address, base);
     256       18000 :   CHECK(result != ULONG_MAX || errno != ERANGE);
     257       18000 :   CHECK(end_address > buffer + *position);
     258       18000 :   *position = end_address - buffer;
     259       18000 :   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        6055 :   while (line_start < length) {
     280        6000 :     if (buffer[line_start] == '\n') {
     281        3000 :       line_start++;
     282        3000 :       continue;
     283             :     }
     284        3000 :     intptr_t position = line_start;
     285        3000 :     uintptr_t start = ReadLong(buffer, &position, 16);
     286        3000 :     CHECK_EQ(buffer[position++], '-');
     287        3000 :     uintptr_t end = ReadLong(buffer, &position, 16);
     288        3000 :     CHECK_EQ(buffer[position++], ' ');
     289        3000 :     CHECK(buffer[position] == '-' || buffer[position] == 'r');
     290        3000 :     bool read_permission = (buffer[position++] == 'r');
     291        3000 :     CHECK(buffer[position] == '-' || buffer[position] == 'w');
     292        3000 :     bool write_permission = (buffer[position++] == 'w');
     293        3000 :     CHECK(buffer[position] == '-' || buffer[position] == 'x');
     294        3000 :     bool execute_permission = (buffer[position++] == 'x');
     295        3000 :     CHECK(buffer[position] == 's' || buffer[position] == 'p');
     296        3000 :     bool private_mapping = (buffer[position++] == 'p');
     297        3000 :     CHECK_EQ(buffer[position++], ' ');
     298        3000 :     uintptr_t offset = ReadLong(buffer, &position, 16);
     299             :     USE(offset);
     300        3000 :     CHECK_EQ(buffer[position++], ' ');
     301        3000 :     uintptr_t major = ReadLong(buffer, &position, 16);
     302             :     USE(major);
     303        3000 :     CHECK_EQ(buffer[position++], ':');
     304        3000 :     uintptr_t minor = ReadLong(buffer, &position, 16);
     305             :     USE(minor);
     306        3000 :     CHECK_EQ(buffer[position++], ' ');
     307        3000 :     uintptr_t inode = ReadLong(buffer, &position, 10);
     308       78815 :     while (position < length && buffer[position] != '\n') position++;
     309        3000 :     if ((read_permission || write_permission || execute_permission) &&
     310        2098 :         private_mapping && inode == 0) {
     311        1185 :       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       26644 : 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       26644 : 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       79917 : }  // namespace v8

Generated by: LCOV version 1.10