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 "v8.h" 31 #include "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_ = 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->exists() ? 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 OS::SetUp(); 211 Isolate* isolate = Isolate::Current(); 212 isolate->InitializeLoggingAndCounters(); 213 Heap* heap = isolate->heap(); 214 CHECK(heap->ConfigureHeapDefault()); 215 216 size_t reserve_area_size = 1 * MB; 217 size_t initial_commit_area_size, second_commit_area_size; 218 219 for (int i = 0; i < 100; i++) { 220 initial_commit_area_size = Pseudorandom(); 221 second_commit_area_size = Pseudorandom(); 222 223 // With CodeRange. 224 CodeRange* code_range = new CodeRange(isolate); 225 const int code_range_size = 32 * MB; 226 if (!code_range->SetUp(code_range_size)) return; 227 228 VerifyMemoryChunk(isolate, 229 heap, 230 code_range, 231 reserve_area_size, 232 initial_commit_area_size, 233 second_commit_area_size, 234 EXECUTABLE); 235 236 VerifyMemoryChunk(isolate, 237 heap, 238 code_range, 239 reserve_area_size, 240 initial_commit_area_size, 241 second_commit_area_size, 242 NOT_EXECUTABLE); 243 delete code_range; 244 245 // Without CodeRange. 246 code_range = NULL; 247 VerifyMemoryChunk(isolate, 248 heap, 249 code_range, 250 reserve_area_size, 251 initial_commit_area_size, 252 second_commit_area_size, 253 EXECUTABLE); 254 255 VerifyMemoryChunk(isolate, 256 heap, 257 code_range, 258 reserve_area_size, 259 initial_commit_area_size, 260 second_commit_area_size, 261 NOT_EXECUTABLE); 262 } 263 } 264 265 266 TEST(MemoryAllocator) { 267 OS::SetUp(); 268 Isolate* isolate = Isolate::Current(); 269 isolate->InitializeLoggingAndCounters(); 270 Heap* heap = isolate->heap(); 271 CHECK(isolate->heap()->ConfigureHeapDefault()); 272 273 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 274 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 275 heap->MaxExecutableSize())); 276 277 int total_pages = 0; 278 OldSpace faked_space(heap, 279 heap->MaxReserved(), 280 OLD_POINTER_SPACE, 281 NOT_EXECUTABLE); 282 Page* first_page = memory_allocator->AllocatePage( 283 faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE); 284 285 first_page->InsertAfter(faked_space.anchor()->prev_page()); 286 CHECK(first_page->is_valid()); 287 CHECK(first_page->next_page() == faked_space.anchor()); 288 total_pages++; 289 290 for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) { 291 CHECK(p->owner() == &faked_space); 292 } 293 294 // Again, we should get n or n - 1 pages. 295 Page* other = memory_allocator->AllocatePage( 296 faked_space.AreaSize(), &faked_space, NOT_EXECUTABLE); 297 CHECK(other->is_valid()); 298 total_pages++; 299 other->InsertAfter(first_page); 300 int page_count = 0; 301 for (Page* p = first_page; p != faked_space.anchor(); p = p->next_page()) { 302 CHECK(p->owner() == &faked_space); 303 page_count++; 304 } 305 CHECK(total_pages == page_count); 306 307 Page* second_page = first_page->next_page(); 308 CHECK(second_page->is_valid()); 309 memory_allocator->Free(first_page); 310 memory_allocator->Free(second_page); 311 memory_allocator->TearDown(); 312 delete memory_allocator; 313 } 314 315 316 TEST(NewSpace) { 317 OS::SetUp(); 318 Isolate* isolate = Isolate::Current(); 319 isolate->InitializeLoggingAndCounters(); 320 Heap* heap = isolate->heap(); 321 CHECK(heap->ConfigureHeapDefault()); 322 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 323 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 324 heap->MaxExecutableSize())); 325 TestMemoryAllocatorScope test_scope(isolate, memory_allocator); 326 327 NewSpace new_space(heap); 328 329 CHECK(new_space.SetUp(HEAP->ReservedSemiSpaceSize(), 330 HEAP->ReservedSemiSpaceSize())); 331 CHECK(new_space.HasBeenSetUp()); 332 333 while (new_space.Available() >= Page::kMaxNonCodeHeapObjectSize) { 334 Object* obj = 335 new_space.AllocateRaw(Page::kMaxNonCodeHeapObjectSize)-> 336 ToObjectUnchecked(); 337 CHECK(new_space.Contains(HeapObject::cast(obj))); 338 } 339 340 new_space.TearDown(); 341 memory_allocator->TearDown(); 342 delete memory_allocator; 343 } 344 345 346 TEST(OldSpace) { 347 OS::SetUp(); 348 Isolate* isolate = Isolate::Current(); 349 isolate->InitializeLoggingAndCounters(); 350 Heap* heap = isolate->heap(); 351 CHECK(heap->ConfigureHeapDefault()); 352 MemoryAllocator* memory_allocator = new MemoryAllocator(isolate); 353 CHECK(memory_allocator->SetUp(heap->MaxReserved(), 354 heap->MaxExecutableSize())); 355 TestMemoryAllocatorScope test_scope(isolate, memory_allocator); 356 357 OldSpace* s = new OldSpace(heap, 358 heap->MaxOldGenerationSize(), 359 OLD_POINTER_SPACE, 360 NOT_EXECUTABLE); 361 CHECK(s != NULL); 362 363 CHECK(s->SetUp()); 364 365 while (s->Available() > 0) { 366 s->AllocateRaw(Page::kMaxNonCodeHeapObjectSize)->ToObjectUnchecked(); 367 } 368 369 s->TearDown(); 370 delete s; 371 memory_allocator->TearDown(); 372 delete memory_allocator; 373 } 374 375 376 TEST(LargeObjectSpace) { 377 v8::V8::Initialize(); 378 379 LargeObjectSpace* lo = HEAP->lo_space(); 380 CHECK(lo != NULL); 381 382 int lo_size = Page::kPageSize; 383 384 Object* obj = lo->AllocateRaw(lo_size, NOT_EXECUTABLE)->ToObjectUnchecked(); 385 CHECK(obj->IsHeapObject()); 386 387 HeapObject* ho = HeapObject::cast(obj); 388 389 CHECK(lo->Contains(HeapObject::cast(obj))); 390 391 CHECK(lo->FindObject(ho->address()) == obj); 392 393 CHECK(lo->Contains(ho)); 394 395 while (true) { 396 intptr_t available = lo->Available(); 397 { MaybeObject* maybe_obj = lo->AllocateRaw(lo_size, NOT_EXECUTABLE); 398 if (!maybe_obj->ToObject(&obj)) break; 399 } 400 CHECK(lo->Available() < available); 401 }; 402 403 CHECK(!lo->IsEmpty()); 404 405 CHECK(lo->AllocateRaw(lo_size, NOT_EXECUTABLE)->IsFailure()); 406 } 407