Index: Source/platform/heap/Heap.cpp |
diff --git a/Source/platform/heap/Heap.cpp b/Source/platform/heap/Heap.cpp |
index 2163e15d963b3259f0c6f7654ee177703cde54f8..3a7ecf0bfd02bd5179ddb522b8d7af925108b9e1 100644 |
--- a/Source/platform/heap/Heap.cpp |
+++ b/Source/platform/heap/Heap.cpp |
@@ -50,7 +50,7 @@ |
#include <stdio.h> |
#include <utility> |
#endif |
-#if ENABLE(GC_PROFILE_HEAP) |
+#if ENABLE(GC_PROFILE_HEAP) || ENABLE(GC_PROFILE_FREE_LIST) || ENABLE(GC_PROFILE_MARKING) |
#include "platform/TracedValue.h" |
#endif |
@@ -63,6 +63,36 @@ |
namespace blink { |
+struct AgeHistogram { |
+ int data[8]; |
+}; |
+ |
+typedef HashMap<String, AgeHistogram> ObjectAgeMap; |
+ |
+static ObjectAgeMap& uom() |
+{ |
+ static ObjectAgeMap uomap; |
+ return uomap; |
+} |
+ |
+static Mutex& uomMutex() |
+{ |
+ AtomicallyInitializedStaticReference(Mutex, mutex, new Mutex); |
+ return mutex; |
+} |
+ |
+static ObjectAgeMap& mom() |
+{ |
+ static ObjectAgeMap momap; |
+ return momap; |
+} |
+ |
+static Mutex& momMutex() |
+{ |
+ AtomicallyInitializedStaticReference(Mutex, mutex, new Mutex); |
+ return mutex; |
+} |
+ |
#if ENABLE(GC_PROFILE_MARKING) |
static String classOf(const void* object) |
{ |
@@ -543,6 +573,11 @@ ThreadHeap::ThreadHeap(ThreadState* state, int index) |
: m_currentAllocationPoint(nullptr) |
, m_remainingAllocationSize(0) |
, m_lastRemainingAllocationSize(0) |
+#if ENABLE(GC_PROFILE_FREE_LIST) |
+ , m_totalAllocationSize(0.0) |
+ , m_allocationCount(0) |
+ , m_inlineAllocationCount(0) |
+#endif |
, m_firstPage(nullptr) |
, m_firstLargeObject(nullptr) |
, m_firstUnsweptPage(nullptr) |
@@ -606,6 +641,10 @@ void ThreadHeap::setAllocationPoint(Address point, size_t size) |
ASSERT(size <= static_cast<HeapPage*>(page)->payloadSize()); |
} |
#endif |
+#if ENABLE(GC_PROFILE_FREE_LIST) |
+ m_allocationPointSizeSum += size; |
+ ++m_setAllocationPointCount; |
+#endif |
if (hasCurrentAllocationArea()) |
addToFreeList(currentAllocationPoint(), remainingAllocationSize()); |
updateRemainingAllocationSize(); |
@@ -615,6 +654,9 @@ void ThreadHeap::setAllocationPoint(Address point, size_t size) |
Address ThreadHeap::outOfLineAllocate(size_t allocationSize, size_t gcInfoIndex) |
{ |
+#if ENABLE(GC_PROFILE_FREE_LIST) |
+ m_threadState->snapshotFreeListIfNecessary(); |
+#endif |
ASSERT(allocationSize > remainingAllocationSize()); |
ASSERT(allocationSize >= allocationGranularity); |
@@ -857,7 +899,9 @@ static bool isLargeObjectAligned(LargeObject* largeObject, Address address) |
// for the guard page). |
return reinterpret_cast<Address>(largeObject) - WTF::kSystemPageSize == roundToBlinkPageStart(reinterpret_cast<Address>(largeObject)); |
} |
+#endif |
+#if ENABLE(ASSERT) || ENABLE(GC_PROFILE_MARKING) |
BaseHeapPage* ThreadHeap::findPageFromAddress(Address address) |
{ |
for (HeapPage* page = m_firstPage; page; page = page->next()) { |
@@ -882,6 +926,44 @@ BaseHeapPage* ThreadHeap::findPageFromAddress(Address address) |
} |
#endif |
+#if ENABLE(GC_PROFILE_FREE_LIST) |
+void ThreadHeap::snapshotFreeList(TracedValue* json) |
+{ |
+ json->setDouble("totalAllocationSize", m_totalAllocationSize); |
+ json->setDouble("inlineAllocationRate", static_cast<double>(m_inlineAllocationCount) / m_allocationCount); |
+ json->setInteger("inlineAllocationCount", m_inlineAllocationCount); |
+ json->setInteger("allocationCount", m_allocationCount); |
+ if (m_setAllocationPointCount > 0) { |
+ json->setDouble("averageAllocationPointSize", static_cast<double>(m_allocationPointSizeSum) / m_setAllocationPointCount); |
+ } |
+ m_allocationPointSizeSum = 0; |
+ m_setAllocationPointCount = 0; |
+ size_t pageCount = 0; |
+ size_t totalPageSize = 0; |
+ for (HeapPage* page = m_firstPage; page; page = page->next()) { |
+ ++pageCount; |
+ totalPageSize += page->payloadSize(); |
+ } |
+ json->setInteger("pageCount", pageCount); |
+ json->setInteger("totalPageSize", totalPageSize); |
+ size_t bucketSizes[blinkPageSizeLog2]; |
+ size_t bucketTotalSizes[blinkPageSizeLog2]; |
+ size_t freeSize = 0; |
+ m_freeList.countBucketSizes(bucketSizes, bucketTotalSizes, &freeSize); |
+ json->setInteger("freeSize", freeSize); |
+ json->beginArray("bucketSizes"); |
+ for (size_t i = 0; i < blinkPageSizeLog2; ++i) { |
+ json->pushInteger(bucketSizes[i]); |
+ } |
+ json->endArray(); |
+ json->beginArray("bucketTotalSizes"); |
+ for (size_t i = 0; i < blinkPageSizeLog2; ++i) { |
+ json->pushInteger(bucketTotalSizes[i]); |
+ } |
+ json->endArray(); |
+} |
+#endif |
+ |
#if ENABLE(GC_PROFILE_HEAP) |
#define GC_PROFILE_HEAP_PAGE_SNAPSHOT_THRESHOLD 0 |
void ThreadHeap::snapshot(TracedValue* json, ThreadState::SnapshotInfo* info) |
@@ -949,6 +1031,24 @@ void FreeList::addToFreeList(Address address, size_t size) |
m_biggestFreeListIndex = index; |
} |
+#if ENABLE(GC_PROFILE_FREE_LIST) |
+void FreeList::countBucketSizes(size_t sizes[], size_t totalSizes[], size_t* freeSize) const |
+{ |
+ *freeSize = 0; |
+ for (size_t i = 0; i < blinkPageSizeLog2; i++) { |
+ sizes[i] = 0; |
+ totalSizes[i] = 0; |
+ FreeListEntry* entry = m_freeLists[i]; |
+ while (entry) { |
+ ++sizes[i]; |
+ *freeSize += entry->size(); |
+ totalSizes[i] += entry->size(); |
+ entry = entry->next(); |
+ } |
+ } |
+} |
+#endif |
+ |
bool ThreadHeap::expandObject(HeapObjectHeader* header, size_t newSize) |
{ |
// It's possible that Vector requests a smaller expanded size because |
@@ -1762,6 +1862,32 @@ size_t LargeObject::objectPayloadSizeForTesting() |
return payloadSize(); |
} |
+void HeapPage::countUnmarkedObjects() |
+{ |
+ MutexLocker locker(uomMutex()); |
+ for (Address headerAddress = payload(); headerAddress < payloadEnd(); ) { |
+ HeapObjectHeader* header = reinterpret_cast<HeapObjectHeader*>(headerAddress); |
+ ASSERT(header->size() < blinkPagePayloadSize()); |
+ |
+ if (!header->isFree() && !header->isMarked()) { |
+ String className(classOf(header->payload())); |
+ ObjectAgeMap::AddResult result = uom().add(className, AgeHistogram()); |
+ result.storedValue->value.data[header->age()]++; |
+ } |
+ headerAddress += header->size(); |
+ } |
+} |
+ |
+#if ENABLE(GC_PROFILE_MARKING) |
+const GCInfo* LargeObject::findGCInfo(Address address) |
+{ |
+ if (!containedInObjectPayload(address)) |
+ return nullptr; |
+ HeapObjectHeader* header = heapObjectHeader(); |
+ return Heap::gcInfo(header->gcInfoIndex()); |
+} |
+#endif |
+ |
#if ENABLE(GC_PROFILE_HEAP) |
void LargeObject::snapshot(TracedValue* json, ThreadState::SnapshotInfo* info) |
{ |
@@ -1921,7 +2047,7 @@ public: |
void reportStats() |
{ |
fprintf(stderr, "\n---------- AFTER MARKING -------------------\n"); |
- for (LiveObjectMap::iterator it = currentlyLive().begin(), end = currentlyLive().payloadEnd(); it != end; ++it) { |
+ for (LiveObjectMap::iterator it = currentlyLive().begin(), end = currentlyLive().end(); it != end; ++it) { |
fprintf(stderr, "%s %u", it->key.ascii().data(), it->value.size()); |
if (it->key == "blink::Document") |
@@ -1938,13 +2064,28 @@ public: |
} |
} |
+ void reportMarkingStats() |
+ { |
+ MutexLocker locker(momMutex()); |
+ RefPtr<TracedValue> json = TracedValue::create(); |
+ for (ObjectAgeMap::iterator it = mom().begin(), end = mom().end(); it != end; ++it) { |
+ json->beginArray(it->key.ascii().data()); |
+ for (size_t i = 0; i < 8; ++i) { |
+ json->pushInteger(it->value.data[i]); |
+ } |
+ json->endArray(); |
+ } |
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID("blink_gc", "MarkingStats", (unsigned long long)0, json.release()); |
+ mom().clear(); |
+ } |
+ |
static void reportStillAlive(LiveObjectSet current, LiveObjectSet previous) |
{ |
int count = 0; |
fprintf(stderr, " [previously %u]", previous.size()); |
for (uintptr_t object : current) { |
- if (previous.find(object) == previous.payloadEnd()) |
+ if (previous.find(object) == previous.end()) |
continue; |
count++; |
} |
@@ -1954,7 +2095,7 @@ public: |
fprintf(stderr, " {survived 2GCs %d: ", count); |
for (uintptr_t object : current) { |
- if (previous.find(object) == previous.payloadEnd()) |
+ if (previous.find(object) == previous.end()) |
continue; |
fprintf(stderr, "%ld", object); |
if (--count) |
@@ -1967,10 +2108,10 @@ public: |
static void dumpPathToObjectFromObjectGraph(const ObjectGraph& graph, uintptr_t target) |
{ |
ObjectGraph::const_iterator it = graph.find(target); |
- if (it == graph.payloadEnd()) |
+ if (it == graph.end()) |
return; |
fprintf(stderr, "Path to %lx of %s\n", target, classOf(reinterpret_cast<const void*>(target)).ascii().data()); |
- while (it != graph.payloadEnd()) { |
+ while (it != graph.end()) { |
fprintf(stderr, "<- %lx of %s\n", it->value.first, it->value.second.utf8().data()); |
it = graph.find(it->value.first); |
} |
@@ -2284,6 +2425,21 @@ void Heap::preGC() |
state->preGC(); |
} |
+void Heap::reportSweepingStats() |
+{ |
+ MutexLocker locker(uomMutex()); |
+ RefPtr<TracedValue> json = TracedValue::create(); |
+ for (ObjectAgeMap::iterator it = uom().begin(), end = uom().end(); it != end; ++it) { |
+ json->beginArray(it->key.ascii().data()); |
+ for (size_t i = 0; i < 8; ++i) { |
+ json->pushInteger(it->value.data[i]); |
+ } |
+ json->endArray(); |
+ } |
+ TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID("blink_gc", "SweepingStats", (unsigned long long)0, json.release()); |
+ uom().clear(); |
+} |
+ |
void Heap::postGC(ThreadState::GCType gcType) |
{ |
ASSERT(ThreadState::current()->isInGC()); |
@@ -2296,6 +2452,10 @@ void Heap::collectGarbage(ThreadState::StackState stackState, ThreadState::GCTyp |
ThreadState* state = ThreadState::current(); |
state->setGCState(ThreadState::StoppingOtherThreads); |
+#if ENABLE(GC_PROFILE_FREE_LIST) |
+ state->snapshotFreeListIfNecessary(); |
+#endif |
+ |
GCScope gcScope(stackState); |
// Check if we successfully parked the other threads. If not we bail out of |
// the GC. |
@@ -2357,7 +2517,8 @@ void Heap::collectGarbage(ThreadState::StackState stackState, ThreadState::GCTyp |
postGC(gcType); |
#if ENABLE(GC_PROFILE_MARKING) |
- static_cast<MarkingVisitor<GlobalMarking>*>(s_markingVisitor)->reportStats(); |
+ //static_cast<MarkingVisitor<GlobalMarking>*>(s_markingVisitor)->reportStats(); |
+ static_cast<MarkingVisitor<GlobalMarking>*>(s_markingVisitor)->reportMarkingStats(); |
#endif |
if (Platform::current()) { |