%PDF- %PDF-
Direktori : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/ |
Current File : /home/vacivi36/vittasync.vacivitta.com.br/vittasync/node/deps/v8/src/heap/memory-chunk.cc |
// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/heap/memory-chunk.h" #include "src/base/logging.h" #include "src/base/platform/mutex.h" #include "src/base/platform/platform.h" #include "src/common/globals.h" #include "src/heap/basic-memory-chunk.h" #include "src/heap/incremental-marking.h" #include "src/heap/marking-state-inl.h" #include "src/heap/memory-allocator.h" #include "src/heap/memory-chunk-inl.h" #include "src/heap/memory-chunk-layout.h" #include "src/heap/spaces.h" #include "src/objects/heap-object.h" namespace v8 { namespace internal { void MemoryChunk::DiscardUnusedMemory(Address addr, size_t size) { base::AddressRegion memory_area = MemoryAllocator::ComputeDiscardMemoryArea(addr, size); if (memory_area.size() != 0) { MemoryAllocator* memory_allocator = heap_->memory_allocator(); v8::PageAllocator* page_allocator = memory_allocator->page_allocator(owner_identity()); CHECK(page_allocator->DiscardSystemPages( reinterpret_cast<void*>(memory_area.begin()), memory_area.size())); } } void MemoryChunk::InitializationMemoryFence() { base::SeqCst_MemoryFence(); #ifdef THREAD_SANITIZER // Since TSAN does not process memory fences, we use the following annotation // to tell TSAN that there is no data race when emitting a // InitializationMemoryFence. Note that the other thread still needs to // perform MemoryChunk::synchronized_heap(). base::Release_Store(reinterpret_cast<base::AtomicWord*>(&heap_), reinterpret_cast<base::AtomicWord>(heap_)); #endif } void MemoryChunk::DecrementWriteUnprotectCounterAndMaybeSetPermissions( PageAllocator::Permission permission) { DCHECK(!V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT); DCHECK(permission == PageAllocator::kRead || permission == PageAllocator::kReadExecute); DCHECK(IsFlagSet(MemoryChunk::IS_EXECUTABLE)); DCHECK(IsAnyCodeSpace(owner_identity())); page_protection_change_mutex_->AssertHeld(); Address protect_start = address() + MemoryChunkLayout::ObjectPageOffsetInCodePage(); size_t page_size = MemoryAllocator::GetCommitPageSize(); DCHECK(IsAligned(protect_start, page_size)); size_t protect_size = RoundUp(area_size(), page_size); CHECK(reservation_.SetPermissions(protect_start, protect_size, permission)); } void MemoryChunk::SetReadable() { DecrementWriteUnprotectCounterAndMaybeSetPermissions(PageAllocator::kRead); } void MemoryChunk::SetReadAndExecutable() { DCHECK(!v8_flags.jitless); DecrementWriteUnprotectCounterAndMaybeSetPermissions( PageAllocator::kReadExecute); } base::MutexGuard MemoryChunk::SetCodeModificationPermissions() { DCHECK(!V8_HEAP_USE_PTHREAD_JIT_WRITE_PROTECT); DCHECK(IsFlagSet(MemoryChunk::IS_EXECUTABLE)); DCHECK(IsAnyCodeSpace(owner_identity())); // Incrementing the write_unprotect_counter_ and changing the page // protection mode has to be atomic. base::MutexGuard guard(page_protection_change_mutex_); Address unprotect_start = address() + MemoryChunkLayout::ObjectPageOffsetInCodePage(); size_t page_size = MemoryAllocator::GetCommitPageSize(); DCHECK(IsAligned(unprotect_start, page_size)); size_t unprotect_size = RoundUp(area_size(), page_size); // We may use RWX pages to write code. Some CPUs have optimisations to push // updates to code to the icache through a fast path, and they may filter // updates based on the written memory being executable. CHECK(reservation_.SetPermissions( unprotect_start, unprotect_size, MemoryChunk::GetCodeModificationPermission())); return guard; } void MemoryChunk::SetDefaultCodePermissions() { if (v8_flags.jitless) { SetReadable(); } else { SetReadAndExecutable(); } } MemoryChunk::MemoryChunk(Heap* heap, BaseSpace* space, size_t chunk_size, Address area_start, Address area_end, VirtualMemory reservation, Executability executable, PageSize page_size) : BasicMemoryChunk(heap, space, chunk_size, area_start, area_end, std::move(reservation)), mutex_(new base::Mutex()), shared_mutex_(new base::SharedMutex()), page_protection_change_mutex_(new base::Mutex()) { DCHECK_NE(space->identity(), RO_SPACE); if (executable == EXECUTABLE) { SetFlag(IS_EXECUTABLE); } if (page_size == PageSize::kRegular) { active_system_pages_ = new ActiveSystemPages; active_system_pages_->Init(MemoryChunkLayout::kMemoryChunkHeaderSize, MemoryAllocator::GetCommitPageSizeBits(), size()); } else { // We do not track active system pages for large pages. active_system_pages_ = nullptr; } // All pages of a shared heap need to be marked with this flag. if (owner()->identity() == SHARED_SPACE || owner()->identity() == SHARED_LO_SPACE) { SetFlag(MemoryChunk::IN_WRITABLE_SHARED_SPACE); } #ifdef DEBUG ValidateOffsets(this); #endif } size_t MemoryChunk::CommittedPhysicalMemory() const { if (!base::OS::HasLazyCommits() || IsLargePage()) return size(); return active_system_pages_->Size(MemoryAllocator::GetCommitPageSizeBits()); } void MemoryChunk::SetOldGenerationPageFlags(MarkingMode marking_mode) { if (marking_mode == MarkingMode::kMajorMarking) { SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); SetFlag(MemoryChunk::INCREMENTAL_MARKING); } else if (owner_identity() == SHARED_SPACE || owner_identity() == SHARED_LO_SPACE) { // We need to track pointers into the SHARED_SPACE for OLD_TO_SHARED. SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); // No need to track OLD_TO_NEW or OLD_TO_SHARED within the shared space. ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); ClearFlag(MemoryChunk::INCREMENTAL_MARKING); } else { ClearFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); if (marking_mode == MarkingMode::kMinorMarking) { SetFlag(MemoryChunk::INCREMENTAL_MARKING); } else { ClearFlags(MemoryChunk::INCREMENTAL_MARKING); } } } void MemoryChunk::SetYoungGenerationPageFlags(MarkingMode marking_mode) { SetFlag(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING); if (marking_mode != MarkingMode::kNoMarking) { SetFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); SetFlag(MemoryChunk::INCREMENTAL_MARKING); } else { ClearFlag(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING); ClearFlag(MemoryChunk::INCREMENTAL_MARKING); } } // ----------------------------------------------------------------------------- // MemoryChunk implementation void MemoryChunk::ReleaseAllocatedMemoryNeededForWritableChunk() { DCHECK(SweepingDone()); if (mutex_ != nullptr) { delete mutex_; mutex_ = nullptr; } if (shared_mutex_) { delete shared_mutex_; shared_mutex_ = nullptr; } if (page_protection_change_mutex_ != nullptr) { delete page_protection_change_mutex_; page_protection_change_mutex_ = nullptr; } if (active_system_pages_ != nullptr) { delete active_system_pages_; active_system_pages_ = nullptr; } possibly_empty_buckets_.Release(); ReleaseSlotSet(OLD_TO_NEW); ReleaseSlotSet(OLD_TO_NEW_BACKGROUND); ReleaseSlotSet(OLD_TO_OLD); ReleaseSlotSet(OLD_TO_CODE); ReleaseSlotSet(OLD_TO_SHARED); ReleaseTypedSlotSet(OLD_TO_NEW); ReleaseTypedSlotSet(OLD_TO_OLD); ReleaseTypedSlotSet(OLD_TO_SHARED); if (!IsLargePage()) { Page* page = static_cast<Page*>(this); page->ReleaseFreeListCategories(); } } void MemoryChunk::ReleaseAllAllocatedMemory() { ReleaseAllocatedMemoryNeededForWritableChunk(); } SlotSet* MemoryChunk::AllocateSlotSet(RememberedSetType type) { SlotSet* new_slot_set = SlotSet::Allocate(buckets()); SlotSet* old_slot_set = base::AsAtomicPointer::AcquireRelease_CompareAndSwap( &slot_set_[type], nullptr, new_slot_set); if (old_slot_set) { SlotSet::Delete(new_slot_set, buckets()); new_slot_set = old_slot_set; } DCHECK_NOT_NULL(new_slot_set); return new_slot_set; } void MemoryChunk::ReleaseSlotSet(RememberedSetType type) { SlotSet* slot_set = slot_set_[type]; if (slot_set) { slot_set_[type] = nullptr; SlotSet::Delete(slot_set, buckets()); } } TypedSlotSet* MemoryChunk::AllocateTypedSlotSet(RememberedSetType type) { TypedSlotSet* typed_slot_set = new TypedSlotSet(address()); TypedSlotSet* old_value = base::AsAtomicPointer::Release_CompareAndSwap( &typed_slot_set_[type], nullptr, typed_slot_set); if (old_value) { delete typed_slot_set; typed_slot_set = old_value; } DCHECK(typed_slot_set); return typed_slot_set; } void MemoryChunk::ReleaseTypedSlotSet(RememberedSetType type) { TypedSlotSet* typed_slot_set = typed_slot_set_[type]; if (typed_slot_set) { typed_slot_set_[type] = nullptr; delete typed_slot_set; } } bool MemoryChunk::ContainsAnySlots() const { for (int rs_type = 0; rs_type < NUMBER_OF_REMEMBERED_SET_TYPES; rs_type++) { if (slot_set_[rs_type] || typed_slot_set_[rs_type]) { return true; } } return false; } void MemoryChunk::ClearLiveness() { marking_bitmap()->Clear<AccessMode::NON_ATOMIC>(); SetLiveBytes(0); } int MemoryChunk::ComputeFreeListsLength() { int length = 0; for (int cat = kFirstCategory; cat <= owner()->free_list()->last_category(); cat++) { if (categories_[cat] != nullptr) { length += categories_[cat]->FreeListLength(); } } return length; } #ifdef DEBUG void MemoryChunk::ValidateOffsets(MemoryChunk* chunk) { // Note that we cannot use offsetof because MemoryChunk is not a POD. DCHECK_EQ(reinterpret_cast<Address>(&chunk->slot_set_) - chunk->address(), MemoryChunkLayout::kSlotSetOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->progress_bar_) - chunk->address(), MemoryChunkLayout::kProgressBarOffset); DCHECK_EQ( reinterpret_cast<Address>(&chunk->live_byte_count_) - chunk->address(), MemoryChunkLayout::kLiveByteCountOffset); DCHECK_EQ( reinterpret_cast<Address>(&chunk->typed_slot_set_) - chunk->address(), MemoryChunkLayout::kTypedSlotSetOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->mutex_) - chunk->address(), MemoryChunkLayout::kMutexOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->shared_mutex_) - chunk->address(), MemoryChunkLayout::kSharedMutexOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->concurrent_sweeping_) - chunk->address(), MemoryChunkLayout::kConcurrentSweepingOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->page_protection_change_mutex_) - chunk->address(), MemoryChunkLayout::kPageProtectionChangeMutexOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->external_backing_store_bytes_) - chunk->address(), MemoryChunkLayout::kExternalBackingStoreBytesOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->list_node_) - chunk->address(), MemoryChunkLayout::kListNodeOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->categories_) - chunk->address(), MemoryChunkLayout::kCategoriesOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->possibly_empty_buckets_) - chunk->address(), MemoryChunkLayout::kPossiblyEmptyBucketsOffset); DCHECK_EQ(reinterpret_cast<Address>(&chunk->active_system_pages_) - chunk->address(), MemoryChunkLayout::kActiveSystemPagesOffset); DCHECK_EQ( reinterpret_cast<Address>(&chunk->allocated_lab_size_) - chunk->address(), MemoryChunkLayout::kAllocatedLabSizeOffset); DCHECK_EQ( reinterpret_cast<Address>(&chunk->age_in_new_space_) - chunk->address(), MemoryChunkLayout::kAgeInNewSpaceOffset); } #endif } // namespace internal } // namespace v8