1 // Copyright 2011 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 #include "src/v8.h" 31 #include "test/cctest/cctest.h" 32 33 using namespace v8::internal; 34 35 #if 0 36 static void VerifyRegionMarking(Address page_start) { 37 #ifdef ENABLE_CARDMARKING_WRITE_BARRIER 38 Page* p = Page::FromAddress(page_start); 39 40 p->SetRegionMarks(Page::kAllRegionsCleanMarks); 41 42 for (Address addr = p->ObjectAreaStart(); 43 addr < p->ObjectAreaEnd(); 44 addr += kPointerSize) { 45 CHECK(!Page::FromAddress(addr)->IsRegionDirty(addr)); 46 } 47 48 for (Address addr = p->ObjectAreaStart(); 49 addr < p->ObjectAreaEnd(); 50 addr += kPointerSize) { 51 Page::FromAddress(addr)->MarkRegionDirty(addr); 52 } 53 54 for (Address addr = p->ObjectAreaStart(); 55 addr < p->ObjectAreaEnd(); 56 addr += kPointerSize) { 57 CHECK(Page::FromAddress(addr)->IsRegionDirty(addr)); 58 } 59 #endif 60 } 61 #endif 62 63 64 // TODO(gc) you can no longer allocate pages like this. Details are hidden. 65 #if 0 66 TEST(Page) { 67 byte* mem = NewArray<byte>(2*Page::kPageSize); 68 CHECK(mem != NULL); 69 70 Address start = reinterpret_cast<Address>(mem); 71 Address page_start = RoundUp(start, Page::kPageSize); 72 73 Page* p = Page::FromAddress(page_start); 74 // Initialized Page has heap pointer, normally set by memory_allocator. 75 p->heap_ = CcTest::heap(); 76 CHECK(p->address() == page_start); 77 CHECK(p->is_valid()); 78 79 p->opaque_header = 0; 80 p->SetIsLargeObjectPage(false); 81 CHECK(!p->next_page()->is_valid()); 82 83 CHECK(p->ObjectAreaStart() == page_start + Page::kObjectStartOffset); 84 CHECK(p->ObjectAreaEnd() == page_start + Page::kPageSize); 85 86 CHECK(p->Offset(page_start + Page::kObjectStartOffset) == 87 Page::kObjectStartOffset); 88 CHECK(p->Offset(page_start + Page::kPageSize) == Page::kPageSize); 89 90 CHECK(p->OffsetToAddress(Page::kObjectStartOffset) == p->ObjectAreaStart()); 91 CHECK(p->OffsetToAddress(Page::kPageSize) == p->ObjectAreaEnd()); 92 93 // test region marking 94 VerifyRegionMarking(page_start); 95 96 DeleteArray(mem); 97 } 98 #endif 99 100 101 namespace v8 { 102 namespace internal { 103 104 // Temporarily sets a given allocator in an isolate. 105 class TestMemoryAllocatorScope { 106 public: 107 TestMemoryAllocatorScope(Isolate* isolate, MemoryAllocator* allocator) 108 : isolate_(isolate), 109 old_allocator_(isolate->memory_allocator_) { 110 isolate->memory_allocator_ = allocator; 111 } 112 113 ~TestMemoryAllocatorScope() { 114 isolate_->memory_allocator_ = old_allocator_; 115 } 116 117 private: 118 Isolate* isolate_; 119 MemoryAllocator* old_allocator_; 120 121 DISALLOW_COPY_AND_ASSIGN(TestMemoryAllocatorScope); 122 }; 123 124 125 // Temporarily sets a given code range in an isolate. 126 class TestCodeRangeScope { 127 public: 128 TestCodeRangeScope(Isolate* isolate, CodeRange* code_range) 129 : isolate_(isolate), 130 old_code_range_(isolate->code_range_) { 131 isolate->code_range_ = code_range; 132 } 133 134 ~TestCodeRangeScope() { 135 isolate_->code_range_ = old_code_range_; 136 } 137 138 private: 139 Isolate* isolate_; 140 CodeRange* old_code_range_; 141 142 DISALLOW_COPY_AND_ASSIGN(TestCodeRangeScope); 143 }; 144 145 } } // namespace v8::internal 146 147 148 static void VerifyMemoryChunk(Isolate* isolate, 149 Heap* heap, 150 CodeRange* code_range, 151 size_t reserve_area_size, 152 size_t commit_area_size, 153 size_t second_commit_area_size, 154 Executability executable) { 155 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 156 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 157 heap->MaxExecutableSize())); 158 TestMemoryAllocatorScope test_allocator_scope(isolate, memory_allocator); 159 TestCodeRangeScope test_code_range_scope(isolate, code_range); 160 161 size_t header_size = (executable == EXECUTABLE) 162 ? MemoryAllocator::CodePageGuardStartOffset() 163 : MemoryChunk::kObjectStartOffset; 164 size_t guard_size = (executable == EXECUTABLE) 165 ? MemoryAllocator::CodePageGuardSize() 166 : 0; 167 168 MemoryChunk* memory_chunk = memory_allocator->AllocateChunk(reserve_area_size, 169 commit_area_size, 170 executable, 171 NULL); 172 size_t alignment = code_range != NULL && code_range->valid() ? 173 MemoryChunk::kAlignment : OS::CommitPageSize(); 174 size_t reserved_size = ((executable == EXECUTABLE)) 175 ? RoundUp(header_size + guard_size + reserve_area_size + guard_size, 176 alignment) 177 : RoundUp(header_size + reserve_area_size, OS::CommitPageSize()); 178 CHECK(memory_chunk->size() == reserved_size); 179 CHECK(memory_chunk->area_start() < memory_chunk->address() + 180 memory_chunk->size()); 181 CHECK(memory_chunk->area_end() <= memory_chunk->address() + 182 memory_chunk->size()); 183 CHECK(static_cast<size_t>(memory_chunk->area_size()) == commit_area_size); 184 185 Address area_start = memory_chunk->area_start(); 186 187 memory_chunk->CommitArea(second_commit_area_size); 188 CHECK(area_start == memory_chunk->area_start()); 189 CHECK(memory_chunk->area_start() < memory_chunk->address() + 190 memory_chunk->size()); 191 CHECK(memory_chunk->area_end() <= memory_chunk->address() + 192 memory_chunk->size()); 193 CHECK(static_cast<size_t>(memory_chunk->area_size()) == 194 second_commit_area_size); 195 196 memory_allocator->Free(memory_chunk); 197 memory_allocator->TearDown(); 198 delete memory_allocator; 199 } 200 201 202 static unsigned int Pseudorandom() { 203 static uint32_t lo = 2345; 204 lo = 18273 * (lo & 0xFFFFF) + (lo >> 16); 205 return lo & 0xFFFFF; 206 } 207 208 209 TEST(MemoryChunk) { 210 Isolate* isolate = CcTest::i_isolate(); 211 isolate->InitializeLoggingAndCounters(); 212 Heap* heap = isolate->heap(); 213 CHECK(heap->ConfigureHeapDefault()); 214 215 size_t reserve_area_size = 1 * MB; 216 size_t initial_commit_area_size, second_commit_area_size; 217 218 for (int i = 0; i < 100; i++) { 219 initial_commit_area_size = Pseudorandom(); 220 second_commit_area_size = Pseudorandom(); 221 222 // With CodeRange. 223 CodeRange* code_range = new CodeRange(isolate); 224 const size_t code_range_size = 32 * MB; 225 if (!code_range->SetUp(code_range_size)) return; 226 227 VerifyMemoryChunk(isolate, 228 heap, 229 code_range, 230 reserve_area_size, 231 initial_commit_area_size, 232 second_commit_area_size, 233 EXECUTABLE); 234 235 VerifyMemoryChunk(isolate, 236 heap, 237 code_range, 238 reserve_area_size, 239 initial_commit_area_size, 240 second_commit_area_size, 241 NOT_EXECUTABLE); 242 delete code_range; 243 244 // Without CodeRange. 245 code_range = NULL; 246 VerifyMemoryChunk(isolate, 247 heap, 248 code_range, 249 reserve_area_size, 250 initial_commit_area_size, 251 second_commit_area_size, 252 EXECUTABLE); 253 254 VerifyMemoryChunk(isolate, 255 heap, 256 code_range, 257 reserve_area_size, 258 initial_commit_area_size, 259 second_commit_area_size, 260 NOT_EXECUTABLE); 261 } 262 } 263 264 265 TEST(MemoryAllocator) { 266 Isolate* isolate = CcTest::i_isolate(); 267 isolate->InitializeLoggingAndCounters(); 268 Heap* heap = isolate->heap(); 269 CHECK(isolate->heap()->ConfigureHeapDefault()); 270 271 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 272 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 273 heap->MaxExecutableSize())); 274 275 int total_pages = 0; 276 OldSpace faked_space(heap, 277 heap->MaxReserved(), 278 OLD_POINTER_SPACE, 279 NOT_EXECUTABLE); 280 Page* first_page = memory_allocator->AllocatePage( 281 faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE); 282 283 first_page->InsertAfter(faked_space.anchor()->prev_page()); 284 CHECK(first_page->is_valid()); 285 CHECK(first_page->next_page() == faked_space.anchor()); 286 total_pages++; 287 288 for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) { 289 CHECK(p->owner() == &faked_space); 290 } 291 292 // Again, we should get n or n - 1 pages. 293 Page* other = memory_allocator->AllocatePage( 294 faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE); 295 CHECK(other->is_valid()); 296 total_pages++; 297 other->InsertAfter(first_page); 298 int page_count = 0; 299 for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) { 300 CHECK(p->owner() == &faked_space); 301 page_count++; 302 } 303 CHECK(total_pages == page_count); 304 305 Page* second_page = first_page->next_page(); 306 CHECK(second_page->is_valid()); 307 memory_allocator->Free(first_page); 308 memory_allocator->Free(second_page); 309 memory_allocator->TearDown(); 310 delete memory_allocator; 311 } 312 313 314 TEST(NewSpace) { 315 Isolate* isolate = CcTest::i_isolate(); 316 isolate->InitializeLoggingAndCounters(); 317 Heap* heap = isolate->heap(); 318 CHECK(heap->ConfigureHeapDefault()); 319 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 320 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 321 heap->MaxExecutableSize())); 322 TestMemoryAllocatorScope test_scope(isolate, memory_allocator); 323 324 NewSpace new_space(heap); 325 326 CHECK(new_space.SetUp(CcTest::heap()->ReservedSemiSpaceSize(), 327 CcTest::heap()->ReservedSemiSpaceSize())); 328 CHECK(new_space.HasBeenSetUp()); 329 330 while (new_space.Available() >= Page::kMaxRegularHeapObjectSize) { 331 Object* obj = new_space.AllocateRaw( 332 Page::kMaxRegularHeapObjectSize).ToObjectChecked(); 333 CHECK(new_space.Contains(HeapObject::cast(obj))); 334 } 335 336 new_space.TearDown(); 337 memory_allocator->TearDown(); 338 delete memory_allocator; 339 } 340 341 342 TEST(OldSpace) { 343 Isolate* isolate = CcTest::i_isolate(); 344 isolate->InitializeLoggingAndCounters(); 345 Heap* heap = isolate->heap(); 346 CHECK(heap->ConfigureHeapDefault()); 347 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 348 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 349 heap->MaxExecutableSize())); 350 TestMemoryAllocatorScope test_scope(isolate, memory_allocator); 351 352 OldSpace* s = new OldSpace(heap, 353 heap->MaxOldGenerationSize(), 354 OLD_POINTER_SPACE, 355 NOT_EXECUTABLE); 356 CHECK(s != NULL); 357 358 CHECK(s->SetUp()); 359 360 while (s->Available() > 0) { 361 s->AllocateRaw(Page::kMaxRegularHeapObjectSize).ToObjectChecked(); 362 } 363 364 s->TearDown(); 365 delete s; 366 memory_allocator->TearDown(); 367 delete memory_allocator; 368 } 369 370 371 TEST(LargeObjectSpace) { 372 v8::V8::Initialize(); 373 374 LargeObjectSpace* lo = CcTest::heap()->lo_space(); 375 CHECK(lo != NULL); 376 377 int lo_size = Page::kPageSize; 378 379 Object* obj = lo->AllocateRaw(lo_size, NOT_EXECUTABLE).ToObjectChecked(); 380 CHECK(obj->IsHeapObject()); 381 382 HeapObject* ho = HeapObject::cast(obj); 383 384 CHECK(lo->Contains(HeapObject::cast(obj))); 385 386 CHECK(lo->FindObject(ho->address()) == obj); 387 388 CHECK(lo->Contains(ho)); 389 390 while (true) { 391 intptr_t available = lo->Available(); 392 { AllocationResult allocation = lo->AllocateRaw(lo_size, NOT_EXECUTABLE); 393 if (allocation.IsRetry()) break; 394 } 395 CHECK(lo->Available() < available); 396 } 397 398 CHECK(!lo->IsEmpty()); 399 400 CHECK(lo->AllocateRaw(lo_size, NOT_EXECUTABLE).IsRetry()); 401 } 402 403 404 TEST(SizeOfFirstPageIsLargeEnough) { 405 if (i::FLAG_always_opt) return; 406 CcTest::InitializeVM(); 407 Isolate* isolate = CcTest::i_isolate(); 408 409 // Freshly initialized VM gets by with one page per space. 410 for (int i = FIRST_PAGED_SPACE; i <= LAST_PAGED_SPACE; i++) { 411 // Debug code can be very large, so skip CODE_SPACE if we are generating it. 412 if (i == CODE_SPACE && i::FLAG_debug_code) continue; 413 CHECK_EQ(1, isolate->heap()->paged_space(i)->CountTotalPages()); 414 } 415 416 // Executing the empty script gets by with one page per space. 417 HandleScope scope(isolate); 418 CompileRun("/*empty*/"); 419 for (int i = FIRST_PAGED_SPACE; i <= LAST_PAGED_SPACE; i++) { 420 // Debug code can be very large, so skip CODE_SPACE if we are generating it. 421 if (i == CODE_SPACE && i::FLAG_debug_code) continue; 422 CHECK_EQ(1, isolate->heap()->paged_space(i)->CountTotalPages()); 423 } 424 425 // No large objects required to perform the above steps. 426 CHECK(isolate->heap()->lo_space()->IsEmpty()); 427 } 428