Commit 5ba6f04d authored by Ulf Hermann's avatar Ulf Hermann
Browse files

QmlProfiler: Load the timeline model data event by event



All the models do the same thing when loading the data: They iterate
the list of events, determine for each one if they accept it, and if
so, they load it. After the list has been fully loaded, they do some
finalization. This can be centralized, and ultimately we won't need to
expose the central QVector<QmlEvent> for that anymore.

Change-Id: Ia82facfdc3968200bbec323a02f2fcc02ac44e9e
Reviewed-by: Joerg Bornemann's avatarJoerg Bornemann <joerg.bornemann@qt.io>
Reviewed-by: Ulf Hermann's avatarUlf Hermann <ulf.hermann@qt.io>
parent 67378a79
......@@ -28,11 +28,6 @@
namespace QmlProfiler {
namespace Internal {
bool DebugMessagesModel::accepted(const QmlEventType &event) const
{
return event.message == DebugMessage;
}
DebugMessagesModel::DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent) :
QmlProfilerTimelineModel(manager, DebugMessage, MaximumRangeType, ProfileDebugMessages, parent),
m_maximumMsgType(-1)
......@@ -99,24 +94,16 @@ int DebugMessagesModel::collapsedRow(int index) const
return 1;
}
void DebugMessagesModel::loadData()
void DebugMessagesModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
{
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
if (simpleModel->isEmpty())
return;
const QVector<QmlEventType> &types = simpleModel->eventTypes();
foreach (const QmlEvent &event, simpleModel->events()) {
const QmlEventType &type = types[event.typeIndex()];
if (!accepted(type) || event.timestamp() < 0)
continue;
m_data.insert(insert(event.timestamp(), 0, type.detailType),
MessageData(event.string(), event.typeIndex()));
if (type.detailType > m_maximumMsgType)
m_maximumMsgType = event.typeIndex();
}
m_data.insert(insert(event.timestamp(), 0, type.detailType),
MessageData(event.string(), event.typeIndex()));
if (type.detailType > m_maximumMsgType)
m_maximumMsgType = event.typeIndex();
}
void DebugMessagesModel::finalize()
{
setCollapsedRowCount(2);
setExpandedRowCount(m_maximumMsgType + 2);
}
......
......@@ -34,9 +34,6 @@ class DebugMessagesModel : public QmlProfilerTimelineModel
{
Q_OBJECT
protected:
bool accepted(const QmlEventType &event) const override;
public:
DebugMessagesModel(QmlProfilerModelManager *manager, QObject *parent = 0);
......@@ -46,7 +43,8 @@ public:
QVariantMap details(int index) const override;
int expandedRow(int index) const override;
int collapsedRow(int index) const override;
void loadData() override;
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
QVariantMap location(int index) const override;
......
......@@ -140,29 +140,22 @@ int InputEventsModel::collapsedRow(int index) const
return 1;
}
void InputEventsModel::loadData()
void InputEventsModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
{
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
if (simpleModel->isEmpty())
return;
const QVector<QmlEventType> &types = simpleModel->eventTypes();
foreach (const QmlEvent &event, simpleModel->events()) {
const QmlEventType &type = types[event.typeIndex()];
if (!accepted(type))
continue;
m_data.insert(insert(event.timestamp(), 0, type.detailType),
InputEvent(static_cast<InputEventType>(event.number<qint32>(0)),
event.number<qint32>(1), event.number<qint32>(2)));
if (type.detailType == Mouse) {
if (m_mouseTypeId == -1)
m_mouseTypeId = event.typeIndex();
} else if (m_keyTypeId == -1) {
m_keyTypeId = event.typeIndex();
}
m_data.insert(insert(event.timestamp(), 0, type.detailType),
InputEvent(static_cast<InputEventType>(event.number<qint32>(0)),
event.number<qint32>(1), event.number<qint32>(2)));
if (type.detailType == Mouse) {
if (m_mouseTypeId == -1)
m_mouseTypeId = event.typeIndex();
} else if (m_keyTypeId == -1) {
m_keyTypeId = event.typeIndex();
}
}
void InputEventsModel::finalize()
{
setCollapsedRowCount(2);
setExpandedRowCount(3);
}
......
......@@ -36,6 +36,9 @@ class InputEventsModel : public QmlProfilerTimelineModel
protected:
bool accepted(const QmlEventType &event) const;
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
public:
struct InputEvent {
......@@ -53,8 +56,6 @@ public:
QVariantMap details(int index) const;
int expandedRow(int index) const;
int collapsedRow(int index) const;
void loadData();
void clear();
private:
static QMetaEnum metaEnum(const char *name);
......
......@@ -27,15 +27,12 @@
#include "qmlprofilermodelmanager.h"
#include "qmlprofilereventtypes.h"
#include <QStack>
namespace QmlProfiler {
namespace Internal {
MemoryUsageModel::MemoryUsageModel(QmlProfilerModelManager *manager, QObject *parent) :
QmlProfilerTimelineModel(manager, MemoryAllocation, MaximumRangeType, ProfileMemory, parent)
{
m_maxSize = 1;
announceFeatures((1ULL << mainFeature()) | Constants::QML_JS_RANGE_FEATURES);
}
......@@ -137,94 +134,78 @@ QVariantMap MemoryUsageModel::details(int index) const
return result;
}
struct RangeStackFrame {
RangeStackFrame() : originTypeIndex(-1), startTime(-1), endTime(-1) {}
RangeStackFrame(int originTypeIndex, qint64 startTime, qint64 endTime) :
originTypeIndex(originTypeIndex), startTime(startTime), endTime(endTime) {}
int originTypeIndex;
qint64 startTime;
qint64 endTime;
};
void MemoryUsageModel::loadData()
bool MemoryUsageModel::accepted(const QmlEventType &type) const
{
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
if (simpleModel->isEmpty())
return;
return QmlProfilerTimelineModel::accepted(type) || type.rangeType != MaximumRangeType;
}
qint64 currentSize = 0;
qint64 currentUsage = 0;
int currentUsageIndex = -1;
int currentJSHeapIndex = -1;
QStack<RangeStackFrame> rangeStack;
const QVector<QmlEventType> &types = simpleModel->eventTypes();
foreach (const QmlEvent &event, simpleModel->events()) {
const QmlEventType &type = types[event.typeIndex()];
while (!rangeStack.empty() && rangeStack.top().endTime < event.timestamp())
rangeStack.pop();
if (!accepted(type)) {
if (type.rangeType != MaximumRangeType) {
rangeStack.push(RangeStackFrame(event.typeIndex(), event.timestamp(),
event.timestamp() + event.duration()));
}
continue;
void MemoryUsageModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
{
while (!m_rangeStack.empty() && m_rangeStack.top().endTime < event.timestamp())
m_rangeStack.pop();
if (type.message != MemoryAllocation) {
if (type.rangeType != MaximumRangeType) {
m_rangeStack.push(RangeStackFrame(event.typeIndex(), event.timestamp(),
event.timestamp() + event.duration()));
}
return;
}
if (type.detailType == SmallItem || type.detailType == LargeItem) {
if (!rangeStack.empty() && currentUsageIndex > -1 &&
type.detailType == selectionId(currentUsageIndex) &&
m_data[currentUsageIndex].originTypeIndex == rangeStack.top().originTypeIndex &&
rangeStack.top().startTime < startTime(currentUsageIndex)) {
m_data[currentUsageIndex].update(event.number<qint64>(0));
currentUsage = m_data[currentUsageIndex].size;
} else {
MemoryAllocationItem allocation(event.typeIndex(), currentUsage,
rangeStack.empty() ? -1 : rangeStack.top().originTypeIndex);
allocation.update(event.number<qint64>(0));
currentUsage = allocation.size;
if (currentUsageIndex != -1) {
insertEnd(currentUsageIndex,
event.timestamp() - startTime(currentUsageIndex) - 1);
}
currentUsageIndex = insertStart(event.timestamp(), SmallItem);
m_data.insert(currentUsageIndex, allocation);
if (type.detailType == SmallItem || type.detailType == LargeItem) {
if (!m_rangeStack.empty() && m_currentUsageIndex > -1 &&
type.detailType == selectionId(m_currentUsageIndex) &&
m_data[m_currentUsageIndex].originTypeIndex == m_rangeStack.top().originTypeIndex &&
m_rangeStack.top().startTime < startTime(m_currentUsageIndex)) {
m_data[m_currentUsageIndex].update(event.number<qint64>(0));
m_currentUsage = m_data[m_currentUsageIndex].size;
} else {
MemoryAllocationItem allocation(event.typeIndex(), m_currentUsage,
m_rangeStack.empty() ? -1 : m_rangeStack.top().originTypeIndex);
allocation.update(event.number<qint64>(0));
m_currentUsage = allocation.size;
if (m_currentUsageIndex != -1) {
insertEnd(m_currentUsageIndex,
event.timestamp() - startTime(m_currentUsageIndex) - 1);
}
m_currentUsageIndex = insertStart(event.timestamp(), SmallItem);
m_data.insert(m_currentUsageIndex, allocation);
}
}
if (type.detailType == HeapPage || type.detailType == LargeItem) {
if (!rangeStack.empty() && currentJSHeapIndex > -1 &&
type.detailType == selectionId(currentJSHeapIndex) &&
m_data[currentJSHeapIndex].originTypeIndex ==
rangeStack.top().originTypeIndex &&
rangeStack.top().startTime < startTime(currentJSHeapIndex)) {
m_data[currentJSHeapIndex].update(event.number<qint64>(0));
currentSize = m_data[currentJSHeapIndex].size;
} else {
MemoryAllocationItem allocation(event.typeIndex(), currentSize,
rangeStack.empty() ? -1 : rangeStack.top().originTypeIndex);
allocation.update(event.number<qint64>(0));
currentSize = allocation.size;
if (currentSize > m_maxSize)
m_maxSize = currentSize;
if (currentJSHeapIndex != -1)
insertEnd(currentJSHeapIndex,
event.timestamp() - startTime(currentJSHeapIndex) - 1);
currentJSHeapIndex = insertStart(event.timestamp(), type.detailType);
m_data.insert(currentJSHeapIndex, allocation);
}
if (type.detailType == HeapPage || type.detailType == LargeItem) {
if (!m_rangeStack.empty() && m_currentJSHeapIndex > -1 &&
type.detailType == selectionId(m_currentJSHeapIndex) &&
m_data[m_currentJSHeapIndex].originTypeIndex ==
m_rangeStack.top().originTypeIndex &&
m_rangeStack.top().startTime < startTime(m_currentJSHeapIndex)) {
m_data[m_currentJSHeapIndex].update(event.number<qint64>(0));
m_currentSize = m_data[m_currentJSHeapIndex].size;
} else {
MemoryAllocationItem allocation(event.typeIndex(), m_currentSize,
m_rangeStack.empty() ? -1 : m_rangeStack.top().originTypeIndex);
allocation.update(event.number<qint64>(0));
m_currentSize = allocation.size;
if (m_currentSize > m_maxSize)
m_maxSize = m_currentSize;
if (m_currentJSHeapIndex != -1)
insertEnd(m_currentJSHeapIndex,
event.timestamp() - startTime(m_currentJSHeapIndex) - 1);
m_currentJSHeapIndex = insertStart(event.timestamp(), type.detailType);
m_data.insert(m_currentJSHeapIndex, allocation);
}
}
}
if (currentJSHeapIndex != -1)
insertEnd(currentJSHeapIndex, modelManager()->traceTime()->endTime() -
startTime(currentJSHeapIndex) - 1);
if (currentUsageIndex != -1)
insertEnd(currentUsageIndex, modelManager()->traceTime()->endTime() -
startTime(currentUsageIndex) - 1);
void MemoryUsageModel::finalize()
{
if (m_currentJSHeapIndex != -1)
insertEnd(m_currentJSHeapIndex, modelManager()->traceTime()->endTime() -
startTime(m_currentJSHeapIndex) - 1);
if (m_currentUsageIndex != -1)
insertEnd(m_currentUsageIndex, modelManager()->traceTime()->endTime() -
startTime(m_currentUsageIndex) - 1);
computeNesting();
......@@ -236,6 +217,11 @@ void MemoryUsageModel::clear()
{
m_data.clear();
m_maxSize = 1;
m_currentSize = 0;
m_currentUsage = 0;
m_currentUsageIndex = -1;
m_currentJSHeapIndex = -1;
m_rangeStack.clear();
QmlProfilerTimelineModel::clear();
}
......
......@@ -30,6 +30,7 @@
#include <QStringList>
#include <QColor>
#include <QStack>
namespace QmlProfiler {
namespace Internal {
......@@ -68,14 +69,30 @@ public:
QVariantMap details(int index) const;
protected:
void loadData();
void clear();
bool accepted(const QmlEventType &type) const override;
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
private:
struct RangeStackFrame {
RangeStackFrame() : originTypeIndex(-1), startTime(-1), endTime(-1) {}
RangeStackFrame(int originTypeIndex, qint64 startTime, qint64 endTime) :
originTypeIndex(originTypeIndex), startTime(startTime), endTime(endTime) {}
int originTypeIndex;
qint64 startTime;
qint64 endTime;
};
static QString memoryTypeName(int type);
QVector<MemoryAllocationItem> m_data;
qint64 m_maxSize;
QStack<RangeStackFrame> m_rangeStack;
qint64 m_maxSize = 1;
qint64 m_currentSize = 0;
qint64 m_currentUsage = 0;
int m_currentUsageIndex = -1;
int m_currentJSHeapIndex = -1;
};
} // namespace Internal
......
......@@ -106,19 +106,23 @@ public:
QVariantMap details(int index) const;
protected:
void loadData();
void clear();
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
private:
void computeMaxCacheSize();
void resizeUnfinishedLoads();
void flattenLoads();
int updateCacheCount(int lastCacheSizeEvent, qint64 startTime, qint64 pixSize,
int updateCacheCount(int m_lastCacheSizeEvent, qint64 startTime, qint64 pixSize,
PixmapCacheItem &newEvent, int typeId);
QVector<PixmapCacheItem> m_data;
QVector<Pixmap> m_pixmaps;
qint64 m_maxCacheSize;
qint64 m_maxCacheSize = 1;
int m_lastCacheSizeEvent = -1;
int m_cumulatedCount = 0;
static const int s_pixmapCacheCountHue = 240;
};
......
......@@ -44,11 +44,11 @@ QmlProfilerAnimationsModel::QmlProfilerAnimationsModel(QmlProfilerModelManager *
QObject *parent) :
QmlProfilerTimelineModel(manager, Event, MaximumRangeType, ProfileAnimations, parent)
{
m_maxGuiThreadAnimations = m_maxRenderThreadAnimations = 0;
}
void QmlProfilerAnimationsModel::clear()
{
m_minNextStartTimes[0] = m_minNextStartTimes[1] = 0;
m_maxGuiThreadAnimations = m_maxRenderThreadAnimations = 0;
m_data.clear();
QmlProfilerTimelineModel::clear();
......@@ -59,59 +59,46 @@ bool QmlProfilerAnimationsModel::accepted(const QmlEventType &event) const
return QmlProfilerTimelineModel::accepted(event) && event.detailType == AnimationFrame;
}
void QmlProfilerAnimationsModel::loadData()
void QmlProfilerAnimationsModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
{
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
if (simpleModel->isEmpty())
return;
// collect events
const QVector<QmlEvent> &referenceList = simpleModel->events();
const QVector<QmlEventType> &typeList = simpleModel->eventTypes();
AnimationThread lastThread;
QmlPaintEventData lastEvent;
qint64 minNextStartTimes[] = {0, 0};
foreach (const QmlEvent &event, referenceList) {
const QmlEventType &type = typeList[event.typeIndex()];
if (!accepted(type))
continue;
Q_UNUSED(type);
AnimationThread lastThread = (AnimationThread)event.number<qint32>(2);
lastThread = (AnimationThread)event.number<qint32>(2);
// initial estimation of the event duration: 1/framerate
qint64 estimatedDuration = event.number<qint32>(0) > 0 ? 1e9 / event.number<qint32>(0) : 1;
// initial estimation of the event duration: 1/framerate
qint64 estimatedDuration = event.number<qint32>(0) > 0 ? 1e9 / event.number<qint32>(0) : 1;
// the profiler registers the animation events at the end of them
qint64 realEndTime = event.timestamp();
// the profiler registers the animation events at the end of them
qint64 realEndTime = event.timestamp();
// ranges should not overlap. If they do, our estimate wasn't accurate enough
qint64 realStartTime = qMax(event.timestamp() - estimatedDuration,
m_minNextStartTimes[lastThread]);
// ranges should not overlap. If they do, our estimate wasn't accurate enough
qint64 realStartTime = qMax(event.timestamp() - estimatedDuration,
minNextStartTimes[lastThread]);
// Sometimes our estimate is far off or the server has miscalculated the frame rate
if (realStartTime >= realEndTime)
realEndTime = realStartTime + 1;
// Sometimes our estimate is far off or the server has miscalculated the frame rate
if (realStartTime >= realEndTime)
realEndTime = realStartTime + 1;
// Don't "fix" the framerate even if we've fixed the duration.
// The server should know better after all and if it doesn't we want to see that.
lastEvent.typeId = event.typeIndex();
lastEvent.framerate = event.number<qint32>(0);
lastEvent.animationcount = event.number<qint32>(1);
QTC_ASSERT(lastEvent.animationcount > 0, continue);
// Don't "fix" the framerate even if we've fixed the duration.
// The server should know better after all and if it doesn't we want to see that.
QmlPaintEventData lastEvent;
lastEvent.typeId = event.typeIndex();
lastEvent.framerate = event.number<qint32>(0);
lastEvent.animationcount = event.number<qint32>(1);
QTC_ASSERT(lastEvent.animationcount > 0, return);
m_data.insert(insert(realStartTime, realEndTime - realStartTime, lastThread), lastEvent);
m_data.insert(insert(realStartTime, realEndTime - realStartTime, lastThread), lastEvent);
if (lastThread == GuiThread)
m_maxGuiThreadAnimations = qMax(lastEvent.animationcount, m_maxGuiThreadAnimations);
else
m_maxRenderThreadAnimations = qMax(lastEvent.animationcount,
m_maxRenderThreadAnimations);
if (lastThread == GuiThread)
m_maxGuiThreadAnimations = qMax(lastEvent.animationcount, m_maxGuiThreadAnimations);
else
m_maxRenderThreadAnimations = qMax(lastEvent.animationcount,
m_maxRenderThreadAnimations);
minNextStartTimes[lastThread] = event.timestamp() + 1;
}
m_minNextStartTimes[lastThread] = event.timestamp() + 1;
}
void QmlProfilerAnimationsModel::finalize()
{
computeNesting();
setExpandedRowCount((m_maxGuiThreadAnimations == 0 || m_maxRenderThreadAnimations == 0) ? 2 : 3);
setCollapsedRowCount(expandedRowCount());
......
......@@ -64,16 +64,18 @@ public:
QVariantList labels() const;
QVariantMap details(int index) const;
bool accepted(const QmlEventType &event) const;
protected:
void loadData();
void clear();
bool accepted(const QmlEventType &event) const override;
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
private:
QVector<QmlProfilerAnimationsModel::QmlPaintEventData> m_data;
int m_maxGuiThreadAnimations;
int m_maxRenderThreadAnimations;
int m_maxGuiThreadAnimations = 0;
int m_maxRenderThreadAnimations = 0;
qint64 m_minNextStartTimes[2] = {0, 0};
int rowFromThreadId(int threadId) const;
};
......
......@@ -62,25 +62,16 @@ bool QmlProfilerRangeModel::supportsBindingLoops() const
return rangeType() == Binding || rangeType() == HandlingSignal;
}
void QmlProfilerRangeModel::loadData()
void QmlProfilerRangeModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
{
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
if (simpleModel->isEmpty())
return;
// collect events
const QVector<QmlEvent> &eventList = simpleModel->events();
const QVector<QmlEventType> &typesList = simpleModel->eventTypes();
foreach (const QmlEvent &event, eventList) {
const QmlEventType &type = typesList[event.typeIndex()];
if (!accepted(type))
continue;
// store starttime-based instance
m_data.insert(insert(event.timestamp(), event.duration(), event.typeIndex()),
QmlRangeEventStartInstance());
}
Q_UNUSED(type);
// store starttime-based instance
m_data.insert(insert(event.timestamp(), event.duration(), event.typeIndex()),
QmlRangeEventStartInstance());
}
void QmlProfilerRangeModel::finalize()
{
// compute range nesting
computeNesting();
......
......@@ -73,8 +73,9 @@ public:
virtual QList<const Timeline::TimelineRenderPass *> supportedRenderPasses() const;
protected:
void loadData();
void clear();
void loadEvent(const QmlEvent &event, const QmlEventType &type) override;
void finalize() override;
void clear() override;
private:
......
......@@ -134,4 +134,21 @@ QVariantMap QmlProfilerTimelineModel::locationFromTypeId(int index) const
return result;
}
void QmlProfilerTimelineModel::loadData()
{
QmlProfilerDataModel *simpleModel = modelManager()->qmlModel();
if (simpleModel->isEmpty())
return;
const QVector<QmlEventType> &types = simpleModel->eventTypes();
foreach (const QmlEvent &event, simpleModel->events()) {