aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/debugger/qqmlprofiler.cpp13
-rw-r--r--src/qml/debugger/qqmlprofiler_p.h2
-rw-r--r--src/qml/debugger/qqmlprofilerservice.cpp88
-rw-r--r--src/qml/debugger/qqmlprofilerservice_p.h10
-rw-r--r--src/qml/debugger/qv4profileradapter.cpp52
-rw-r--r--src/qml/debugger/qv4profileradapter_p.h1
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp5
-rw-r--r--src/quick/util/qquickprofiler.cpp16
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml14
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro3
-rw-r--r--tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp23
11 files changed, 177 insertions, 50 deletions
diff --git a/src/qml/debugger/qqmlprofiler.cpp b/src/qml/debugger/qqmlprofiler.cpp
index 2fd27617b5..30e089ac44 100644
--- a/src/qml/debugger/qqmlprofiler.cpp
+++ b/src/qml/debugger/qqmlprofiler.cpp
@@ -110,7 +110,10 @@ qint64 QQmlProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messag
void QQmlProfilerAdapter::receiveData(const QVector<QQmlProfilerData> &new_data)
{
- data = new_data;
+ if (data.isEmpty())
+ data = new_data;
+ else
+ data.append(new_data);
service->dataReady(this);
}
@@ -131,16 +134,12 @@ void QQmlProfiler::stopProfiling()
{
featuresEnabled = false;
reportData();
- m_data.clear();
}
void QQmlProfiler::reportData()
{
- QVector<QQmlProfilerData> result;
- result.reserve(m_data.size());
- for (int i = 0; i < m_data.size(); ++i)
- result.append(m_data[i]);
- emit dataReady(result);
+ emit dataReady(m_data);
+ m_data.clear();
}
QT_END_NAMESPACE
diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h
index 93a864baec..67e6c9eda6 100644
--- a/src/qml/debugger/qqmlprofiler_p.h
+++ b/src/qml/debugger/qqmlprofiler_p.h
@@ -175,7 +175,7 @@ signals:
protected:
QElapsedTimer m_timer;
- QVarLengthArray<QQmlProfilerData> m_data;
+ QVector<QQmlProfilerData> m_data;
};
class QQmlProfilerAdapter : public QQmlAbstractProfilerAdapter {
diff --git a/src/qml/debugger/qqmlprofilerservice.cpp b/src/qml/debugger/qqmlprofilerservice.cpp
index 6f50b41b1b..2b26cdccf9 100644
--- a/src/qml/debugger/qqmlprofilerservice.cpp
+++ b/src/qml/debugger/qqmlprofilerservice.cpp
@@ -48,7 +48,8 @@ QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(QQmlProfilerService, profilerInstance)
QQmlProfilerService::QQmlProfilerService()
- : QQmlConfigurableDebugService<QQmlDebugService>(QStringLiteral("CanvasFrameRate"), 1)
+ : QQmlConfigurableDebugService<QQmlDebugService>(QStringLiteral("CanvasFrameRate"), 1),
+ m_waitingForStop(false)
{
m_timer.start();
}
@@ -242,6 +243,8 @@ void QQmlProfilerService::startProfiling(QQmlEngine *engine, quint64 features)
if (!profiler->isRunning())
profiler->startProfiling(features);
}
+
+ emit startFlushTimer();
}
emit messageToClient(name(), message);
@@ -273,6 +276,9 @@ void QQmlProfilerService::stopProfiling(QQmlEngine *engine)
}
}
+ if (stopping.isEmpty())
+ return;
+
foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
if (!profiler->isRunning())
continue;
@@ -284,6 +290,9 @@ void QQmlProfilerService::stopProfiling(QQmlEngine *engine)
}
}
+ emit stopFlushTimer();
+ m_waitingForStop = true;
+
foreach (QQmlAbstractProfilerAdapter *profiler, reporting)
profiler->reportData();
@@ -299,16 +308,19 @@ void QQmlProfilerService::sendMessages()
QList<QByteArray> messages;
QByteArray data;
- QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly);
- traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace;
- QSet<QQmlEngine *> seen;
- foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) {
- for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin());
- i != m_engineProfilers.end(); ++i) {
- if (i.value() == profiler && !seen.contains(i.key())) {
- seen << i.key();
- traceEnd << idForObject(i.key());
+ if (m_waitingForStop) {
+ QQmlDebugStream traceEnd(&data, QIODevice::WriteOnly);
+ traceEnd << m_timer.nsecsElapsed() << (int)Event << (int)EndTrace;
+
+ QSet<QQmlEngine *> seen;
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_startTimes) {
+ for (QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *>::iterator i(m_engineProfilers.begin());
+ i != m_engineProfilers.end(); ++i) {
+ if (i.value() == profiler && !seen.contains(i.key())) {
+ seen << i.key();
+ traceEnd << idForObject(i.key());
+ }
}
}
}
@@ -325,15 +337,26 @@ void QQmlProfilerService::sendMessages()
}
}
- //indicate completion
- messages << data;
- data.clear();
+ if (m_waitingForStop) {
+ //indicate completion
+ messages << data;
+ data.clear();
- QQmlDebugStream ds(&data, QIODevice::WriteOnly);
- ds << (qint64)-1 << (int)Complete;
- messages << data;
+ QQmlDebugStream ds(&data, QIODevice::WriteOnly);
+ ds << (qint64)-1 << (int)Complete;
+ messages << data;
+ m_waitingForStop = false;
+ }
emit messagesToClient(name(), messages);
+
+ // Restart flushing if any profilers are still running
+ foreach (const QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) {
+ if (profiler->isRunning()) {
+ emit startFlushTimer();
+ break;
+ }
+ }
}
void QQmlProfilerService::stateAboutToBeChanged(QQmlDebugService::State newState)
@@ -360,11 +383,25 @@ void QQmlProfilerService::messageReceived(const QByteArray &message)
int engineId = -1;
quint64 features = std::numeric_limits<quint64>::max();
bool enabled;
+ uint flushInterval = 0;
stream >> enabled;
if (!stream.atEnd())
stream >> engineId;
if (!stream.atEnd())
stream >> features;
+ if (!stream.atEnd()) {
+ stream >> flushInterval;
+ m_flushTimer.setInterval(flushInterval);
+ if (flushInterval > 0) {
+ connect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush()));
+ connect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start()));
+ connect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop()));
+ } else {
+ disconnect(&m_flushTimer, SIGNAL(timeout()), this, SLOT(flush()));
+ disconnect(this, SIGNAL(startFlushTimer()), &m_flushTimer, SLOT(start()));
+ disconnect(this, SIGNAL(stopFlushTimer()), &m_flushTimer, SLOT(stop()));
+ }
+ }
// If engineId == -1 objectForId() and then the cast will return 0.
if (enabled)
@@ -375,4 +412,23 @@ void QQmlProfilerService::messageReceived(const QByteArray &message)
stopWaiting();
}
+void QQmlProfilerService::flush()
+{
+ QMutexLocker lock(&m_configMutex);
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_engineProfilers) {
+ if (profiler->isRunning()) {
+ m_startTimes.insert(-1, profiler);
+ profiler->reportData();
+ }
+ }
+
+ foreach (QQmlAbstractProfilerAdapter *profiler, m_globalProfilers) {
+ if (profiler->isRunning()) {
+ m_startTimes.insert(-1, profiler);
+ profiler->reportData();
+ }
+ }
+}
+
QT_END_NAMESPACE
diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h
index 518f60bb7c..d593c43b9c 100644
--- a/src/qml/debugger/qqmlprofilerservice_p.h
+++ b/src/qml/debugger/qqmlprofilerservice_p.h
@@ -57,6 +57,7 @@
#include <QtCore/qvector.h>
#include <QtCore/qstringbuilder.h>
#include <QtCore/qwaitcondition.h>
+#include <QtCore/qtimer.h>
#include <limits>
@@ -90,6 +91,13 @@ public:
void dataReady(QQmlAbstractProfilerAdapter *profiler);
+signals:
+ void startFlushTimer();
+ void stopFlushTimer();
+
+private slots:
+ void flush();
+
protected:
virtual void stateAboutToBeChanged(State state);
virtual void messageReceived(const QByteArray &);
@@ -101,6 +109,8 @@ private:
void removeProfilerFromStartTimes(const QQmlAbstractProfilerAdapter *profiler);
QElapsedTimer m_timer;
+ QTimer m_flushTimer;
+ bool m_waitingForStop;
QList<QQmlAbstractProfilerAdapter *> m_globalProfilers;
QMultiHash<QQmlEngine *, QQmlAbstractProfilerAdapter *> m_engineProfilers;
diff --git a/src/qml/debugger/qv4profileradapter.cpp b/src/qml/debugger/qv4profileradapter.cpp
index 667657a062..0da8c47939 100644
--- a/src/qml/debugger/qv4profileradapter.cpp
+++ b/src/qml/debugger/qv4profileradapter.cpp
@@ -70,16 +70,34 @@ qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &m
return memory_data.length() == memoryPos ? -1 : memory_data[memoryPos].timestamp;
}
+qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &messages,
+ qint64 callNext)
+{
+ if (callNext == -1) {
+ data.clear();
+ dataPos = 0;
+ }
+
+ qint64 memoryNext = appendMemoryEvents(until, messages);
+
+ if (memoryNext == -1) {
+ memory_data.clear();
+ memoryPos = 0;
+ return callNext;
+ }
+
+ return callNext == -1 ? memoryNext : qMin(callNext, memoryNext);
+}
+
qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
{
QByteArray message;
while (true) {
while (!stack.isEmpty() && (dataPos == data.length() ||
stack.top() <= data[dataPos].start)) {
- if (stack.top() > until) {
- qint64 memoryNext = appendMemoryEvents(until, messages);
- return memoryNext == -1 ? stack.top() : qMin(stack.top(), memoryNext);
- }
+ if (stack.top() > until)
+ return finalizeMessages(until, messages, stack.top());
+
appendMemoryEvents(stack.top(), messages);
QQmlDebugStream d(&message, QIODevice::WriteOnly);
d << stack.pop() << RangeEnd << Javascript;
@@ -87,10 +105,9 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
}
while (dataPos != data.length() && (stack.empty() || data[dataPos].start < stack.top())) {
const QV4::Profiling::FunctionCallProperties &props = data[dataPos];
- if (props.start > until) {
- qint64 memory_next = appendMemoryEvents(until, messages);
- return memory_next == -1 ? props.start : qMin(props.start, memory_next);
- }
+ if (props.start > until)
+ return finalizeMessages(until, messages, props.start);
+
appendMemoryEvents(props.start, messages);
QQmlDebugStream d_start(&message, QIODevice::WriteOnly);
@@ -110,7 +127,7 @@ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &message
++dataPos;
}
if (stack.empty() && dataPos == data.length())
- return appendMemoryEvents(until, messages);
+ return finalizeMessages(until, messages, -1);
}
}
@@ -118,10 +135,19 @@ void QV4ProfilerAdapter::receiveData(
const QVector<QV4::Profiling::FunctionCallProperties> &new_data,
const QVector<QV4::Profiling::MemoryAllocationProperties> &new_memory_data)
{
- data = new_data;
- memory_data = new_memory_data;
- dataPos = memoryPos = 0;
- stack.clear();
+ // In rare cases it could be that another flush or stop event is processed while data from
+ // the previous one is still pending. In that case we just append the data.
+
+ if (data.isEmpty())
+ data = new_data;
+ else
+ data.append(new_data);
+
+ if (memory_data.isEmpty())
+ memory_data = new_memory_data;
+ else
+ memory_data.append(new_memory_data);
+
service->dataReady(this);
}
diff --git a/src/qml/debugger/qv4profileradapter_p.h b/src/qml/debugger/qv4profileradapter_p.h
index 645b20dd03..34c37baf59 100644
--- a/src/qml/debugger/qv4profileradapter_p.h
+++ b/src/qml/debugger/qv4profileradapter_p.h
@@ -73,6 +73,7 @@ private:
int memoryPos;
QStack<qint64> stack;
qint64 appendMemoryEvents(qint64 until, QList<QByteArray> &messages);
+ qint64 finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext);
};
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index a0db2bf5cf..9b77599904 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -84,14 +84,13 @@ void Profiler::reportData()
resolved.insert(std::upper_bound(resolved.begin(), resolved.end(), props, comp), props);
}
emit dataReady(resolved, m_memory_data);
+ m_data.clear();
+ m_memory_data.clear();
}
void Profiler::startProfiling(quint64 features)
{
if (featuresEnabled == 0) {
- m_data.clear();
- m_memory_data.clear();
-
if (features & (1 << FeatureMemoryAllocation)) {
qint64 timestamp = m_timer.nsecsElapsed();
MemoryAllocationProperties heap = {timestamp,
diff --git a/src/quick/util/qquickprofiler.cpp b/src/quick/util/qquickprofiler.cpp
index d9132a9cb2..7bd32f07e8 100644
--- a/src/quick/util/qquickprofiler.cpp
+++ b/src/quick/util/qquickprofiler.cpp
@@ -113,10 +113,15 @@ void QQuickProfilerData::toByteArrays(QList<QByteArray> &messages) const
qint64 QQuickProfiler::sendMessages(qint64 until, QList<QByteArray> &messages)
{
QMutexLocker lock(&m_dataMutex);
- while (next < m_data.size() && m_data[next].time <= until) {
- m_data[next++].toByteArrays(messages);
+ while (next < m_data.size()) {
+ if (m_data[next].time <= until)
+ m_data[next++].toByteArrays(messages);
+ else
+ return m_data[next].time;
}
- return next < m_data.size() ? m_data[next].time : -1;
+ m_data.clear();
+ next = 0;
+ return -1;
}
void QQuickProfiler::initialize()
@@ -196,17 +201,12 @@ void QQuickProfiler::stopProfilingImpl()
{
QMutexLocker lock(&m_dataMutex);
featuresEnabled = 0;
- next = 0;
}
service->dataReady(this);
}
void QQuickProfiler::reportDataImpl()
{
- {
- QMutexLocker lock(&m_dataMutex);
- next = 0;
- }
service->dataReady(this);
}
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml b/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml
new file mode 100644
index 0000000000..18b8947172
--- /dev/null
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/data/timer.qml
@@ -0,0 +1,14 @@
+import QtQuick 2.0
+
+Rectangle {
+ width: 100
+ height: 62
+
+ Timer {
+ running: true
+ repeat: true
+ interval: 50
+ onTriggered: height = (2 * height) % 99;
+ }
+}
+
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro
index ec84139797..e422d3ef99 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/qqmlprofilerservice.pro
@@ -21,4 +21,5 @@ OTHER_FILES += \
data/scenegraphTest.qml \
data/TestImage_2x2.png \
data/signalSourceLocation.qml \
- data/javascript.qml
+ data/javascript.qml \
+ data/timer.qml
diff --git a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
index f9f05964a7..744830b55b 100644
--- a/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
+++ b/tests/auto/qml/debugger/qqmlprofilerservice/tst_qqmlprofilerservice.cpp
@@ -145,10 +145,12 @@ public:
QVector<QQmlProfilerData> asynchronousMessages;
QVector<QQmlProfilerData> pixmapMessages;
- void setTraceState(bool enabled) {
+ void setTraceState(bool enabled, quint32 flushInterval = 0) {
QByteArray message;
QDataStream stream(&message, QIODevice::WriteOnly);
stream << enabled;
+ if (enabled && flushInterval)
+ stream << -1 << std::numeric_limits<quint64>::max() << flushInterval;
sendMessage(message);
}
@@ -213,6 +215,7 @@ private slots:
void controlFromJS();
void signalSourceLocation();
void javascript();
+ void flushInterval();
};
#define VERIFY(type, position, expected, checks) QVERIFY(verify(type, position, expected, checks))
@@ -766,6 +769,24 @@ void tst_QQmlProfilerService::javascript()
VERIFY(MessageListJavaScript, 21, expected, CheckMessageType | CheckDetailType);
}
+void tst_QQmlProfilerService::flushInterval()
+{
+ connect(true, "timer.qml");
+ QVERIFY(m_client);
+ QTRY_COMPARE(m_client->state(), QQmlDebugClient::Enabled);
+
+ m_client->setTraceState(true, 1);
+
+ // Make sure we get multiple messages
+ QTRY_VERIFY(m_client->qmlMessages.length() > 0);
+ QVERIFY(m_client->qmlMessages.length() < 100);
+ QTRY_VERIFY(m_client->qmlMessages.length() > 100);
+
+ m_client->setTraceState(false);
+ checkTraceReceived();
+ checkJsHeap();
+}
+
QTEST_MAIN(tst_QQmlProfilerService)
#include "tst_qqmlprofilerservice.moc"