aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2014-06-02 18:33:19 +0200
committerThe Qt Project <[email protected]>2014-06-06 18:18:35 +0200
commitac56e7cda724aa7463ef6ffe5f0e93bd3208cb51 (patch)
treec96c9a5e611937b658f13ab21e9d71787c12b512 /src/qml/jsruntime
parent4a127e3b2e98c1d690d9baf346e4d3ee8aa4edf1 (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.cpp2
-rw-r--r--src/qml/jsruntime/qv4mm.cpp20
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp4
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h40
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