From d53b07ee19ee940672b1bcd8e692a488f3b6eb4e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 23 Nov 2017 10:05:30 +0100 Subject: Re-enable QML memory profiling Task-number: QTBUG-64674 Change-Id: I48ed1a51f66ef8d55cc026f140d270baaca04fbf Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4profiling.cpp | 3 ++- src/qml/jsruntime/qv4profiling_p.h | 20 +++++++++++----- src/qml/memory/qv4mm.cpp | 27 ++++++++++++++++------ src/qml/memory/qv4mm_p.h | 10 ++++---- src/qml/memory/qv4mmdefs_p.h | 4 ++-- .../debugger/qqmlprofilerservice/data/memory.qml | 17 ++++++++++++++ .../qqmlprofilerservice/qqmlprofilerservice.pro | 3 ++- .../tst_qqmlprofilerservice.cpp | 21 +++++++++++++++++ 8 files changed, 84 insertions(+), 21 deletions(-) create mode 100644 tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index bedcb5b164..5fd200efc1 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -120,7 +120,8 @@ void Profiler::startProfiling(quint64 features) if (features & (1 << FeatureMemoryAllocation)) { qint64 timestamp = m_timer.nsecsElapsed(); MemoryAllocationProperties heap = {timestamp, - (qint64)m_engine->memoryManager->getAllocatedMem(), + (qint64)m_engine->memoryManager->getAllocatedMem() - + (qint64)m_engine->memoryManager->getLargeItemsMem(), HeapPage}; m_memory_data.append(heap); MemoryAllocationProperties small = {timestamp, diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 9de597ad0e..08b4ba6c76 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -230,16 +230,24 @@ public: bool trackAlloc(size_t size, MemoryType type) { - MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)size, type}; - m_memory_data.append(allocation); - return true; + if (size) { + MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)size, type}; + m_memory_data.append(allocation); + return true; + } else { + return false; + } } bool trackDealloc(size_t size, MemoryType type) { - MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), -(qint64)size, type}; - m_memory_data.append(allocation); - return true; + if (size) { + MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), -(qint64)size, type}; + m_memory_data.append(allocation); + return true; + } else { + return false; + } } quint64 featuresEnabled; diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 56f1254421..538be0d16a 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -275,7 +275,7 @@ QString binary(quintptr) { return QString(); } #define SDUMP if (1) ; else qDebug #endif -bool Chunk::sweep() +bool Chunk::sweep(ExecutionEngine *engine) { bool hasUsedSlots = false; SDUMP() << "sweeping chunk" << this; @@ -316,6 +316,9 @@ bool Chunk::sweep() b->_checkIsDestroyed(); } } + Q_V4_PROFILE_DEALLOC(engine, qPopulationCount((objectBitmap[i] | extendsBitmap[i]) + - (blackBitmap[i] | e)) * Chunk::SlotSize, + Profiling::SmallItem); objectBitmap[i] = blackBitmap[i]; hasUsedSlots |= (blackBitmap[i] != 0); blackBitmap[i] = 0; @@ -330,7 +333,7 @@ bool Chunk::sweep() return hasUsedSlots; } -void Chunk::freeAll() +void Chunk::freeAll(ExecutionEngine *engine) { // DEBUG << "sweeping chunk" << this << (*freeList); HeapItem *o = realBase(); @@ -361,6 +364,8 @@ void Chunk::freeAll() b->_checkIsDestroyed(); } } + Q_V4_PROFILE_DEALLOC(engine, (qPopulationCount(objectBitmap[i]|extendsBitmap[i]) + - qPopulationCount(e)) * Chunk::SlotSize, Profiling::SmallItem); objectBitmap[i] = 0; blackBitmap[i] = 0; extendsBitmap[i] = e; @@ -560,6 +565,7 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { if (!forceAllocation) return 0; Chunk *newChunk = chunkAllocator->allocate(); + Q_V4_PROFILE_ALLOC(engine, Chunk::DataSize, Profiling::HeapPage); chunks.push_back(newChunk); nextFree = newChunk->first(); nFree = Chunk::AvailableSlots; @@ -570,6 +576,7 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { done: m->setAllocatedSlots(slotsRequired); + Q_V4_PROFILE_ALLOC(engine, slotsRequired * Chunk::SlotSize, Profiling::SmallItem); // DEBUG << " " << hex << m->chunk() << m->chunk()->objectBitmap[0] << m->chunk()->extendsBitmap[0] << (m - m->chunk()->realBase()); return m; } @@ -584,12 +591,13 @@ void BlockAllocator::sweep() usedSlotsAfterLastSweep = 0; auto isFree = [this] (Chunk *c) { - bool isUsed = c->sweep(); + bool isUsed = c->sweep(engine); if (isUsed) { c->sortIntoBins(freeBins, NumBins); usedSlotsAfterLastSweep += c->nUsedSlots(); } else { + Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage); chunkAllocator->free(c); } return !isUsed; @@ -602,7 +610,8 @@ void BlockAllocator::sweep() void BlockAllocator::freeAll() { for (auto c : chunks) { - c->freeAll(); + c->freeAll(engine); + Q_V4_PROFILE_DEALLOC(engine, Chunk::DataSize, Profiling::HeapPage); chunkAllocator->free(c); } } @@ -642,6 +651,7 @@ HeapItem *HugeItemAllocator::allocate(size_t size) { Chunk *c = chunkAllocator->allocate(size); chunks.push_back(HugeChunk{c, size}); Chunk::setBit(c->objectBitmap, c->first() - c->realBase()); + Q_V4_PROFILE_ALLOC(engine, size, Profiling::LargeItem); return c->first(); } @@ -660,8 +670,10 @@ void HugeItemAllocator::sweep() { auto isBlack = [this] (const HugeChunk &c) { bool b = c.chunk->first()->isBlack(); Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()); - if (!b) + if (!b) { + Q_V4_PROFILE_DEALLOC(engine, c.size, Profiling::LargeItem); freeHugeChunk(chunkAllocator, c); + } return !b; }; @@ -672,6 +684,7 @@ void HugeItemAllocator::sweep() { void HugeItemAllocator::freeAll() { for (auto &c : chunks) { + Q_V4_PROFILE_DEALLOC(engine, c.size, Profiling::LargeItem); freeHugeChunk(chunkAllocator, c); } } @@ -681,8 +694,8 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) : engine(engine) , chunkAllocator(new ChunkAllocator) , stackAllocator(chunkAllocator) - , blockAllocator(chunkAllocator) - , hugeItemAllocator(chunkAllocator) + , blockAllocator(chunkAllocator, engine) + , hugeItemAllocator(chunkAllocator, engine) , m_persistentValues(new PersistentValueStorage(engine)) , m_weakValues(new PersistentValueStorage(engine)) , unmanagedHeapSizeGCLimit(MIN_UNMANAGED_HEAPSIZE_GC_LIMIT) diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 77c5885dfe..e3cccb6aa0 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -117,8 +117,8 @@ struct StackAllocator { }; struct BlockAllocator { - BlockAllocator(ChunkAllocator *chunkAllocator) - : chunkAllocator(chunkAllocator) + BlockAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine) + : chunkAllocator(chunkAllocator), engine(engine) { memset(freeBins, 0, sizeof(freeBins)); #if MM_DEBUG @@ -161,6 +161,7 @@ struct BlockAllocator { size_t usedSlotsAfterLastSweep = 0; HeapItem *freeBins[NumBins]; ChunkAllocator *chunkAllocator; + ExecutionEngine *engine; std::vector chunks; #if MM_DEBUG uint allocations[NumBins]; @@ -168,8 +169,8 @@ struct BlockAllocator { }; struct HugeItemAllocator { - HugeItemAllocator(ChunkAllocator *chunkAllocator) - : chunkAllocator(chunkAllocator) + HugeItemAllocator(ChunkAllocator *chunkAllocator, ExecutionEngine *engine) + : chunkAllocator(chunkAllocator), engine(engine) {} HeapItem *allocate(size_t size); @@ -184,6 +185,7 @@ struct HugeItemAllocator { } ChunkAllocator *chunkAllocator; + ExecutionEngine *engine; struct HugeChunk { Chunk *chunk; size_t size; diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index ef93971ab8..7c82fef056 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -179,8 +179,8 @@ struct Chunk { return usedSlots; } - bool sweep(); - void freeAll(); + bool sweep(ExecutionEngine *engine); + void freeAll(ExecutionEngine *engine); void sortIntoBins(HeapItem **bins, uint nBins); }; diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml new file mode 100644 index 0000000000..f39dcdf16a --- /dev/null +++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/memory.qml @@ -0,0 +1,17 @@ +import QtQml 2.0 + +Timer { + interval: 1 + running: true + + function recurse(i) { + var x = { t: [1, 2, 3, 4] } + console.log(x.t[i]); + if (i < 3) + recurse(i + 1); + else + Qt.quit(); + } + + onTriggered: recurse(0) +} diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro index f5e3dbdc2f..56840d5c8f 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro +++ b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro @@ -21,4 +21,5 @@ OTHER_FILES += \ data/TestImage_2x2.png \ data/signalSourceLocation.qml \ data/javascript.qml \ - data/timer.qml + data/timer.qml \ + data/memory.qml diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp index b8e1f1f21d..bc6c51707a 100644 --- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp +++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp @@ -312,6 +312,7 @@ private slots: void signalSourceLocation(); void javascript(); void flushInterval(); + void memory(); }; #define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks)) @@ -756,6 +757,26 @@ void tst_QQmlProfilerService::flushInterval() checkJsHeap(); } +void tst_QQmlProfilerService::memory() +{ + connect(true, "memory.qml"); + if (QTest::currentTestFailed() || QTestResult::skipCurrentTest()) + return; + + m_client->sendRecordingStatus(true); + + checkTraceReceived(); + checkJsHeap(); + + int smallItems = 0; + for (auto message : m_client->jsHeapMessages) { + if (message.detailType == QV4::Profiling::SmallItem) + ++smallItems; + } + + QVERIFY(smallItems > 5); +} + QTEST_MAIN(tst_QQmlProfilerService) #include "tst_qqmlprofilerservice.moc" -- cgit v1.2.3