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 "src/v8.h" 29 #include "test/cctest/cctest.h" 30 31 #include "src/accessors.h" 32 #include "src/api.h" 33 #include "test/cctest/heap/heap-tester.h" 34 #include "test/cctest/heap/utils-inl.h" 35 36 using namespace v8::internal; 37 38 39 AllocationResult v8::internal::HeapTester::AllocateAfterFailures() { 40 Heap* heap = CcTest::heap(); 41 42 // New space. 43 heap->AllocateByteArray(100).ToObjectChecked(); 44 heap->AllocateFixedArray(100, NOT_TENURED).ToObjectChecked(); 45 46 // Make sure we can allocate through optimized allocation functions 47 // for specific kinds. 48 heap->AllocateFixedArray(100).ToObjectChecked(); 49 heap->AllocateHeapNumber(0.42).ToObjectChecked(); 50 Object* object = heap->AllocateJSObject( 51 *CcTest::i_isolate()->object_function()).ToObjectChecked(); 52 heap->CopyJSObject(JSObject::cast(object)).ToObjectChecked(); 53 54 // Old data space. 55 SimulateFullSpace(heap->old_space()); 56 heap->AllocateByteArray(100, TENURED).ToObjectChecked(); 57 58 // Old pointer space. 59 SimulateFullSpace(heap->old_space()); 60 heap->AllocateFixedArray(10000, TENURED).ToObjectChecked(); 61 62 // Large object space. 63 static const int kLargeObjectSpaceFillerLength = 3 * (Page::kPageSize / 10); 64 static const int kLargeObjectSpaceFillerSize = FixedArray::SizeFor( 65 kLargeObjectSpaceFillerLength); 66 CHECK(kLargeObjectSpaceFillerSize > heap->old_space()->AreaSize()); 67 while (heap->OldGenerationSpaceAvailable() > kLargeObjectSpaceFillerSize) { 68 heap->AllocateFixedArray( 69 kLargeObjectSpaceFillerLength, TENURED).ToObjectChecked(); 70 } 71 heap->AllocateFixedArray( 72 kLargeObjectSpaceFillerLength, TENURED).ToObjectChecked(); 73 74 // Map space. 75 SimulateFullSpace(heap->map_space()); 76 int instance_size = JSObject::kHeaderSize; 77 heap->AllocateMap(JS_OBJECT_TYPE, instance_size).ToObjectChecked(); 78 79 // Test that we can allocate in old pointer space and code space. 80 SimulateFullSpace(heap->code_space()); 81 heap->AllocateFixedArray(100, TENURED).ToObjectChecked(); 82 heap->CopyCode(CcTest::i_isolate()->builtins()->builtin( 83 Builtins::kIllegal)).ToObjectChecked(); 84 85 // Return success. 86 return heap->true_value(); 87 } 88 89 90 Handle<Object> v8::internal::HeapTester::TestAllocateAfterFailures() { 91 // Similar to what the CALL_AND_RETRY macro does in the last-resort case, we 92 // are wrapping the allocator function in an AlwaysAllocateScope. Test that 93 // all allocations succeed immediately without any retry. 94 CcTest::heap()->CollectAllAvailableGarbage("panic"); 95 AlwaysAllocateScope scope(CcTest::i_isolate()); 96 return handle(AllocateAfterFailures().ToObjectChecked(), CcTest::i_isolate()); 97 } 98 99 100 HEAP_TEST(StressHandles) { 101 v8::HandleScope scope(CcTest::isolate()); 102 v8::Local<v8::Context> env = v8::Context::New(CcTest::isolate()); 103 env->Enter(); 104 Handle<Object> o = TestAllocateAfterFailures(); 105 CHECK(o->IsTrue()); 106 env->Exit(); 107 } 108 109 110 void TestGetter( 111 v8::Local<v8::Name> name, 112 const v8::PropertyCallbackInfo<v8::Value>& info) { 113 i::Isolate* isolate = reinterpret_cast<i::Isolate*>(info.GetIsolate()); 114 HandleScope scope(isolate); 115 info.GetReturnValue().Set(v8::Utils::ToLocal( 116 v8::internal::HeapTester::TestAllocateAfterFailures())); 117 } 118 119 120 void TestSetter( 121 v8::Local<v8::Name> name, 122 v8::Local<v8::Value> value, 123 const v8::PropertyCallbackInfo<void>& info) { 124 UNREACHABLE(); 125 } 126 127 128 Handle<AccessorInfo> TestAccessorInfo( 129 Isolate* isolate, PropertyAttributes attributes) { 130 Handle<String> name = isolate->factory()->NewStringFromStaticChars("get"); 131 return Accessors::MakeAccessor(isolate, name, &TestGetter, &TestSetter, 132 attributes); 133 } 134 135 136 TEST(StressJS) { 137 Isolate* isolate = CcTest::i_isolate(); 138 Factory* factory = isolate->factory(); 139 v8::HandleScope scope(CcTest::isolate()); 140 v8::Local<v8::Context> env = v8::Context::New(CcTest::isolate()); 141 env->Enter(); 142 Handle<JSFunction> function = factory->NewFunction( 143 factory->function_string()); 144 // Force the creation of an initial map and set the code to 145 // something empty. 146 factory->NewJSObject(function); 147 function->ReplaceCode(CcTest::i_isolate()->builtins()->builtin( 148 Builtins::kEmptyFunction)); 149 // Patch the map to have an accessor for "get". 150 Handle<Map> map(function->initial_map()); 151 Handle<DescriptorArray> instance_descriptors(map->instance_descriptors()); 152 CHECK(instance_descriptors->IsEmpty()); 153 154 PropertyAttributes attrs = NONE; 155 Handle<AccessorInfo> foreign = TestAccessorInfo(isolate, attrs); 156 Map::EnsureDescriptorSlack(map, 1); 157 158 AccessorConstantDescriptor d(Handle<Name>(Name::cast(foreign->name())), 159 foreign, attrs); 160 map->AppendDescriptor(&d); 161 162 // Add the Foo constructor the global object. 163 CHECK(env->Global() 164 ->Set(env, v8::String::NewFromUtf8(CcTest::isolate(), "Foo", 165 v8::NewStringType::kNormal) 166 .ToLocalChecked(), 167 v8::Utils::CallableToLocal(function)) 168 .FromJust()); 169 // Call the accessor through JavaScript. 170 v8::Local<v8::Value> result = 171 v8::Script::Compile( 172 env, v8::String::NewFromUtf8(CcTest::isolate(), "(new Foo).get", 173 v8::NewStringType::kNormal) 174 .ToLocalChecked()) 175 .ToLocalChecked() 176 ->Run(env) 177 .ToLocalChecked(); 178 CHECK_EQ(true, result->BooleanValue(env).FromJust()); 179 env->Exit(); 180 } 181 182 183 // CodeRange test. 184 // Tests memory management in a CodeRange by allocating and freeing blocks, 185 // using a pseudorandom generator to choose block sizes geometrically 186 // distributed between 2 * Page::kPageSize and 2^5 + 1 * Page::kPageSize. 187 // Ensure that the freed chunks are collected and reused by allocating (in 188 // total) more than the size of the CodeRange. 189 190 // This pseudorandom generator does not need to be particularly good. 191 // Use the lower half of the V8::Random() generator. 192 unsigned int Pseudorandom() { 193 static uint32_t lo = 2345; 194 lo = 18273 * (lo & 0xFFFF) + (lo >> 16); // Provably not 0. 195 return lo & 0xFFFF; 196 } 197 198 199 // Plain old data class. Represents a block of allocated memory. 200 class Block { 201 public: 202 Block(Address base_arg, int size_arg) 203 : base(base_arg), size(size_arg) {} 204 205 Address base; 206 int size; 207 }; 208 209 210 TEST(CodeRange) { 211 const size_t code_range_size = 32*MB; 212 CcTest::InitializeVM(); 213 CodeRange code_range(reinterpret_cast<Isolate*>(CcTest::isolate())); 214 code_range.SetUp(code_range_size + 215 kReservedCodeRangePages * v8::base::OS::CommitPageSize()); 216 size_t current_allocated = 0; 217 size_t total_allocated = 0; 218 List< ::Block> blocks(1000); 219 220 while (total_allocated < 5 * code_range_size) { 221 if (current_allocated < code_range_size / 10) { 222 // Allocate a block. 223 // Geometrically distributed sizes, greater than 224 // Page::kMaxRegularHeapObjectSize (which is greater than code page area). 225 // TODO(gc): instead of using 3 use some contant based on code_range_size 226 // kMaxRegularHeapObjectSize. 227 size_t requested = 228 (Page::kMaxRegularHeapObjectSize << (Pseudorandom() % 3)) + 229 Pseudorandom() % 5000 + 1; 230 size_t allocated = 0; 231 232 // The request size has to be at least 2 code guard pages larger than the 233 // actual commit size. 234 Address base = code_range.AllocateRawMemory( 235 requested, requested - (2 * MemoryAllocator::CodePageGuardSize()), 236 &allocated); 237 CHECK(base != NULL); 238 blocks.Add(::Block(base, static_cast<int>(allocated))); 239 current_allocated += static_cast<int>(allocated); 240 total_allocated += static_cast<int>(allocated); 241 } else { 242 // Free a block. 243 int index = Pseudorandom() % blocks.length(); 244 code_range.FreeRawMemory(blocks[index].base, blocks[index].size); 245 current_allocated -= blocks[index].size; 246 if (index < blocks.length() - 1) { 247 blocks[index] = blocks.RemoveLast(); 248 } else { 249 blocks.RemoveLast(); 250 } 251 } 252 } 253 } 254