diff options
author | Ulf Hermann <[email protected]> | 2014-06-02 18:33:19 +0200 |
---|---|---|
committer | The Qt Project <[email protected]> | 2014-06-06 18:18:35 +0200 |
commit | ac56e7cda724aa7463ef6ffe5f0e93bd3208cb51 (patch) | |
tree | c96c9a5e611937b658f13ab21e9d71787c12b512 /src/qml/jsruntime | |
parent | 4a127e3b2e98c1d690d9baf346e4d3ee8aa4edf1 (diff) |
Javascript heap profiler
This profiler tracks every memory allocation and deallocation, by the
MemoryManager as well as the V4 VM, and exposes them as a stream of
events to the profiler service.
Change-Id: I85297d498f0a7eb55df5d7829c4b7307de980519
Reviewed-by: Simon Hausmann <[email protected]>
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r-- | src/qml/jsruntime/qv4engine.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4mm.cpp | 20 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4profiling.cpp | 4 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4profiling_p.h | 40 |
4 files changed, 60 insertions, 6 deletions
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 8916cc597e..1eab157ecc 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -418,7 +418,9 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) ExecutionEngine::~ExecutionEngine() { delete debugger; + debugger = 0; delete profiler; + profiler = 0; delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = 0; delete identifierTable; diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index ca2ccd33f7..472fa4071a 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -57,6 +57,7 @@ #include <cstdlib> #include <algorithm> #include "qv4alloca_p.h" +#include "qv4profiling_p.h" #ifdef V4_USE_VALGRIND #include <valgrind/valgrind.h> @@ -102,6 +103,7 @@ struct MemoryManager::Data struct LargeItem { LargeItem *next; + size_t size; void *data; Managed *managed() { @@ -149,8 +151,10 @@ struct MemoryManager::Data ~Data() { - for (QVector<Chunk>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) + for (QVector<Chunk>::iterator i = heapChunks.begin(), ei = heapChunks.end(); i != ei; ++i) { + Q_V4_PROFILE_DEALLOC(engine, 0, i->memory.size(), Profiling::HeapPage); i->memory.deallocate(); + } } }; @@ -190,9 +194,12 @@ Managed *MemoryManager::alloc(std::size_t size) // doesn't fit into a small bucket if (size >= MemoryManager::Data::MaxItemSize) { // we use malloc for this - MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(malloc(size + sizeof(MemoryManager::Data::LargeItem))); + MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>( + malloc(Q_V4_PROFILE_ALLOC(engine(), size + sizeof(MemoryManager::Data::LargeItem), + Profiling::LargeItem))); memset(item, 0, size + sizeof(MemoryManager::Data::LargeItem)); item->next = m_d->largeItems; + item->size = size; m_d->largeItems = item; return item->managed(); } @@ -218,7 +225,9 @@ Managed *MemoryManager::alloc(std::size_t size) std::size_t allocSize = m_d->maxChunkSize*(size_t(1) << shift); allocSize = roundUpToMultipleOf(WTF::pageSize(), allocSize); Data::Chunk allocation; - allocation.memory = PageAllocation::allocate(allocSize, OSAllocator::JSGCHeapPages); + allocation.memory = PageAllocation::allocate( + Q_V4_PROFILE_ALLOC(engine(), allocSize, Profiling::HeapPage), + OSAllocator::JSGCHeapPages); allocation.chunkSize = int(size); m_d->heapChunks.append(allocation); std::sort(m_d->heapChunks.begin(), m_d->heapChunks.end()); @@ -247,6 +256,7 @@ Managed *MemoryManager::alloc(std::size_t size) #ifdef V4_USE_VALGRIND VALGRIND_MEMPOOL_ALLOC(this, m, size); #endif + Q_V4_PROFILE_ALLOC(engine(), size, Profiling::SmallItem); ++m_d->allocCount[pos]; ++m_d->totalAlloc; @@ -363,7 +373,8 @@ void MemoryManager::sweep(bool lastSweep) m->internalClass->vtable->destroy(m); *last = i->next; - free(i); + free(Q_V4_PROFILE_DEALLOC(engine(), i, i->size + sizeof(Data::LargeItem), + Profiling::LargeItem)); i = *last; } @@ -409,6 +420,7 @@ void MemoryManager::sweep(char *chunkStart, std::size_t chunkSize, size_t size) VALGRIND_DISABLE_ERROR_REPORTING; VALGRIND_MEMPOOL_FREE(this, m); #endif + Q_V4_PROFILE_DEALLOC(engine(), m, size, Profiling::SmallItem); *f = m; } } diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp index 8a0cc56448..f1c70c6b33 100644 --- a/src/qml/jsruntime/qv4profiling.cpp +++ b/src/qml/jsruntime/qv4profiling.cpp @@ -63,7 +63,9 @@ FunctionCallProperties FunctionCall::resolve() const Profiler::Profiler() : enabled(false) { static int metatype = qRegisterMetaType<QList<QV4::Profiling::FunctionCallProperties> >(); + static int metatype2 = qRegisterMetaType<QList<QV4::Profiling::MemoryAllocationProperties> >(); Q_UNUSED(metatype); + Q_UNUSED(metatype2); m_timer.start(); } @@ -87,7 +89,7 @@ void Profiler::reportData() FunctionCallProperties props = call.resolve(); resolved.insert(std::upper_bound(resolved.begin(), resolved.end(), props, comp), props); } - emit dataReady(resolved); + emit dataReady(resolved, m_memory_data); } void Profiler::startProfiling() diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 6869b3134d..cafe36861d 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -54,6 +54,12 @@ namespace QV4 { namespace Profiling { +enum MemoryType { + HeapPage, + LargeItem, + SmallItem +}; + struct FunctionCallProperties { qint64 start; qint64 end; @@ -63,6 +69,12 @@ struct FunctionCallProperties { int column; }; +struct MemoryAllocationProperties { + qint64 timestamp; + qint64 size; + MemoryType type; +}; + class FunctionCall { public: @@ -101,6 +113,14 @@ private: qint64 m_end; }; +#define Q_V4_PROFILE_ALLOC(engine, size, type)\ + (engine->profiler && engine->profiler->enabled ?\ + engine->profiler->trackAlloc(size, type) : size) + +#define Q_V4_PROFILE_DEALLOC(engine, pointer, size, type) \ + (engine->profiler && engine->profiler->enabled ?\ + engine->profiler->trackDealloc(pointer, size, type) : pointer) + #define Q_V4_PROFILE(engine, ctx, function)\ ((engine->profiler && engine->profiler->enabled) ?\ Profiling::FunctionCallProfiler::profileCall(engine->profiler, ctx, function) :\ @@ -112,6 +132,20 @@ class Q_QML_EXPORT Profiler : public QObject { public: Profiler(); + size_t trackAlloc(size_t size, MemoryType type) + { + MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)size, type}; + m_memory_data.append(allocation); + return size; + } + + void *trackDealloc(void *pointer, size_t size, MemoryType type) + { + MemoryAllocationProperties allocation = {m_timer.nsecsElapsed(), (qint64)-size, type}; + m_memory_data.append(allocation); + return pointer; + } + bool enabled; public slots: @@ -121,11 +155,13 @@ public slots: void setTimer(const QElapsedTimer &timer) { m_timer = timer; } signals: - void dataReady(const QList<QV4::Profiling::FunctionCallProperties> &); + void dataReady(const QList<QV4::Profiling::FunctionCallProperties> &, + const QList<QV4::Profiling::MemoryAllocationProperties> &); private: QElapsedTimer m_timer; QVector<FunctionCall> m_data; + QList<MemoryAllocationProperties> m_memory_data; friend class FunctionCallProfiler; }; @@ -160,10 +196,12 @@ public: } // namespace Profiling } // namespace QV4 +Q_DECLARE_TYPEINFO(QV4::Profiling::MemoryAllocationProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCallProperties, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QV4::Profiling::FunctionCall, Q_MOVABLE_TYPE); QT_END_NAMESPACE Q_DECLARE_METATYPE(QList<QV4::Profiling::FunctionCallProperties>) +Q_DECLARE_METATYPE(QList<QV4::Profiling::MemoryAllocationProperties>) #endif // QV4PROFILING_H |