| Index: Source/platform/heap/Heap.cpp
|
| diff --git a/Source/platform/heap/Heap.cpp b/Source/platform/heap/Heap.cpp
|
| index d4a3a5dbbfdaa7b2b8d0e5cb974342bf94cf1c9f..5cd8cdbed8da3cf42c369a32a89ad59964f7a4ae 100644
|
| --- a/Source/platform/heap/Heap.cpp
|
| +++ b/Source/platform/heap/Heap.cpp
|
| @@ -32,7 +32,6 @@
|
| #include "platform/heap/Heap.h"
|
|
|
| #include "platform/ScriptForbiddenScope.h"
|
| -#include "platform/Task.h"
|
| #include "platform/TraceEvent.h"
|
| #include "platform/heap/BlinkGCMemoryDumpProvider.h"
|
| #include "platform/heap/CallbackStack.h"
|
| @@ -45,10 +44,8 @@
|
| #include "public/platform/WebMemoryAllocatorDump.h"
|
| #include "public/platform/WebProcessMemoryDump.h"
|
| #include "wtf/Assertions.h"
|
| -#include "wtf/ContainerAnnotations.h"
|
| #include "wtf/LeakAnnotations.h"
|
| #include "wtf/MainThread.h"
|
| -#include "wtf/PageAllocator.h"
|
| #include "wtf/Partitions.h"
|
| #include "wtf/PassOwnPtr.h"
|
| #if ENABLE(GC_PROFILING)
|
| @@ -61,57 +58,8 @@
|
| #include <utility>
|
| #endif
|
|
|
| -#if OS(POSIX)
|
| -#include <sys/mman.h>
|
| -#include <unistd.h>
|
| -#elif OS(WIN)
|
| -#include <windows.h>
|
| -#endif
|
| -
|
| -#ifdef ANNOTATE_CONTIGUOUS_CONTAINER
|
| -// FIXME: have ContainerAnnotations.h define an ENABLE_-style name instead.
|
| -#define ENABLE_ASAN_CONTAINER_ANNOTATIONS 1
|
| -
|
| -// When finalizing a non-inlined vector backing store/container, remove
|
| -// its contiguous container annotation. Required as it will not be destructed
|
| -// from its Vector.
|
| -#define ASAN_RETIRE_CONTAINER_ANNOTATION(object, objectSize) \
|
| - do { \
|
| - BasePage* page = pageFromObject(object); \
|
| - ASSERT(page); \
|
| - bool isContainer = ThreadState::isVectorHeapIndex(page->heap()->heapIndex()); \
|
| - if (!isContainer && page->isLargeObjectPage()) \
|
| - isContainer = static_cast<LargeObjectPage*>(page)->isVectorBackingPage(); \
|
| - if (isContainer) \
|
| - ANNOTATE_DELETE_BUFFER(object, objectSize, 0); \
|
| - } while (0)
|
| -
|
| -// A vector backing store represented by a large object is marked
|
| -// so that when it is finalized, its ASan annotation will be
|
| -// correctly retired.
|
| -#define ASAN_MARK_LARGE_VECTOR_CONTAINER(heap, largeObject) \
|
| - if (ThreadState::isVectorHeapIndex(heap->heapIndex())) { \
|
| - BasePage* largePage = pageFromObject(largeObject); \
|
| - ASSERT(largePage->isLargeObjectPage()); \
|
| - static_cast<LargeObjectPage*>(largePage)->setIsVectorBackingPage(); \
|
| - }
|
| -#else
|
| -#define ENABLE_ASAN_CONTAINER_ANNOTATIONS 0
|
| -#define ASAN_RETIRE_CONTAINER_ANNOTATION(payload, payloadSize)
|
| -#define ASAN_MARK_LARGE_VECTOR_CONTAINER(heap, largeObject)
|
| -#endif
|
| -
|
| namespace blink {
|
|
|
| -#if ENABLE(GC_PROFILING)
|
| -static String classOf(const void* object)
|
| -{
|
| - if (const GCInfo* gcInfo = Heap::findGCInfo(reinterpret_cast<Address>(const_cast<void*>(object))))
|
| - return gcInfo->m_className;
|
| - return "unknown";
|
| -}
|
| -#endif
|
| -
|
| class GCForbiddenScope final {
|
| public:
|
| explicit GCForbiddenScope(ThreadState* state)
|
| @@ -197,1724 +145,6 @@ private:
|
| bool m_parkedAllThreads; // False if we fail to park all threads
|
| };
|
|
|
| -#if ENABLE(ASSERT)
|
| -NO_SANITIZE_ADDRESS
|
| -void HeapObjectHeader::zapMagic()
|
| -{
|
| - ASSERT(checkHeader());
|
| - m_magic = zappedMagic;
|
| -}
|
| -#endif
|
| -
|
| -void HeapObjectHeader::finalize(Address object, size_t objectSize)
|
| -{
|
| - const GCInfo* gcInfo = Heap::gcInfo(gcInfoIndex());
|
| - if (gcInfo->hasFinalizer())
|
| - gcInfo->m_finalize(object);
|
| -
|
| - ASAN_RETIRE_CONTAINER_ANNOTATION(object, objectSize);
|
| -}
|
| -
|
| -BaseHeap::BaseHeap(ThreadState* state, int index)
|
| - : m_firstPage(nullptr)
|
| - , m_firstUnsweptPage(nullptr)
|
| - , m_threadState(state)
|
| - , m_index(index)
|
| -{
|
| -}
|
| -
|
| -BaseHeap::~BaseHeap()
|
| -{
|
| - ASSERT(!m_firstPage);
|
| - ASSERT(!m_firstUnsweptPage);
|
| -}
|
| -
|
| -void BaseHeap::cleanupPages()
|
| -{
|
| - clearFreeLists();
|
| -
|
| - ASSERT(!m_firstUnsweptPage);
|
| - // Add the BaseHeap's pages to the orphanedPagePool.
|
| - for (BasePage* page = m_firstPage; page; page = page->next()) {
|
| - Heap::decreaseAllocatedSpace(page->size());
|
| - Heap::orphanedPagePool()->addOrphanedPage(heapIndex(), page);
|
| - }
|
| - m_firstPage = nullptr;
|
| -}
|
| -
|
| -void BaseHeap::takeSnapshot(const String& dumpBaseName, ThreadState::GCSnapshotInfo& info)
|
| -{
|
| - // |dumpBaseName| at this point is "blink_gc/thread_X/heaps/HeapName"
|
| - WebMemoryAllocatorDump* allocatorDump = BlinkGCMemoryDumpProvider::instance()->createMemoryAllocatorDumpForCurrentGC(dumpBaseName);
|
| - size_t pageIndex = 0;
|
| - size_t heapTotalFreeSize = 0;
|
| - size_t heapTotalFreeCount = 0;
|
| - for (BasePage* page = m_firstUnsweptPage; page; page = page->next()) {
|
| - size_t heapPageFreeSize = 0;
|
| - size_t heapPageFreeCount = 0;
|
| - page->takeSnapshot(dumpBaseName, pageIndex, info, &heapPageFreeSize, &heapPageFreeCount);
|
| - heapTotalFreeSize += heapPageFreeSize;
|
| - heapTotalFreeCount += heapPageFreeCount;
|
| - pageIndex++;
|
| - }
|
| - allocatorDump->AddScalar("blink_page_count", "objects", pageIndex);
|
| -
|
| - // When taking a full dump (w/ freelist), both the /buckets and /pages
|
| - // report their free size but they are not meant to be added together.
|
| - // Therefore, here we override the free_size of the parent heap to be
|
| - // equal to the free_size of the sum of its heap pages.
|
| - allocatorDump->AddScalar("free_size", "bytes", heapTotalFreeSize);
|
| - allocatorDump->AddScalar("free_count", "objects", heapTotalFreeCount);
|
| -}
|
| -
|
| -#if ENABLE(ASSERT) || ENABLE(GC_PROFILING)
|
| -BasePage* BaseHeap::findPageFromAddress(Address address)
|
| -{
|
| - for (BasePage* page = m_firstPage; page; page = page->next()) {
|
| - if (page->contains(address))
|
| - return page;
|
| - }
|
| - for (BasePage* page = m_firstUnsweptPage; page; page = page->next()) {
|
| - if (page->contains(address))
|
| - return page;
|
| - }
|
| - return nullptr;
|
| -}
|
| -#endif
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -#define GC_PROFILE_HEAP_PAGE_SNAPSHOT_THRESHOLD 0
|
| -void BaseHeap::snapshot(TracedValue* json, ThreadState::SnapshotInfo* info)
|
| -{
|
| - ASSERT(isConsistentForGC());
|
| - size_t previousPageCount = info->pageCount;
|
| -
|
| - json->beginArray("pages");
|
| - for (BasePage* page = m_firstPage; page; page = page->next(), ++info->pageCount) {
|
| - // FIXME: To limit the size of the snapshot we only output "threshold" many page snapshots.
|
| - if (info->pageCount < GC_PROFILE_HEAP_PAGE_SNAPSHOT_THRESHOLD) {
|
| - json->beginArray();
|
| - json->pushInteger(reinterpret_cast<intptr_t>(page));
|
| - page->snapshot(json, info);
|
| - json->endArray();
|
| - } else {
|
| - page->snapshot(nullptr, info);
|
| - }
|
| - }
|
| - json->endArray();
|
| -
|
| - json->setInteger("pageCount", info->pageCount - previousPageCount);
|
| -}
|
| -
|
| -void BaseHeap::countMarkedObjects(ClassAgeCountsMap& classAgeCounts) const
|
| -{
|
| - for (BasePage* page = m_firstPage; page; page = page->next())
|
| - page->countMarkedObjects(classAgeCounts);
|
| -}
|
| -
|
| -void BaseHeap::countObjectsToSweep(ClassAgeCountsMap& classAgeCounts) const
|
| -{
|
| - for (BasePage* page = m_firstPage; page; page = page->next())
|
| - page->countObjectsToSweep(classAgeCounts);
|
| -}
|
| -
|
| -void BaseHeap::incrementMarkedObjectsAge()
|
| -{
|
| - for (BasePage* page = m_firstPage; page; page = page->next())
|
| - page->incrementMarkedObjectsAge();
|
| -}
|
| -#endif
|
| -
|
| -void BaseHeap::makeConsistentForGC()
|
| -{
|
| - clearFreeLists();
|
| - ASSERT(isConsistentForGC());
|
| - for (BasePage* page = m_firstPage; page; page = page->next())
|
| - page->markAsUnswept();
|
| -
|
| - // If a new GC is requested before this thread got around to sweep,
|
| - // ie. due to the thread doing a long running operation, we clear
|
| - // the mark bits and mark any of the dead objects as dead. The latter
|
| - // is used to ensure the next GC marking does not trace already dead
|
| - // objects. If we trace a dead object we could end up tracing into
|
| - // garbage or the middle of another object via the newly conservatively
|
| - // found object.
|
| - BasePage* previousPage = nullptr;
|
| - for (BasePage* page = m_firstUnsweptPage; page; previousPage = page, page = page->next()) {
|
| - page->makeConsistentForGC();
|
| - ASSERT(!page->hasBeenSwept());
|
| - }
|
| - if (previousPage) {
|
| - ASSERT(m_firstUnsweptPage);
|
| - previousPage->m_next = m_firstPage;
|
| - m_firstPage = m_firstUnsweptPage;
|
| - m_firstUnsweptPage = nullptr;
|
| - }
|
| - ASSERT(!m_firstUnsweptPage);
|
| -}
|
| -
|
| -void BaseHeap::makeConsistentForMutator()
|
| -{
|
| - clearFreeLists();
|
| - ASSERT(isConsistentForGC());
|
| - ASSERT(!m_firstPage);
|
| -
|
| - // Drop marks from marked objects and rebuild free lists in preparation for
|
| - // resuming the executions of mutators.
|
| - BasePage* previousPage = nullptr;
|
| - for (BasePage* page = m_firstUnsweptPage; page; previousPage = page, page = page->next()) {
|
| - page->makeConsistentForMutator();
|
| - page->markAsSwept();
|
| - }
|
| - if (previousPage) {
|
| - ASSERT(m_firstUnsweptPage);
|
| - previousPage->m_next = m_firstPage;
|
| - m_firstPage = m_firstUnsweptPage;
|
| - m_firstUnsweptPage = nullptr;
|
| - }
|
| - ASSERT(!m_firstUnsweptPage);
|
| -}
|
| -
|
| -size_t BaseHeap::objectPayloadSizeForTesting()
|
| -{
|
| - ASSERT(isConsistentForGC());
|
| - ASSERT(!m_firstUnsweptPage);
|
| -
|
| - size_t objectPayloadSize = 0;
|
| - for (BasePage* page = m_firstPage; page; page = page->next())
|
| - objectPayloadSize += page->objectPayloadSizeForTesting();
|
| - return objectPayloadSize;
|
| -}
|
| -
|
| -void BaseHeap::prepareHeapForTermination()
|
| -{
|
| - ASSERT(!m_firstUnsweptPage);
|
| - for (BasePage* page = m_firstPage; page; page = page->next()) {
|
| - page->setTerminating();
|
| - }
|
| -}
|
| -
|
| -void BaseHeap::prepareForSweep()
|
| -{
|
| - ASSERT(threadState()->isInGC());
|
| - ASSERT(!m_firstUnsweptPage);
|
| -
|
| - // Move all pages to a list of unswept pages.
|
| - m_firstUnsweptPage = m_firstPage;
|
| - m_firstPage = nullptr;
|
| -}
|
| -
|
| -#if defined(ADDRESS_SANITIZER)
|
| -void BaseHeap::poisonHeap(ThreadState::ObjectsToPoison objectsToPoison, ThreadState::Poisoning poisoning)
|
| -{
|
| - // TODO(sof): support complete poisoning of all heaps.
|
| - ASSERT(objectsToPoison != ThreadState::MarkedAndUnmarked || heapIndex() == ThreadState::EagerSweepHeapIndex);
|
| -
|
| - // This method may either be called to poison (SetPoison) heap
|
| - // object payloads prior to sweeping, or it may be called at
|
| - // the completion of a sweep to unpoison (ClearPoison) the
|
| - // objects remaining in the heap. Those will all be live and unmarked.
|
| - //
|
| - // Poisoning may be limited to unmarked objects only, or apply to all.
|
| - if (poisoning == ThreadState::SetPoison) {
|
| - for (BasePage* page = m_firstUnsweptPage; page; page = page->next())
|
| - page->poisonObjects(objectsToPoison, poisoning);
|
| - return;
|
| - }
|
| - // Support clearing of poisoning after sweeping has completed,
|
| - // in which case the pages of the live objects are reachable
|
| - // via m_firstPage.
|
| - ASSERT(!m_firstUnsweptPage);
|
| - for (BasePage* page = m_firstPage; page; page = page->next())
|
| - page->poisonObjects(objectsToPoison, poisoning);
|
| -}
|
| -#endif
|
| -
|
| -Address BaseHeap::lazySweep(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - // If there are no pages to be swept, return immediately.
|
| - if (!m_firstUnsweptPage)
|
| - return nullptr;
|
| -
|
| - RELEASE_ASSERT(threadState()->isSweepingInProgress());
|
| -
|
| - // lazySweepPages() can be called recursively if finalizers invoked in
|
| - // page->sweep() allocate memory and the allocation triggers
|
| - // lazySweepPages(). This check prevents the sweeping from being executed
|
| - // recursively.
|
| - if (threadState()->sweepForbidden())
|
| - return nullptr;
|
| -
|
| - TRACE_EVENT0("blink_gc", "BaseHeap::lazySweepPages");
|
| - ThreadState::SweepForbiddenScope scope(threadState());
|
| -
|
| - if (threadState()->isMainThread())
|
| - ScriptForbiddenScope::enter();
|
| -
|
| - Address result = lazySweepPages(allocationSize, gcInfoIndex);
|
| -
|
| - if (threadState()->isMainThread())
|
| - ScriptForbiddenScope::exit();
|
| -
|
| - Heap::reportMemoryUsageForTracing();
|
| -
|
| - return result;
|
| -}
|
| -
|
| -void BaseHeap::sweepUnsweptPage()
|
| -{
|
| - BasePage* page = m_firstUnsweptPage;
|
| - if (page->isEmpty()) {
|
| - page->unlink(&m_firstUnsweptPage);
|
| - page->removeFromHeap();
|
| - } else {
|
| - // Sweep a page and move the page from m_firstUnsweptPages to
|
| - // m_firstPages.
|
| - page->sweep();
|
| - page->unlink(&m_firstUnsweptPage);
|
| - page->link(&m_firstPage);
|
| - page->markAsSwept();
|
| - }
|
| -}
|
| -
|
| -bool BaseHeap::lazySweepWithDeadline(double deadlineSeconds)
|
| -{
|
| - // It might be heavy to call Platform::current()->monotonicallyIncreasingTime()
|
| - // per page (i.e., 128 KB sweep or one LargeObject sweep), so we check
|
| - // the deadline per 10 pages.
|
| - static const int deadlineCheckInterval = 10;
|
| -
|
| - RELEASE_ASSERT(threadState()->isSweepingInProgress());
|
| - ASSERT(threadState()->sweepForbidden());
|
| - ASSERT(!threadState()->isMainThread() || ScriptForbiddenScope::isScriptForbidden());
|
| -
|
| - int pageCount = 1;
|
| - while (m_firstUnsweptPage) {
|
| - sweepUnsweptPage();
|
| - if (pageCount % deadlineCheckInterval == 0) {
|
| - if (deadlineSeconds <= Platform::current()->monotonicallyIncreasingTime()) {
|
| - // Deadline has come.
|
| - Heap::reportMemoryUsageForTracing();
|
| - return !m_firstUnsweptPage;
|
| - }
|
| - }
|
| - pageCount++;
|
| - }
|
| - Heap::reportMemoryUsageForTracing();
|
| - return true;
|
| -}
|
| -
|
| -void BaseHeap::completeSweep()
|
| -{
|
| - RELEASE_ASSERT(threadState()->isSweepingInProgress());
|
| - ASSERT(threadState()->sweepForbidden());
|
| - ASSERT(!threadState()->isMainThread() || ScriptForbiddenScope::isScriptForbidden());
|
| -
|
| - while (m_firstUnsweptPage) {
|
| - sweepUnsweptPage();
|
| - }
|
| -
|
| - Heap::reportMemoryUsageForTracing();
|
| -}
|
| -
|
| -NormalPageHeap::NormalPageHeap(ThreadState* state, int index)
|
| - : BaseHeap(state, index)
|
| - , m_currentAllocationPoint(nullptr)
|
| - , m_remainingAllocationSize(0)
|
| - , m_lastRemainingAllocationSize(0)
|
| - , m_promptlyFreedSize(0)
|
| -#if ENABLE(GC_PROFILING)
|
| - , m_cumulativeAllocationSize(0)
|
| - , m_allocationCount(0)
|
| - , m_inlineAllocationCount(0)
|
| -#endif
|
| -{
|
| - clearFreeLists();
|
| -}
|
| -
|
| -void NormalPageHeap::clearFreeLists()
|
| -{
|
| - setAllocationPoint(nullptr, 0);
|
| - m_freeList.clear();
|
| -}
|
| -
|
| -#if ENABLE(ASSERT)
|
| -bool NormalPageHeap::isConsistentForGC()
|
| -{
|
| - // A thread heap is consistent for sweeping if none of the pages to be swept
|
| - // contain a freelist block or the current allocation point.
|
| - for (size_t i = 0; i < blinkPageSizeLog2; ++i) {
|
| - for (FreeListEntry* freeListEntry = m_freeList.m_freeLists[i]; freeListEntry; freeListEntry = freeListEntry->next()) {
|
| - if (pagesToBeSweptContains(freeListEntry->address()))
|
| - return false;
|
| - }
|
| - }
|
| - if (hasCurrentAllocationArea()) {
|
| - if (pagesToBeSweptContains(currentAllocationPoint()))
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -bool NormalPageHeap::pagesToBeSweptContains(Address address)
|
| -{
|
| - for (BasePage* page = m_firstUnsweptPage; page; page = page->next()) {
|
| - if (page->contains(address))
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -#endif
|
| -
|
| -void NormalPageHeap::takeFreelistSnapshot(const String& dumpName)
|
| -{
|
| - if (m_freeList.takeSnapshot(dumpName)) {
|
| - WebMemoryAllocatorDump* bucketsDump = BlinkGCMemoryDumpProvider::instance()->createMemoryAllocatorDumpForCurrentGC(dumpName + "/buckets");
|
| - WebMemoryAllocatorDump* pagesDump = BlinkGCMemoryDumpProvider::instance()->createMemoryAllocatorDumpForCurrentGC(dumpName + "/pages");
|
| - BlinkGCMemoryDumpProvider::instance()->currentProcessMemoryDump()->AddOwnershipEdge(pagesDump->guid(), bucketsDump->guid());
|
| - }
|
| -}
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -void NormalPageHeap::snapshotFreeList(TracedValue& json)
|
| -{
|
| - json.setInteger("cumulativeAllocationSize", m_cumulativeAllocationSize);
|
| - json.setDouble("inlineAllocationRate", static_cast<double>(m_inlineAllocationCount) / m_allocationCount);
|
| - json.setInteger("inlineAllocationCount", m_inlineAllocationCount);
|
| - json.setInteger("allocationCount", m_allocationCount);
|
| - size_t pageCount = 0;
|
| - size_t totalPageSize = 0;
|
| - for (NormalPage* page = static_cast<NormalPage*>(m_firstPage); page; page = static_cast<NormalPage*>(page->next())) {
|
| - ++pageCount;
|
| - totalPageSize += page->payloadSize();
|
| - }
|
| - json.setInteger("pageCount", pageCount);
|
| - json.setInteger("totalPageSize", totalPageSize);
|
| -
|
| - FreeList::PerBucketFreeListStats bucketStats[blinkPageSizeLog2];
|
| - size_t totalFreeSize;
|
| - m_freeList.getFreeSizeStats(bucketStats, totalFreeSize);
|
| - json.setInteger("totalFreeSize", totalFreeSize);
|
| -
|
| - json.beginArray("perBucketEntryCount");
|
| - for (size_t i = 0; i < blinkPageSizeLog2; ++i)
|
| - json.pushInteger(bucketStats[i].entryCount);
|
| - json.endArray();
|
| -
|
| - json.beginArray("perBucketFreeSize");
|
| - for (size_t i = 0; i < blinkPageSizeLog2; ++i)
|
| - json.pushInteger(bucketStats[i].freeSize);
|
| - json.endArray();
|
| -}
|
| -#endif
|
| -
|
| -void NormalPageHeap::allocatePage()
|
| -{
|
| - threadState()->shouldFlushHeapDoesNotContainCache();
|
| - PageMemory* pageMemory = Heap::freePagePool()->takeFreePage(heapIndex());
|
| - // We continue allocating page memory until we succeed in committing one.
|
| - while (!pageMemory) {
|
| - // Allocate a memory region for blinkPagesPerRegion pages that
|
| - // will each have the following layout.
|
| - //
|
| - // [ guard os page | ... payload ... | guard os page ]
|
| - // ^---{ aligned to blink page size }
|
| - PageMemoryRegion* region = PageMemoryRegion::allocateNormalPages();
|
| -
|
| - // Setup the PageMemory object for each of the pages in the region.
|
| - size_t offset = 0;
|
| - for (size_t i = 0; i < blinkPagesPerRegion; ++i) {
|
| - PageMemory* memory = PageMemory::setupPageMemoryInRegion(region, offset, blinkPagePayloadSize());
|
| - // Take the first possible page ensuring that this thread actually
|
| - // gets a page and add the rest to the page pool.
|
| - if (!pageMemory) {
|
| - if (memory->commit())
|
| - pageMemory = memory;
|
| - else
|
| - delete memory;
|
| - } else {
|
| - Heap::freePagePool()->addFreePage(heapIndex(), memory);
|
| - }
|
| - offset += blinkPageSize;
|
| - }
|
| - }
|
| - NormalPage* page = new (pageMemory->writableStart()) NormalPage(pageMemory, this);
|
| - page->link(&m_firstPage);
|
| -
|
| - Heap::increaseAllocatedSpace(page->size());
|
| -#if ENABLE(ASSERT) || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
|
| - // Allow the following addToFreeList() to add the newly allocated memory
|
| - // to the free list.
|
| - ASAN_UNPOISON_MEMORY_REGION(page->payload(), page->payloadSize());
|
| - Address address = page->payload();
|
| - for (size_t i = 0; i < page->payloadSize(); i++)
|
| - address[i] = reuseAllowedZapValue;
|
| - ASAN_POISON_MEMORY_REGION(page->payload(), page->payloadSize());
|
| -#endif
|
| - addToFreeList(page->payload(), page->payloadSize());
|
| -}
|
| -
|
| -void NormalPageHeap::freePage(NormalPage* page)
|
| -{
|
| - Heap::decreaseAllocatedSpace(page->size());
|
| -
|
| - if (page->terminating()) {
|
| - // The thread is shutting down and this page is being removed as a part
|
| - // of the thread local GC. In that case the object could be traced in
|
| - // the next global GC if there is a dangling pointer from a live thread
|
| - // heap to this dead thread heap. To guard against this, we put the
|
| - // page into the orphaned page pool and zap the page memory. This
|
| - // ensures that tracing the dangling pointer in the next global GC just
|
| - // crashes instead of causing use-after-frees. After the next global
|
| - // GC, the orphaned pages are removed.
|
| - Heap::orphanedPagePool()->addOrphanedPage(heapIndex(), page);
|
| - } else {
|
| - PageMemory* memory = page->storage();
|
| - page->~NormalPage();
|
| - Heap::freePagePool()->addFreePage(heapIndex(), memory);
|
| - }
|
| -}
|
| -
|
| -bool NormalPageHeap::coalesce()
|
| -{
|
| - // Don't coalesce heaps if there are not enough promptly freed entries
|
| - // to be coalesced.
|
| - //
|
| - // FIXME: This threshold is determined just to optimize blink_perf
|
| - // benchmarks. Coalescing is very sensitive to the threashold and
|
| - // we need further investigations on the coalescing scheme.
|
| - if (m_promptlyFreedSize < 1024 * 1024)
|
| - return false;
|
| -
|
| - if (threadState()->sweepForbidden())
|
| - return false;
|
| -
|
| - ASSERT(!hasCurrentAllocationArea());
|
| - TRACE_EVENT0("blink_gc", "BaseHeap::coalesce");
|
| -
|
| - // Rebuild free lists.
|
| - m_freeList.clear();
|
| - size_t freedSize = 0;
|
| - for (NormalPage* page = static_cast<NormalPage*>(m_firstPage); page; page = static_cast<NormalPage*>(page->next())) {
|
| - page->clearObjectStartBitMap();
|
| - Address startOfGap = page->payload();
|
| - for (Address headerAddress = startOfGap; headerAddress < page->payloadEnd(); ) {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - size_t size = header->size();
|
| - ASSERT(size > 0);
|
| - ASSERT(size < blinkPagePayloadSize());
|
| -
|
| - if (header->isPromptlyFreed()) {
|
| - ASSERT(size >= sizeof(HeapObjectHeader));
|
| - // Zero the memory in the free list header to maintain the
|
| - // invariant that memory on the free list is zero filled.
|
| - // The rest of the memory is already on the free list and is
|
| - // therefore already zero filled.
|
| - SET_MEMORY_INACCESSIBLE(headerAddress, sizeof(HeapObjectHeader));
|
| - freedSize += size;
|
| - headerAddress += size;
|
| - continue;
|
| - }
|
| - if (header->isFree()) {
|
| - // Zero the memory in the free list header to maintain the
|
| - // invariant that memory on the free list is zero filled.
|
| - // The rest of the memory is already on the free list and is
|
| - // therefore already zero filled.
|
| - SET_MEMORY_INACCESSIBLE(headerAddress, size < sizeof(FreeListEntry) ? size : sizeof(FreeListEntry));
|
| - headerAddress += size;
|
| - continue;
|
| - }
|
| - ASSERT(header->checkHeader());
|
| - if (startOfGap != headerAddress)
|
| - addToFreeList(startOfGap, headerAddress - startOfGap);
|
| -
|
| - headerAddress += size;
|
| - startOfGap = headerAddress;
|
| - }
|
| -
|
| - if (startOfGap != page->payloadEnd())
|
| - addToFreeList(startOfGap, page->payloadEnd() - startOfGap);
|
| - }
|
| - Heap::decreaseAllocatedObjectSize(freedSize);
|
| - ASSERT(m_promptlyFreedSize == freedSize);
|
| - m_promptlyFreedSize = 0;
|
| - return true;
|
| -}
|
| -
|
| -void NormalPageHeap::promptlyFreeObject(HeapObjectHeader* header)
|
| -{
|
| - ASSERT(!threadState()->sweepForbidden());
|
| - ASSERT(header->checkHeader());
|
| - Address address = reinterpret_cast<Address>(header);
|
| - Address payload = header->payload();
|
| - size_t size = header->size();
|
| - size_t payloadSize = header->payloadSize();
|
| - ASSERT(size > 0);
|
| - ASSERT(pageFromObject(address) == findPageFromAddress(address));
|
| -
|
| - {
|
| - ThreadState::SweepForbiddenScope forbiddenScope(threadState());
|
| - header->finalize(payload, payloadSize);
|
| - if (address + size == m_currentAllocationPoint) {
|
| - m_currentAllocationPoint = address;
|
| - if (m_lastRemainingAllocationSize == m_remainingAllocationSize) {
|
| - Heap::decreaseAllocatedObjectSize(size);
|
| - m_lastRemainingAllocationSize += size;
|
| - }
|
| - m_remainingAllocationSize += size;
|
| - SET_MEMORY_INACCESSIBLE(address, size);
|
| - return;
|
| - }
|
| - SET_MEMORY_INACCESSIBLE(payload, payloadSize);
|
| - header->markPromptlyFreed();
|
| - }
|
| -
|
| - m_promptlyFreedSize += size;
|
| -}
|
| -
|
| -bool NormalPageHeap::expandObject(HeapObjectHeader* header, size_t newSize)
|
| -{
|
| - // It's possible that Vector requests a smaller expanded size because
|
| - // Vector::shrinkCapacity can set a capacity smaller than the actual payload
|
| - // size.
|
| - ASSERT(header->checkHeader());
|
| - if (header->payloadSize() >= newSize)
|
| - return true;
|
| - size_t allocationSize = Heap::allocationSizeFromSize(newSize);
|
| - ASSERT(allocationSize > header->size());
|
| - size_t expandSize = allocationSize - header->size();
|
| - if (header->payloadEnd() == m_currentAllocationPoint && expandSize <= m_remainingAllocationSize) {
|
| - m_currentAllocationPoint += expandSize;
|
| - m_remainingAllocationSize -= expandSize;
|
| -
|
| - // Unpoison the memory used for the object (payload).
|
| - SET_MEMORY_ACCESSIBLE(header->payloadEnd(), expandSize);
|
| - header->setSize(allocationSize);
|
| - ASSERT(findPageFromAddress(header->payloadEnd() - 1));
|
| - return true;
|
| - }
|
| - return false;
|
| -}
|
| -
|
| -bool NormalPageHeap::shrinkObject(HeapObjectHeader* header, size_t newSize)
|
| -{
|
| - ASSERT(header->checkHeader());
|
| - ASSERT(header->payloadSize() > newSize);
|
| - size_t allocationSize = Heap::allocationSizeFromSize(newSize);
|
| - ASSERT(header->size() > allocationSize);
|
| - size_t shrinkSize = header->size() - allocationSize;
|
| - if (header->payloadEnd() == m_currentAllocationPoint) {
|
| - m_currentAllocationPoint -= shrinkSize;
|
| - m_remainingAllocationSize += shrinkSize;
|
| - SET_MEMORY_INACCESSIBLE(m_currentAllocationPoint, shrinkSize);
|
| - header->setSize(allocationSize);
|
| - return true;
|
| - }
|
| - ASSERT(shrinkSize >= sizeof(HeapObjectHeader));
|
| - ASSERT(header->gcInfoIndex() > 0);
|
| - Address shrinkAddress = header->payloadEnd() - shrinkSize;
|
| - HeapObjectHeader* freedHeader = new (NotNull, shrinkAddress) HeapObjectHeader(shrinkSize, header->gcInfoIndex());
|
| - freedHeader->markPromptlyFreed();
|
| - ASSERT(pageFromObject(reinterpret_cast<Address>(header)) == findPageFromAddress(reinterpret_cast<Address>(header)));
|
| - m_promptlyFreedSize += shrinkSize;
|
| - header->setSize(allocationSize);
|
| - SET_MEMORY_INACCESSIBLE(shrinkAddress + sizeof(HeapObjectHeader), shrinkSize - sizeof(HeapObjectHeader));
|
| - return false;
|
| -}
|
| -
|
| -Address NormalPageHeap::lazySweepPages(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - ASSERT(!hasCurrentAllocationArea());
|
| - Address result = nullptr;
|
| - while (m_firstUnsweptPage) {
|
| - BasePage* page = m_firstUnsweptPage;
|
| - if (page->isEmpty()) {
|
| - page->unlink(&m_firstUnsweptPage);
|
| - page->removeFromHeap();
|
| - } else {
|
| - // Sweep a page and move the page from m_firstUnsweptPages to
|
| - // m_firstPages.
|
| - page->sweep();
|
| - page->unlink(&m_firstUnsweptPage);
|
| - page->link(&m_firstPage);
|
| - page->markAsSwept();
|
| -
|
| - // For NormalPage, stop lazy sweeping once we find a slot to
|
| - // allocate a new object.
|
| - result = allocateFromFreeList(allocationSize, gcInfoIndex);
|
| - if (result)
|
| - break;
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -void NormalPageHeap::updateRemainingAllocationSize()
|
| -{
|
| - if (m_lastRemainingAllocationSize > remainingAllocationSize()) {
|
| - Heap::increaseAllocatedObjectSize(m_lastRemainingAllocationSize - remainingAllocationSize());
|
| - m_lastRemainingAllocationSize = remainingAllocationSize();
|
| - }
|
| - ASSERT(m_lastRemainingAllocationSize == remainingAllocationSize());
|
| -}
|
| -
|
| -void NormalPageHeap::setAllocationPoint(Address point, size_t size)
|
| -{
|
| -#if ENABLE(ASSERT)
|
| - if (point) {
|
| - ASSERT(size);
|
| - BasePage* page = pageFromObject(point);
|
| - ASSERT(!page->isLargeObjectPage());
|
| - ASSERT(size <= static_cast<NormalPage*>(page)->payloadSize());
|
| - }
|
| -#endif
|
| - if (hasCurrentAllocationArea()) {
|
| - addToFreeList(currentAllocationPoint(), remainingAllocationSize());
|
| - }
|
| - updateRemainingAllocationSize();
|
| - m_currentAllocationPoint = point;
|
| - m_lastRemainingAllocationSize = m_remainingAllocationSize = size;
|
| -}
|
| -
|
| -Address NormalPageHeap::outOfLineAllocate(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - ASSERT(allocationSize > remainingAllocationSize());
|
| - ASSERT(allocationSize >= allocationGranularity);
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| - threadState()->snapshotFreeListIfNecessary();
|
| -#endif
|
| -
|
| - // Ideally we want to update the persistent count every time a persistent
|
| - // handle is created or destructed, but that is heavy. So we do the update
|
| - // only in outOfLineAllocate().
|
| - threadState()->updatePersistentCounters();
|
| -
|
| - // 1. If this allocation is big enough, allocate a large object.
|
| - if (allocationSize >= largeObjectSizeThreshold) {
|
| - // TODO(sof): support eagerly finalized large objects, if ever needed.
|
| - RELEASE_ASSERT(heapIndex() != ThreadState::EagerSweepHeapIndex);
|
| - LargeObjectHeap* largeObjectHeap = static_cast<LargeObjectHeap*>(threadState()->heap(ThreadState::LargeObjectHeapIndex));
|
| - Address largeObject = largeObjectHeap->allocateLargeObjectPage(allocationSize, gcInfoIndex);
|
| - ASAN_MARK_LARGE_VECTOR_CONTAINER(this, largeObject);
|
| - return largeObject;
|
| - }
|
| -
|
| - // 2. Try to allocate from a free list.
|
| - updateRemainingAllocationSize();
|
| - Address result = allocateFromFreeList(allocationSize, gcInfoIndex);
|
| - if (result)
|
| - return result;
|
| -
|
| - // 3. Reset the allocation point.
|
| - setAllocationPoint(nullptr, 0);
|
| -
|
| - // 4. Lazily sweep pages of this heap until we find a freed area for
|
| - // this allocation or we finish sweeping all pages of this heap.
|
| - result = lazySweep(allocationSize, gcInfoIndex);
|
| - if (result)
|
| - return result;
|
| -
|
| - // 5. Coalesce promptly freed areas and then try to allocate from a free
|
| - // list.
|
| - if (coalesce()) {
|
| - result = allocateFromFreeList(allocationSize, gcInfoIndex);
|
| - if (result)
|
| - return result;
|
| - }
|
| -
|
| - // 6. Complete sweeping.
|
| - threadState()->completeSweep();
|
| -
|
| - // 7. Check if we should trigger a GC.
|
| - threadState()->scheduleGCIfNeeded();
|
| -
|
| - // 8. Add a new page to this heap.
|
| - allocatePage();
|
| -
|
| - // 9. Try to allocate from a free list. This allocation must succeed.
|
| - result = allocateFromFreeList(allocationSize, gcInfoIndex);
|
| - RELEASE_ASSERT(result);
|
| - return result;
|
| -}
|
| -
|
| -Address NormalPageHeap::allocateFromFreeList(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - // Try reusing a block from the largest bin. The underlying reasoning
|
| - // being that we want to amortize this slow allocation call by carving
|
| - // off as a large a free block as possible in one go; a block that will
|
| - // service this block and let following allocations be serviced quickly
|
| - // by bump allocation.
|
| - size_t bucketSize = 1 << m_freeList.m_biggestFreeListIndex;
|
| - int index = m_freeList.m_biggestFreeListIndex;
|
| - for (; index > 0; --index, bucketSize >>= 1) {
|
| - FreeListEntry* entry = m_freeList.m_freeLists[index];
|
| - if (allocationSize > bucketSize) {
|
| - // Final bucket candidate; check initial entry if it is able
|
| - // to service this allocation. Do not perform a linear scan,
|
| - // as it is considered too costly.
|
| - if (!entry || entry->size() < allocationSize)
|
| - break;
|
| - }
|
| - if (entry) {
|
| - entry->unlink(&m_freeList.m_freeLists[index]);
|
| - setAllocationPoint(entry->address(), entry->size());
|
| - ASSERT(hasCurrentAllocationArea());
|
| - ASSERT(remainingAllocationSize() >= allocationSize);
|
| - m_freeList.m_biggestFreeListIndex = index;
|
| - return allocateObject(allocationSize, gcInfoIndex);
|
| - }
|
| - }
|
| - m_freeList.m_biggestFreeListIndex = index;
|
| - return nullptr;
|
| -}
|
| -
|
| -LargeObjectHeap::LargeObjectHeap(ThreadState* state, int index)
|
| - : BaseHeap(state, index)
|
| -{
|
| -}
|
| -
|
| -Address LargeObjectHeap::allocateLargeObjectPage(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - // Caller already added space for object header and rounded up to allocation
|
| - // alignment
|
| - ASSERT(!(allocationSize & allocationMask));
|
| -
|
| - // 1. Try to sweep large objects more than allocationSize bytes
|
| - // before allocating a new large object.
|
| - Address result = lazySweep(allocationSize, gcInfoIndex);
|
| - if (result)
|
| - return result;
|
| -
|
| - // 2. If we have failed in sweeping allocationSize bytes,
|
| - // we complete sweeping before allocating this large object.
|
| - threadState()->completeSweep();
|
| -
|
| - // 3. Check if we should trigger a GC.
|
| - threadState()->scheduleGCIfNeeded();
|
| -
|
| - return doAllocateLargeObjectPage(allocationSize, gcInfoIndex);
|
| -}
|
| -
|
| -Address LargeObjectHeap::doAllocateLargeObjectPage(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - size_t largeObjectSize = LargeObjectPage::pageHeaderSize() + allocationSize;
|
| - // If ASan is supported we add allocationGranularity bytes to the allocated
|
| - // space and poison that to detect overflows
|
| -#if defined(ADDRESS_SANITIZER)
|
| - largeObjectSize += allocationGranularity;
|
| -#endif
|
| -
|
| - threadState()->shouldFlushHeapDoesNotContainCache();
|
| - PageMemory* pageMemory = PageMemory::allocate(largeObjectSize);
|
| - Address largeObjectAddress = pageMemory->writableStart();
|
| - Address headerAddress = largeObjectAddress + LargeObjectPage::pageHeaderSize();
|
| -#if ENABLE(ASSERT)
|
| - // Verify that the allocated PageMemory is expectedly zeroed.
|
| - for (size_t i = 0; i < largeObjectSize; ++i)
|
| - ASSERT(!largeObjectAddress[i]);
|
| -#endif
|
| - ASSERT(gcInfoIndex > 0);
|
| - HeapObjectHeader* header = new (NotNull, headerAddress) HeapObjectHeader(largeObjectSizeInHeader, gcInfoIndex);
|
| - Address result = headerAddress + sizeof(*header);
|
| - ASSERT(!(reinterpret_cast<uintptr_t>(result) & allocationMask));
|
| - LargeObjectPage* largeObject = new (largeObjectAddress) LargeObjectPage(pageMemory, this, allocationSize);
|
| - ASSERT(header->checkHeader());
|
| -
|
| - // Poison the object header and allocationGranularity bytes after the object
|
| - ASAN_POISON_MEMORY_REGION(header, sizeof(*header));
|
| - ASAN_POISON_MEMORY_REGION(largeObject->address() + largeObject->size(), allocationGranularity);
|
| -
|
| - largeObject->link(&m_firstPage);
|
| -
|
| - Heap::increaseAllocatedSpace(largeObject->size());
|
| - Heap::increaseAllocatedObjectSize(largeObject->size());
|
| - return result;
|
| -}
|
| -
|
| -void LargeObjectHeap::freeLargeObjectPage(LargeObjectPage* object)
|
| -{
|
| - ASAN_UNPOISON_MEMORY_REGION(object->payload(), object->payloadSize());
|
| - object->heapObjectHeader()->finalize(object->payload(), object->payloadSize());
|
| - Heap::decreaseAllocatedSpace(object->size());
|
| -
|
| - // Unpoison the object header and allocationGranularity bytes after the
|
| - // object before freeing.
|
| - ASAN_UNPOISON_MEMORY_REGION(object->heapObjectHeader(), sizeof(HeapObjectHeader));
|
| - ASAN_UNPOISON_MEMORY_REGION(object->address() + object->size(), allocationGranularity);
|
| -
|
| - if (object->terminating()) {
|
| - ASSERT(ThreadState::current()->isTerminating());
|
| - // The thread is shutting down and this page is being removed as a part
|
| - // of the thread local GC. In that case the object could be traced in
|
| - // the next global GC if there is a dangling pointer from a live thread
|
| - // heap to this dead thread heap. To guard against this, we put the
|
| - // page into the orphaned page pool and zap the page memory. This
|
| - // ensures that tracing the dangling pointer in the next global GC just
|
| - // crashes instead of causing use-after-frees. After the next global
|
| - // GC, the orphaned pages are removed.
|
| - Heap::orphanedPagePool()->addOrphanedPage(heapIndex(), object);
|
| - } else {
|
| - ASSERT(!ThreadState::current()->isTerminating());
|
| - PageMemory* memory = object->storage();
|
| - object->~LargeObjectPage();
|
| - delete memory;
|
| - }
|
| -}
|
| -
|
| -Address LargeObjectHeap::lazySweepPages(size_t allocationSize, size_t gcInfoIndex)
|
| -{
|
| - Address result = nullptr;
|
| - size_t sweptSize = 0;
|
| - while (m_firstUnsweptPage) {
|
| - BasePage* page = m_firstUnsweptPage;
|
| - if (page->isEmpty()) {
|
| - sweptSize += static_cast<LargeObjectPage*>(page)->payloadSize() + sizeof(HeapObjectHeader);
|
| - page->unlink(&m_firstUnsweptPage);
|
| - page->removeFromHeap();
|
| - // For LargeObjectPage, stop lazy sweeping once we have swept
|
| - // more than allocationSize bytes.
|
| - if (sweptSize >= allocationSize) {
|
| - result = doAllocateLargeObjectPage(allocationSize, gcInfoIndex);
|
| - ASSERT(result);
|
| - break;
|
| - }
|
| - } else {
|
| - // Sweep a page and move the page from m_firstUnsweptPages to
|
| - // m_firstPages.
|
| - page->sweep();
|
| - page->unlink(&m_firstUnsweptPage);
|
| - page->link(&m_firstPage);
|
| - page->markAsSwept();
|
| - }
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -FreeList::FreeList()
|
| - : m_biggestFreeListIndex(0)
|
| -{
|
| -}
|
| -
|
| -void FreeList::addToFreeList(Address address, size_t size)
|
| -{
|
| - ASSERT(size < blinkPagePayloadSize());
|
| - // The free list entries are only pointer aligned (but when we allocate
|
| - // from them we are 8 byte aligned due to the header size).
|
| - ASSERT(!((reinterpret_cast<uintptr_t>(address) + sizeof(HeapObjectHeader)) & allocationMask));
|
| - ASSERT(!(size & allocationMask));
|
| - ASAN_UNPOISON_MEMORY_REGION(address, size);
|
| - FreeListEntry* entry;
|
| - if (size < sizeof(*entry)) {
|
| - // Create a dummy header with only a size and freelist bit set.
|
| - ASSERT(size >= sizeof(HeapObjectHeader));
|
| - // Free list encode the size to mark the lost memory as freelist memory.
|
| - new (NotNull, address) HeapObjectHeader(size, gcInfoIndexForFreeListHeader);
|
| -
|
| - ASAN_POISON_MEMORY_REGION(address, size);
|
| - // This memory gets lost. Sweeping can reclaim it.
|
| - return;
|
| - }
|
| - entry = new (NotNull, address) FreeListEntry(size);
|
| -
|
| -#if ENABLE(ASSERT) || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER)
|
| - // The following logic delays reusing free lists for (at least) one GC
|
| - // cycle or coalescing. This is helpful to detect use-after-free errors
|
| - // that could be caused by lazy sweeping etc.
|
| - size_t allowedCount = 0;
|
| - size_t forbiddenCount = 0;
|
| - for (size_t i = sizeof(FreeListEntry); i < size; i++) {
|
| - if (address[i] == reuseAllowedZapValue) {
|
| - allowedCount++;
|
| - } else if (address[i] == reuseForbiddenZapValue) {
|
| - forbiddenCount++;
|
| - } else {
|
| - ASSERT_NOT_REACHED();
|
| - }
|
| - }
|
| - size_t entryCount = size - sizeof(FreeListEntry);
|
| - if (forbiddenCount == entryCount) {
|
| - // If all values in the memory region are reuseForbiddenZapValue,
|
| - // we flip them to reuseAllowedZapValue. This allows the next
|
| - // addToFreeList() to add the memory region to the free list
|
| - // (unless someone concatenates the memory region with another memory
|
| - // region that contains reuseForbiddenZapValue.)
|
| - for (size_t i = sizeof(FreeListEntry); i < size; i++)
|
| - address[i] = reuseAllowedZapValue;
|
| - ASAN_POISON_MEMORY_REGION(address, size);
|
| - // Don't add the memory region to the free list in this addToFreeList().
|
| - return;
|
| - }
|
| - if (allowedCount != entryCount) {
|
| - // If the memory region mixes reuseForbiddenZapValue and
|
| - // reuseAllowedZapValue, we (conservatively) flip all the values
|
| - // to reuseForbiddenZapValue. These values will be changed to
|
| - // reuseAllowedZapValue in the next addToFreeList().
|
| - for (size_t i = sizeof(FreeListEntry); i < size; i++)
|
| - address[i] = reuseForbiddenZapValue;
|
| - ASAN_POISON_MEMORY_REGION(address, size);
|
| - // Don't add the memory region to the free list in this addToFreeList().
|
| - return;
|
| - }
|
| - // We reach here only when all the values in the memory region are
|
| - // reuseAllowedZapValue. In this case, we are allowed to add the memory
|
| - // region to the free list and reuse it for another object.
|
| -#endif
|
| - ASAN_POISON_MEMORY_REGION(address, size);
|
| -
|
| - int index = bucketIndexForSize(size);
|
| - entry->link(&m_freeLists[index]);
|
| - if (index > m_biggestFreeListIndex)
|
| - m_biggestFreeListIndex = index;
|
| -}
|
| -
|
| -#if ENABLE(ASSERT) || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER)
|
| -NO_SANITIZE_ADDRESS
|
| -NO_SANITIZE_MEMORY
|
| -void NEVER_INLINE FreeList::zapFreedMemory(Address address, size_t size)
|
| -{
|
| - for (size_t i = 0; i < size; i++) {
|
| - // See the comment in addToFreeList().
|
| - if (address[i] != reuseAllowedZapValue)
|
| - address[i] = reuseForbiddenZapValue;
|
| - }
|
| -}
|
| -#endif
|
| -
|
| -void FreeList::clear()
|
| -{
|
| - m_biggestFreeListIndex = 0;
|
| - for (size_t i = 0; i < blinkPageSizeLog2; ++i)
|
| - m_freeLists[i] = nullptr;
|
| -}
|
| -
|
| -int FreeList::bucketIndexForSize(size_t size)
|
| -{
|
| - ASSERT(size > 0);
|
| - int index = -1;
|
| - while (size) {
|
| - size >>= 1;
|
| - index++;
|
| - }
|
| - return index;
|
| -}
|
| -
|
| -bool FreeList::takeSnapshot(const String& dumpBaseName)
|
| -{
|
| - bool didDumpBucketStats = false;
|
| - for (size_t i = 0; i < blinkPageSizeLog2; ++i) {
|
| - size_t entryCount = 0;
|
| - size_t freeSize = 0;
|
| - for (FreeListEntry* entry = m_freeLists[i]; entry; entry = entry->next()) {
|
| - ++entryCount;
|
| - freeSize += entry->size();
|
| - }
|
| -
|
| - String dumpName = dumpBaseName + String::format("/buckets/bucket_%lu", static_cast<unsigned long>(1 << i));
|
| - WebMemoryAllocatorDump* bucketDump = BlinkGCMemoryDumpProvider::instance()->createMemoryAllocatorDumpForCurrentGC(dumpName);
|
| - bucketDump->AddScalar("free_count", "objects", entryCount);
|
| - bucketDump->AddScalar("free_size", "bytes", freeSize);
|
| - didDumpBucketStats = true;
|
| - }
|
| - return didDumpBucketStats;
|
| -}
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -void FreeList::getFreeSizeStats(PerBucketFreeListStats bucketStats[], size_t& totalFreeSize) const
|
| -{
|
| - totalFreeSize = 0;
|
| - for (size_t i = 0; i < blinkPageSizeLog2; i++) {
|
| - size_t& entryCount = bucketStats[i].entryCount;
|
| - size_t& freeSize = bucketStats[i].freeSize;
|
| - for (FreeListEntry* entry = m_freeLists[i]; entry; entry = entry->next()) {
|
| - ++entryCount;
|
| - freeSize += entry->size();
|
| - }
|
| - totalFreeSize += freeSize;
|
| - }
|
| -}
|
| -#endif
|
| -
|
| -BasePage::BasePage(PageMemory* storage, BaseHeap* heap)
|
| - : m_storage(storage)
|
| - , m_heap(heap)
|
| - , m_next(nullptr)
|
| - , m_terminating(false)
|
| - , m_swept(true)
|
| -{
|
| - ASSERT(isPageHeaderAddress(reinterpret_cast<Address>(this)));
|
| -}
|
| -
|
| -void BasePage::markOrphaned()
|
| -{
|
| - m_heap = nullptr;
|
| - m_terminating = false;
|
| - // Since we zap the page payload for orphaned pages we need to mark it as
|
| - // unused so a conservative pointer won't interpret the object headers.
|
| - storage()->markUnused();
|
| -}
|
| -
|
| -NormalPage::NormalPage(PageMemory* storage, BaseHeap* heap)
|
| - : BasePage(storage, heap)
|
| -{
|
| - m_objectStartBitMapComputed = false;
|
| - ASSERT(isPageHeaderAddress(reinterpret_cast<Address>(this)));
|
| -}
|
| -
|
| -size_t NormalPage::objectPayloadSizeForTesting()
|
| -{
|
| - size_t objectPayloadSize = 0;
|
| - Address headerAddress = payload();
|
| - markAsSwept();
|
| - ASSERT(headerAddress != payloadEnd());
|
| - do {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - if (!header->isFree()) {
|
| - ASSERT(header->checkHeader());
|
| - objectPayloadSize += header->payloadSize();
|
| - }
|
| - ASSERT(header->size() < blinkPagePayloadSize());
|
| - headerAddress += header->size();
|
| - ASSERT(headerAddress <= payloadEnd());
|
| - } while (headerAddress < payloadEnd());
|
| - return objectPayloadSize;
|
| -}
|
| -
|
| -bool NormalPage::isEmpty()
|
| -{
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(payload());
|
| - return header->isFree() && header->size() == payloadSize();
|
| -}
|
| -
|
| -void NormalPage::removeFromHeap()
|
| -{
|
| - heapForNormalPage()->freePage(this);
|
| -}
|
| -
|
| -void NormalPage::sweep()
|
| -{
|
| - clearObjectStartBitMap();
|
| -
|
| - size_t markedObjectSize = 0;
|
| - Address startOfGap = payload();
|
| - for (Address headerAddress = startOfGap; headerAddress < payloadEnd(); ) {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - ASSERT(header->size() > 0);
|
| - ASSERT(header->size() < blinkPagePayloadSize());
|
| -
|
| - if (header->isPromptlyFreed())
|
| - heapForNormalPage()->decreasePromptlyFreedSize(header->size());
|
| - if (header->isFree()) {
|
| - size_t size = header->size();
|
| - // Zero the memory in the free list header to maintain the
|
| - // invariant that memory on the free list is zero filled.
|
| - // The rest of the memory is already on the free list and is
|
| - // therefore already zero filled.
|
| - SET_MEMORY_INACCESSIBLE(headerAddress, size < sizeof(FreeListEntry) ? size : sizeof(FreeListEntry));
|
| - headerAddress += size;
|
| - continue;
|
| - }
|
| - ASSERT(header->checkHeader());
|
| -
|
| - if (!header->isMarked()) {
|
| - size_t size = header->size();
|
| - // This is a fast version of header->payloadSize().
|
| - size_t payloadSize = size - sizeof(HeapObjectHeader);
|
| - Address payload = header->payload();
|
| - // For ASan, unpoison the object before calling the finalizer. The
|
| - // finalized object will be zero-filled and poison'ed afterwards.
|
| - // Given all other unmarked objects are poisoned, ASan will detect
|
| - // an error if the finalizer touches any other on-heap object that
|
| - // die at the same GC cycle.
|
| - ASAN_UNPOISON_MEMORY_REGION(payload, payloadSize);
|
| - header->finalize(payload, payloadSize);
|
| - // This memory will be added to the freelist. Maintain the invariant
|
| - // that memory on the freelist is zero filled.
|
| - SET_MEMORY_INACCESSIBLE(headerAddress, size);
|
| - headerAddress += size;
|
| - continue;
|
| - }
|
| - if (startOfGap != headerAddress)
|
| - heapForNormalPage()->addToFreeList(startOfGap, headerAddress - startOfGap);
|
| - header->unmark();
|
| - headerAddress += header->size();
|
| - markedObjectSize += header->size();
|
| - startOfGap = headerAddress;
|
| - }
|
| - if (startOfGap != payloadEnd())
|
| - heapForNormalPage()->addToFreeList(startOfGap, payloadEnd() - startOfGap);
|
| -
|
| - if (markedObjectSize)
|
| - Heap::increaseMarkedObjectSize(markedObjectSize);
|
| -}
|
| -
|
| -void NormalPage::makeConsistentForGC()
|
| -{
|
| - size_t markedObjectSize = 0;
|
| - for (Address headerAddress = payload(); headerAddress < payloadEnd();) {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - ASSERT(header->size() < blinkPagePayloadSize());
|
| - // Check if a free list entry first since we cannot call
|
| - // isMarked on a free list entry.
|
| - if (header->isFree()) {
|
| - headerAddress += header->size();
|
| - continue;
|
| - }
|
| - ASSERT(header->checkHeader());
|
| - if (header->isMarked()) {
|
| - header->unmark();
|
| - markedObjectSize += header->size();
|
| - } else {
|
| - header->markDead();
|
| - }
|
| - headerAddress += header->size();
|
| - }
|
| - if (markedObjectSize)
|
| - Heap::increaseMarkedObjectSize(markedObjectSize);
|
| -}
|
| -
|
| -void NormalPage::makeConsistentForMutator()
|
| -{
|
| - Address startOfGap = payload();
|
| - for (Address headerAddress = payload(); headerAddress < payloadEnd();) {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - size_t size = header->size();
|
| - ASSERT(size < blinkPagePayloadSize());
|
| - if (header->isPromptlyFreed())
|
| - heapForNormalPage()->decreasePromptlyFreedSize(size);
|
| - if (header->isFree()) {
|
| - // Zero the memory in the free list header to maintain the
|
| - // invariant that memory on the free list is zero filled.
|
| - // The rest of the memory is already on the free list and is
|
| - // therefore already zero filled.
|
| - SET_MEMORY_INACCESSIBLE(headerAddress, size < sizeof(FreeListEntry) ? size : sizeof(FreeListEntry));
|
| - headerAddress += size;
|
| - continue;
|
| - }
|
| - ASSERT(header->checkHeader());
|
| -
|
| - if (startOfGap != headerAddress)
|
| - heapForNormalPage()->addToFreeList(startOfGap, headerAddress - startOfGap);
|
| - if (header->isMarked())
|
| - header->unmark();
|
| - headerAddress += size;
|
| - startOfGap = headerAddress;
|
| - ASSERT(headerAddress <= payloadEnd());
|
| - }
|
| - if (startOfGap != payloadEnd())
|
| - heapForNormalPage()->addToFreeList(startOfGap, payloadEnd() - startOfGap);
|
| -}
|
| -
|
| -#if defined(ADDRESS_SANITIZER)
|
| -void NormalPage::poisonObjects(ThreadState::ObjectsToPoison objectsToPoison, ThreadState::Poisoning poisoning)
|
| -{
|
| - for (Address headerAddress = payload(); headerAddress < payloadEnd();) {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - ASSERT(header->size() < blinkPagePayloadSize());
|
| - // Check if a free list entry first since we cannot call
|
| - // isMarked on a free list entry.
|
| - if (header->isFree()) {
|
| - headerAddress += header->size();
|
| - continue;
|
| - }
|
| - ASSERT(header->checkHeader());
|
| - if (objectsToPoison == ThreadState::MarkedAndUnmarked || !header->isMarked()) {
|
| - if (poisoning == ThreadState::SetPoison)
|
| - ASAN_POISON_MEMORY_REGION(header->payload(), header->payloadSize());
|
| - else
|
| - ASAN_UNPOISON_MEMORY_REGION(header->payload(), header->payloadSize());
|
| - }
|
| - headerAddress += header->size();
|
| - }
|
| -}
|
| -#endif
|
| -
|
| -void NormalPage::populateObjectStartBitMap()
|
| -{
|
| - memset(&m_objectStartBitMap, 0, objectStartBitMapSize);
|
| - Address start = payload();
|
| - for (Address headerAddress = start; headerAddress < payloadEnd();) {
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - size_t objectOffset = headerAddress - start;
|
| - ASSERT(!(objectOffset & allocationMask));
|
| - size_t objectStartNumber = objectOffset / allocationGranularity;
|
| - size_t mapIndex = objectStartNumber / 8;
|
| - ASSERT(mapIndex < objectStartBitMapSize);
|
| - m_objectStartBitMap[mapIndex] |= (1 << (objectStartNumber & 7));
|
| - headerAddress += header->size();
|
| - ASSERT(headerAddress <= payloadEnd());
|
| - }
|
| - m_objectStartBitMapComputed = true;
|
| -}
|
| -
|
| -void NormalPage::clearObjectStartBitMap()
|
| -{
|
| - m_objectStartBitMapComputed = false;
|
| -}
|
| -
|
| -static int numberOfLeadingZeroes(uint8_t byte)
|
| -{
|
| - if (!byte)
|
| - return 8;
|
| - int result = 0;
|
| - if (byte <= 0x0F) {
|
| - result += 4;
|
| - byte = byte << 4;
|
| - }
|
| - if (byte <= 0x3F) {
|
| - result += 2;
|
| - byte = byte << 2;
|
| - }
|
| - if (byte <= 0x7F)
|
| - result++;
|
| - return result;
|
| -}
|
| -
|
| -HeapObjectHeader* NormalPage::findHeaderFromAddress(Address address)
|
| -{
|
| - if (address < payload())
|
| - return nullptr;
|
| - if (!isObjectStartBitMapComputed())
|
| - populateObjectStartBitMap();
|
| - size_t objectOffset = address - payload();
|
| - size_t objectStartNumber = objectOffset / allocationGranularity;
|
| - size_t mapIndex = objectStartNumber / 8;
|
| - ASSERT(mapIndex < objectStartBitMapSize);
|
| - size_t bit = objectStartNumber & 7;
|
| - uint8_t byte = m_objectStartBitMap[mapIndex] & ((1 << (bit + 1)) - 1);
|
| - while (!byte) {
|
| - ASSERT(mapIndex > 0);
|
| - byte = m_objectStartBitMap[--mapIndex];
|
| - }
|
| - int leadingZeroes = numberOfLeadingZeroes(byte);
|
| - objectStartNumber = (mapIndex * 8) + 7 - leadingZeroes;
|
| - objectOffset = objectStartNumber * allocationGranularity;
|
| - Address objectAddress = objectOffset + payload();
|
| - HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(objectAddress);
|
| - if (header->isFree())
|
| - return nullptr;
|
| - ASSERT(header->checkHeader());
|
| - return header;
|
| -}
|
| -
|
| -#if ENABLE(ASSERT)
|
| -static bool isUninitializedMemory(void* objectPointer, size_t objectSize)
|
| -{
|
| - // Scan through the object's fields and check that they are all zero.
|
| - Address* objectFields = reinterpret_cast<Address*>(objectPointer);
|
| - for (size_t i = 0; i < objectSize / sizeof(Address); ++i) {
|
| - if (objectFields[i] != 0)
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -#endif
|
| -
|
| -static void markPointer(Visitor* visitor, HeapObjectHeader* header)
|
| -{
|
| - ASSERT(header->checkHeader());
|
| - const GCInfo* gcInfo = Heap::gcInfo(header->gcInfoIndex());
|
| - if (gcInfo->hasVTable() && !vTableInitialized(header->payload())) {
|
| - // We hit this branch when a GC strikes before GarbageCollected<>'s
|
| - // constructor runs.
|
| - //
|
| - // class A : public GarbageCollected<A> { virtual void f() = 0; };
|
| - // class B : public A {
|
| - // B() : A(foo()) { };
|
| - // };
|
| - //
|
| - // If foo() allocates something and triggers a GC, the vtable of A
|
| - // has not yet been initialized. In this case, we should mark the A
|
| - // object without tracing any member of the A object.
|
| - visitor->markHeaderNoTracing(header);
|
| - ASSERT(isUninitializedMemory(header->payload(), header->payloadSize()));
|
| - } else {
|
| - visitor->markHeader(header, gcInfo->m_trace);
|
| - }
|
| -}
|
| -
|
| -void NormalPage::checkAndMarkPointer(Visitor* visitor, Address address)
|
| -{
|
| - ASSERT(contains(address));
|
| - HeapObjectHeader* header = findHeaderFromAddress(address);
|
| - if (!header || header->isDead())
|
| - return;
|
| - markPointer(visitor, header);
|
| -}
|
| -
|
| -void NormalPage::markOrphaned()
|
| -{
|
| - // Zap the payload with a recognizable value to detect any incorrect
|
| - // cross thread pointer usage.
|
| -#if defined(ADDRESS_SANITIZER)
|
| - // This needs to zap poisoned memory as well.
|
| - // Force unpoison memory before memset.
|
| - ASAN_UNPOISON_MEMORY_REGION(payload(), payloadSize());
|
| -#endif
|
| - OrphanedPagePool::asanDisabledMemset(payload(), OrphanedPagePool::orphanedZapValue, payloadSize());
|
| - BasePage::markOrphaned();
|
| -}
|
| -
|
| -void NormalPage::takeSnapshot(String dumpName, size_t pageIndex, ThreadState::GCSnapshotInfo& info, size_t* outFreeSize, size_t* outFreeCount)
|
| -{
|
| - dumpName.append(String::format("/pages/page_%lu", static_cast<unsigned long>(pageIndex)));
|
| - WebMemoryAllocatorDump* pageDump = BlinkGCMemoryDumpProvider::instance()->createMemoryAllocatorDumpForCurrentGC(dumpName);
|
| -
|
| - HeapObjectHeader* header = nullptr;
|
| - size_t liveCount = 0;
|
| - size_t deadCount = 0;
|
| - size_t freeCount = 0;
|
| - size_t liveSize = 0;
|
| - size_t deadSize = 0;
|
| - size_t freeSize = 0;
|
| - for (Address headerAddress = payload(); headerAddress < payloadEnd(); headerAddress += header->size()) {
|
| - header = reinterpret_cast<HeapObjectHeader*>(headerAddress);
|
| - if (header->isFree()) {
|
| - freeCount++;
|
| - freeSize += header->size();
|
| - } else if (header->isMarked()) {
|
| - liveCount++;
|
| - liveSize += header->size();
|
| -
|
| - size_t gcInfoIndex = header->gcInfoIndex();
|
| - info.liveCount[gcInfoIndex]++;
|
| - info.liveSize[gcInfoIndex] += header->size();
|
| - } else {
|
| - deadCount++;
|
| - deadSize += header->size();
|
| -
|
| - size_t gcInfoIndex = header->gcInfoIndex();
|
| - info.deadCount[gcInfoIndex]++;
|
| - info.deadSize[gcInfoIndex] += header->size();
|
| - }
|
| - }
|
| -
|
| - pageDump->AddScalar("live_count", "objects", liveCount);
|
| - pageDump->AddScalar("dead_count", "objects", deadCount);
|
| - pageDump->AddScalar("free_count", "objects", freeCount);
|
| - pageDump->AddScalar("live_size", "bytes", liveSize);
|
| - pageDump->AddScalar("dead_size", "bytes", deadSize);
|
| - pageDump->AddScalar("free_size", "bytes", freeSize);
|
| - *outFreeSize = freeSize;
|
| - *outFreeCount = freeCount;
|
| -}
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -const GCInfo* NormalPage::findGCInfo(Address address)
|
| -{
|
| - if (address < payload())
|
| - return nullptr;
|
| -
|
| - HeapObjectHeader* header = findHeaderFromAddress(address);
|
| - if (!header)
|
| - return nullptr;
|
| -
|
| - return Heap::gcInfo(header->gcInfoIndex());
|
| -}
|
| -#endif
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -void NormalPage::snapshot(TracedValue* json, ThreadState::SnapshotInfo* info)
|
| -{
|
| - HeapObjectHeader* header = nullptr;
|
| - for (Address addr = payload(); addr < payloadEnd(); addr += header->size()) {
|
| - header = reinterpret_cast<HeapObjectHeader*>(addr);
|
| - if (json)
|
| - json->pushInteger(header->encodedSize());
|
| - if (header->isFree()) {
|
| - info->freeSize += header->size();
|
| - continue;
|
| - }
|
| - ASSERT(header->checkHeader());
|
| -
|
| - size_t tag = info->getClassTag(Heap::gcInfo(header->gcInfoIndex()));
|
| - size_t age = header->age();
|
| - if (json)
|
| - json->pushInteger(tag);
|
| - if (header->isMarked()) {
|
| - info->liveCount[tag] += 1;
|
| - info->liveSize[tag] += header->size();
|
| - // Count objects that are live when promoted to the final generation.
|
| - if (age == maxHeapObjectAge - 1)
|
| - info->generations[tag][maxHeapObjectAge] += 1;
|
| - } else {
|
| - info->deadCount[tag] += 1;
|
| - info->deadSize[tag] += header->size();
|
| - // Count objects that are dead before the final generation.
|
| - if (age < maxHeapObjectAge)
|
| - info->generations[tag][age] += 1;
|
| - }
|
| - }
|
| -}
|
| -
|
| -void NormalPage::incrementMarkedObjectsAge()
|
| -{
|
| - HeapObjectHeader* header = nullptr;
|
| - for (Address address = payload(); address < payloadEnd(); address += header->size()) {
|
| - header = reinterpret_cast<HeapObjectHeader*>(address);
|
| - if (header->isMarked())
|
| - header->incrementAge();
|
| - }
|
| -}
|
| -
|
| -void NormalPage::countMarkedObjects(ClassAgeCountsMap& classAgeCounts)
|
| -{
|
| - HeapObjectHeader* header = nullptr;
|
| - for (Address address = payload(); address < payloadEnd(); address += header->size()) {
|
| - header = reinterpret_cast<HeapObjectHeader*>(address);
|
| - if (header->isMarked()) {
|
| - String className(classOf(header->payload()));
|
| - ++(classAgeCounts.add(className, AgeCounts()).storedValue->value.ages[header->age()]);
|
| - }
|
| - }
|
| -}
|
| -
|
| -void NormalPage::countObjectsToSweep(ClassAgeCountsMap& classAgeCounts)
|
| -{
|
| - HeapObjectHeader* header = nullptr;
|
| - for (Address address = payload(); address < payloadEnd(); address += header->size()) {
|
| - header = reinterpret_cast<HeapObjectHeader*>(address);
|
| - if (!header->isFree() && !header->isMarked()) {
|
| - String className(classOf(header->payload()));
|
| - ++(classAgeCounts.add(className, AgeCounts()).storedValue->value.ages[header->age()]);
|
| - }
|
| - }
|
| -}
|
| -#endif
|
| -
|
| -#if ENABLE(ASSERT) || ENABLE(GC_PROFILING)
|
| -bool NormalPage::contains(Address addr)
|
| -{
|
| - Address blinkPageStart = roundToBlinkPageStart(address());
|
| - ASSERT(blinkPageStart == address() - blinkGuardPageSize); // Page is at aligned address plus guard page size.
|
| - return blinkPageStart <= addr && addr < blinkPageStart + blinkPageSize;
|
| -}
|
| -#endif
|
| -
|
| -NormalPageHeap* NormalPage::heapForNormalPage()
|
| -{
|
| - return static_cast<NormalPageHeap*>(heap());
|
| -}
|
| -
|
| -LargeObjectPage::LargeObjectPage(PageMemory* storage, BaseHeap* heap, size_t payloadSize)
|
| - : BasePage(storage, heap)
|
| - , m_payloadSize(payloadSize)
|
| -#if ENABLE(ASAN_CONTAINER_ANNOTATIONS)
|
| - , m_isVectorBackingPage(false)
|
| -#endif
|
| -{
|
| -}
|
| -
|
| -size_t LargeObjectPage::objectPayloadSizeForTesting()
|
| -{
|
| - markAsSwept();
|
| - return payloadSize();
|
| -}
|
| -
|
| -bool LargeObjectPage::isEmpty()
|
| -{
|
| - return !heapObjectHeader()->isMarked();
|
| -}
|
| -
|
| -void LargeObjectPage::removeFromHeap()
|
| -{
|
| - static_cast<LargeObjectHeap*>(heap())->freeLargeObjectPage(this);
|
| -}
|
| -
|
| -void LargeObjectPage::sweep()
|
| -{
|
| - heapObjectHeader()->unmark();
|
| - Heap::increaseMarkedObjectSize(size());
|
| -}
|
| -
|
| -void LargeObjectPage::makeConsistentForGC()
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - if (header->isMarked()) {
|
| - header->unmark();
|
| - Heap::increaseMarkedObjectSize(size());
|
| - } else {
|
| - header->markDead();
|
| - }
|
| -}
|
| -
|
| -void LargeObjectPage::makeConsistentForMutator()
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - if (header->isMarked())
|
| - header->unmark();
|
| -}
|
| -
|
| -#if defined(ADDRESS_SANITIZER)
|
| -void LargeObjectPage::poisonObjects(ThreadState::ObjectsToPoison objectsToPoison, ThreadState::Poisoning poisoning)
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - if (objectsToPoison == ThreadState::MarkedAndUnmarked || !header->isMarked()) {
|
| - if (poisoning == ThreadState::SetPoison)
|
| - ASAN_POISON_MEMORY_REGION(header->payload(), header->payloadSize());
|
| - else
|
| - ASAN_UNPOISON_MEMORY_REGION(header->payload(), header->payloadSize());
|
| - }
|
| -}
|
| -#endif
|
| -
|
| -void LargeObjectPage::checkAndMarkPointer(Visitor* visitor, Address address)
|
| -{
|
| - ASSERT(contains(address));
|
| - if (!containedInObjectPayload(address) || heapObjectHeader()->isDead())
|
| - return;
|
| - markPointer(visitor, heapObjectHeader());
|
| -}
|
| -
|
| -void LargeObjectPage::markOrphaned()
|
| -{
|
| - // Zap the payload with a recognizable value to detect any incorrect
|
| - // cross thread pointer usage.
|
| - OrphanedPagePool::asanDisabledMemset(payload(), OrphanedPagePool::orphanedZapValue, payloadSize());
|
| - BasePage::markOrphaned();
|
| -}
|
| -
|
| -void LargeObjectPage::takeSnapshot(String dumpName, size_t pageIndex, ThreadState::GCSnapshotInfo& info, size_t* outFreeSize, size_t* outFreeCount)
|
| -{
|
| - dumpName.append(String::format("/pages/page_%lu", static_cast<unsigned long>(pageIndex)));
|
| - WebMemoryAllocatorDump* pageDump = BlinkGCMemoryDumpProvider::instance()->createMemoryAllocatorDumpForCurrentGC(dumpName);
|
| -
|
| - size_t liveSize = 0;
|
| - size_t deadSize = 0;
|
| - size_t liveCount = 0;
|
| - size_t deadCount = 0;
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - size_t gcInfoIndex = header->gcInfoIndex();
|
| - if (header->isMarked()) {
|
| - liveCount = 1;
|
| - liveSize += header->payloadSize();
|
| - info.liveCount[gcInfoIndex]++;
|
| - info.liveSize[gcInfoIndex] += header->size();
|
| - } else {
|
| - deadCount = 1;
|
| - deadSize += header->payloadSize();
|
| - info.deadCount[gcInfoIndex]++;
|
| - info.deadSize[gcInfoIndex] += header->size();
|
| - }
|
| -
|
| - pageDump->AddScalar("live_count", "objects", liveCount);
|
| - pageDump->AddScalar("dead_count", "objects", deadCount);
|
| - pageDump->AddScalar("live_size", "bytes", liveSize);
|
| - pageDump->AddScalar("dead_size", "bytes", deadSize);
|
| -}
|
| -
|
| -#if ENABLE(GC_PROFILING)
|
| -const GCInfo* LargeObjectPage::findGCInfo(Address address)
|
| -{
|
| - if (!containedInObjectPayload(address))
|
| - return nullptr;
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - return Heap::gcInfo(header->gcInfoIndex());
|
| -}
|
| -
|
| -void LargeObjectPage::snapshot(TracedValue* json, ThreadState::SnapshotInfo* info)
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - size_t tag = info->getClassTag(Heap::gcInfo(header->gcInfoIndex()));
|
| - size_t age = header->age();
|
| - if (header->isMarked()) {
|
| - info->liveCount[tag] += 1;
|
| - info->liveSize[tag] += header->size();
|
| - // Count objects that are live when promoted to the final generation.
|
| - if (age == maxHeapObjectAge - 1)
|
| - info->generations[tag][maxHeapObjectAge] += 1;
|
| - } else {
|
| - info->deadCount[tag] += 1;
|
| - info->deadSize[tag] += header->size();
|
| - // Count objects that are dead before the final generation.
|
| - if (age < maxHeapObjectAge)
|
| - info->generations[tag][age] += 1;
|
| - }
|
| -
|
| - if (json) {
|
| - json->setInteger("class", tag);
|
| - json->setInteger("size", header->size());
|
| - json->setInteger("isMarked", header->isMarked());
|
| - }
|
| -}
|
| -
|
| -void LargeObjectPage::incrementMarkedObjectsAge()
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - if (header->isMarked())
|
| - header->incrementAge();
|
| -}
|
| -
|
| -void LargeObjectPage::countMarkedObjects(ClassAgeCountsMap& classAgeCounts)
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - if (header->isMarked()) {
|
| - String className(classOf(header->payload()));
|
| - ++(classAgeCounts.add(className, AgeCounts()).storedValue->value.ages[header->age()]);
|
| - }
|
| -}
|
| -
|
| -void LargeObjectPage::countObjectsToSweep(ClassAgeCountsMap& classAgeCounts)
|
| -{
|
| - HeapObjectHeader* header = heapObjectHeader();
|
| - if (!header->isFree() && !header->isMarked()) {
|
| - String className(classOf(header->payload()));
|
| - ++(classAgeCounts.add(className, AgeCounts()).storedValue->value.ages[header->age()]);
|
| - }
|
| -}
|
| -#endif
|
| -
|
| -#if ENABLE(ASSERT) || ENABLE(GC_PROFILING)
|
| -bool LargeObjectPage::contains(Address object)
|
| -{
|
| - return roundToBlinkPageStart(address()) <= object && object < roundToBlinkPageEnd(address() + size());
|
| -}
|
| -#endif
|
| -
|
| -void HeapDoesNotContainCache::flush()
|
| -{
|
| - if (m_hasEntries) {
|
| - for (int i = 0; i < numberOfEntries; ++i)
|
| - m_entries[i] = nullptr;
|
| - m_hasEntries = false;
|
| - }
|
| -}
|
| -
|
| -size_t HeapDoesNotContainCache::hash(Address address)
|
| -{
|
| - size_t value = (reinterpret_cast<size_t>(address) >> blinkPageSizeLog2);
|
| - value ^= value >> numberOfEntriesLog2;
|
| - value ^= value >> (numberOfEntriesLog2 * 2);
|
| - value &= numberOfEntries - 1;
|
| - return value & ~1; // Returns only even number.
|
| -}
|
| -
|
| -bool HeapDoesNotContainCache::lookup(Address address)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - size_t index = hash(address);
|
| - ASSERT(!(index & 1));
|
| - Address cachePage = roundToBlinkPageStart(address);
|
| - if (m_entries[index] == cachePage)
|
| - return m_entries[index];
|
| - if (m_entries[index + 1] == cachePage)
|
| - return m_entries[index + 1];
|
| - return false;
|
| -}
|
| -
|
| -void HeapDoesNotContainCache::addEntry(Address address)
|
| -{
|
| - ASSERT(ThreadState::current()->isInGC());
|
| -
|
| - m_hasEntries = true;
|
| - size_t index = hash(address);
|
| - ASSERT(!(index & 1));
|
| - Address cachePage = roundToBlinkPageStart(address);
|
| - m_entries[index + 1] = m_entries[index];
|
| - m_entries[index] = cachePage;
|
| -}
|
| -
|
| void Heap::flushHeapDoesNotContainCache()
|
| {
|
| s_heapDoesNotContainCache->flush();
|
|
|