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

Generated by: LCOV version 1.10