Commit 4a1e5a63 authored by Ulf Hermann's avatar Ulf Hermann

QmlProfiler: Drive event loading from the model manager

We want to get rid of the big master list of QmlEvent in
QmlProfilerDataModel, as that gets very large for longer traces. In
order to reduce the dependencies on that list we load the events on the
fly into the child models while they are being received, rather than
having the child models query QmlProfilerDataModel for the event list
later.

As the trace client so far only emitted rangedEvent() for complete
ranges we run into problems with models that need their events sorted.
The rangedEvent() signals were sorted by end time, rather than start
time which makes it inconvenient to analyze them in a stack based way,
for aggregation. This is solved by passing on all the details from the
trace client to the models, with the QmlProfilerDataModel aggregating
the type information before having the events dispatched to the child
models.

Change-Id: I5831a20551f21cf91e27d298a709f604ebd96c3e
Reviewed-by: Joerg Bornemann's avatarJoerg Bornemann <joerg.bornemann@qt.io>
Reviewed-by: Ulf Hermann's avatarUlf Hermann <ulf.hermann@qt.io>
parent dd87df7e
......@@ -63,7 +63,7 @@ FlameGraphModel::FlameGraphModel(QmlProfilerModelManager *modelManager,
void FlameGraphModel::clear()
{
beginResetModel();
m_stackBottom = FlameGraphData();
m_stackBottom = FlameGraphData(0, -1, 1);
m_callStack.clear();
m_callStack.append(QmlEvent());
m_stackTop = &m_stackBottom;
......@@ -102,15 +102,16 @@ void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
beginResetModel();
const QmlEvent *potentialParent = &(m_callStack.top());
while (potentialParent->isValid() &&
potentialParent->timestamp() + potentialParent->duration() <= event.timestamp()) {
if (event.rangeStage() == RangeEnd) {
m_stackTop->duration += event.timestamp() - potentialParent->timestamp();
m_callStack.pop();
m_stackTop = m_stackTop->parent;
potentialParent = &(m_callStack.top());
} else {
QTC_ASSERT(event.rangeStage() == RangeStart, return);
m_callStack.push(event);
m_stackTop = pushChild(m_stackTop, event);
}
m_callStack.push(event);
m_stackTop = pushChild(m_stackTop, event);
}
void FlameGraphModel::finalize()
......@@ -126,31 +127,6 @@ void FlameGraphModel::onModelManagerStateChanged()
{
if (m_modelManager->state() == QmlProfilerModelManager::ClearingData)
clear();
else if (m_modelManager->state() == QmlProfilerModelManager::ProcessingData)
loadData();
}
void FlameGraphModel::loadData(qint64 rangeStart, qint64 rangeEnd)
{
clear();
const bool checkRanges = (rangeStart != -1) && (rangeEnd != -1);
const QVector<QmlEvent> &eventList = m_modelManager->qmlModel()->events();
const QVector<QmlEventType> &typesList = m_modelManager->qmlModel()->eventTypes();
for (int i = 0; i < eventList.size(); ++i) {
const QmlEvent &event = eventList[i];
if (checkRanges) {
if ((event.timestamp() + event.duration() < rangeStart)
|| (event.timestamp() > rangeEnd))
continue;
}
loadEvent(event, typesList[event.typeIndex()]);
}
finalize();
}
static QString nameForType(RangeType typeNumber)
......@@ -223,12 +199,11 @@ FlameGraphData *FlameGraphModel::pushChild(FlameGraphData *parent, const QmlEven
foreach (FlameGraphData *child, parent->children) {
if (child->typeIndex == data.typeIndex()) {
++child->calls;
child->duration += data.duration();
return child;
}
}
FlameGraphData *child = new FlameGraphData(parent, data.typeIndex(), data.duration());
FlameGraphData *child = new FlameGraphData(parent, data.typeIndex());
parent->children.append(child);
return child;
}
......@@ -293,5 +268,10 @@ QHash<int, QByteArray> FlameGraphModel::roleNames() const
return names;
}
QmlProfilerModelManager *FlameGraphModel::modelManager() const
{
return m_modelManager;
}
} // namespace Internal
} // namespace QmlProfiler
......@@ -80,12 +80,12 @@ public:
int columnCount(const QModelIndex &parent) const override;
QVariant data(const QModelIndex &index, int role) const override;
QHash<int, QByteArray> roleNames() const override;
QmlProfilerModelManager *modelManager() const;
public slots:
void loadEvent(const QmlEvent &event, const QmlEventType &type);
void finalize();
void onModelManagerStateChanged();
void loadData(qint64 rangeStart = -1, qint64 rangeEnd = -1);
void loadNotes(int typeId, bool emitSignal);
void clear();
......
......@@ -37,7 +37,7 @@ namespace Internal {
FlameGraphView::FlameGraphView(QWidget *parent, QmlProfilerModelManager *manager) :
QmlProfilerEventsView(parent), m_content(new QQuickWidget(this)),
m_model(new FlameGraphModel(manager, this)), m_isRestrictedToRange(false)
m_model(new FlameGraphModel(manager, this))
{
setWindowTitle(QStringLiteral("Flamegraph"));
setObjectName(QStringLiteral("QmlProfilerFlamegraph"));
......@@ -66,22 +66,6 @@ FlameGraphView::FlameGraphView(QWidget *parent, QmlProfilerModelManager *manager
this, SIGNAL(gotoSourceLocation(QString,int,int)));
}
void FlameGraphView::clear()
{
m_isRestrictedToRange = false;
}
void FlameGraphView::restrictToRange(qint64 rangeStart, qint64 rangeEnd)
{
m_isRestrictedToRange = (rangeStart != -1 || rangeEnd != -1);
m_model->loadData(rangeStart, rangeEnd);
}
bool FlameGraphView::isRestrictedToRange() const
{
return m_isRestrictedToRange;
}
void FlameGraphView::selectByTypeId(int typeIndex)
{
m_content->rootObject()->setProperty("selectedTypeId", typeIndex);
......@@ -107,7 +91,7 @@ void FlameGraphView::contextMenuEvent(QContextMenuEvent *ev)
menu.addActions(QmlProfilerTool::profilerContextMenuActions());
menu.addSeparator();
getGlobalStatsAction = menu.addAction(tr("Show Full Range"));
if (!isRestrictedToRange())
if (!m_model->modelManager()->isRestrictedToRange())
getGlobalStatsAction->setEnabled(false);
if (menu.exec(position) == getGlobalStatsAction)
......
......@@ -40,10 +40,6 @@ class FlameGraphView : public QmlProfilerEventsView
public:
FlameGraphView(QWidget *parent, QmlProfilerModelManager *manager);
void clear() override;
void restrictToRange(qint64 rangeStart, qint64 rangeEnd) override;
bool isRestrictedToRange() const override;
public slots:
void selectByTypeId(int typeIndex) override;
void onVisibleFeaturesChanged(quint64 features) override;
......@@ -54,7 +50,6 @@ protected:
private:
QQuickWidget *m_content;
FlameGraphModel *m_model;
bool m_isRestrictedToRange;
};
} // namespace Internal
......
......@@ -142,12 +142,12 @@ bool MemoryUsageModel::accepted(const QmlEventType &type) const
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()));
if (event.rangeStage() == RangeStart)
m_rangeStack.push(RangeStackFrame(event.typeIndex(), event.timestamp()));
else if (event.rangeStage() == RangeEnd)
m_rangeStack.pop();
}
return;
}
......
......@@ -76,12 +76,11 @@ protected:
private:
struct RangeStackFrame {
RangeStackFrame() : originTypeIndex(-1), startTime(-1), endTime(-1) {}
RangeStackFrame(int originTypeIndex, qint64 startTime, qint64 endTime) :
originTypeIndex(originTypeIndex), startTime(startTime), endTime(endTime) {}
RangeStackFrame() : originTypeIndex(-1), startTime(-1) {}
RangeStackFrame(int originTypeIndex, qint64 startTime) :
originTypeIndex(originTypeIndex), startTime(startTime) {}
int originTypeIndex;
qint64 startTime;
qint64 endTime;
};
static QString memoryTypeName(int type);
......
......@@ -198,10 +198,6 @@ void QmlProfilerClientManager::connectClientSignals()
this, &QmlProfilerClientManager::qmlComplete);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::newEngine,
this, &QmlProfilerClientManager::qmlNewEngine);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::rangedEvent,
d->modelManager, &QmlProfilerModelManager::addQmlEvent);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::debugMessage,
d->modelManager, &QmlProfilerModelManager::addDebugMessage);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::traceFinished,
d->modelManager->traceTime(), &QmlProfilerTraceTime::increaseEndTime);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::traceStarted,
......@@ -212,6 +208,8 @@ void QmlProfilerClientManager::connectClientSignals()
d->qmlclientplugin.data(), &QmlProfilerTraceClient::setRequestedFeatures);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::recordedFeaturesChanged,
d->profilerState, &QmlProfilerStateManager::setRecordedFeatures);
connect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::qmlEvent,
d->modelManager, &QmlProfilerModelManager::addQmlEvent);
}
}
......@@ -222,10 +220,6 @@ void QmlProfilerClientManager::disconnectClientSignals()
this, &QmlProfilerClientManager::qmlComplete);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::newEngine,
this, &QmlProfilerClientManager::qmlNewEngine);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::rangedEvent,
d->modelManager, &QmlProfilerModelManager::addQmlEvent);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::debugMessage,
d->modelManager, &QmlProfilerModelManager::addDebugMessage);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::traceFinished,
d->modelManager->traceTime(), &QmlProfilerTraceTime::increaseEndTime);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::traceStarted,
......@@ -236,6 +230,8 @@ void QmlProfilerClientManager::disconnectClientSignals()
d->qmlclientplugin.data(), &QmlProfilerTraceClient::setRequestedFeatures);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::recordedFeaturesChanged,
d->profilerState, &QmlProfilerStateManager::setRecordedFeatures);
disconnect(d->qmlclientplugin.data(), &QmlProfilerTraceClient::qmlEvent,
d->modelManager, &QmlProfilerModelManager::addQmlEvent);
}
}
......
......@@ -49,22 +49,21 @@ public:
const QVector<QmlEventType> &eventTypes() const;
void setData(qint64 traceStart, qint64 traceEnd, const QVector<QmlEventType> &types,
const QVector<QmlEvent> &events);
void processData();
int count() const;
void clear();
bool isEmpty() const;
void addEvent(Message message, RangeType rangeType, int bindingType, qint64 startTime,
qint64 duration, const QString &data, const QmlEventLocation &location,
qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5);
void addEvent(const QmlEvent &event, const QmlEventType &type);
void replayEvents(qint64 startTime, qint64 endTime,
QmlProfilerModelManager::EventLoader loader) const;
void finalize();
qint64 lastTimeMark() const;
signals:
void requestReload();
void allTypesLoaded();
protected slots:
void detailsChanged(int requestId, const QString &newString);
void detailsDone();
private:
class QmlProfilerDataModelPrivate;
......
......@@ -38,10 +38,7 @@ class QMLPROFILER_EXPORT QmlProfilerEventsView : public QWidget
Q_OBJECT
public:
QmlProfilerEventsView(QWidget *parent = 0) : QWidget(parent) {}
virtual void clear() = 0;
virtual void restrictToRange(qint64 rangeStart, qint64 rangeEnd) = 0;
virtual bool isRestrictedToRange() const = 0;
virtual void clear() {}
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
......
......@@ -37,6 +37,8 @@
#include <QFile>
#include <QMessageBox>
#include <functional>
namespace QmlProfiler {
namespace Internal {
......@@ -59,22 +61,19 @@ Q_STATIC_ASSERT(sizeof(ProfileFeatureNames) == sizeof(char *) * MaximumProfileFe
/////////////////////////////////////////////////////////////////////
QmlProfilerTraceTime::QmlProfilerTraceTime(QObject *parent) :
QObject(parent), m_startTime(-1), m_endTime(-1)
{
}
QmlProfilerTraceTime::~QmlProfilerTraceTime()
QObject(parent), m_startTime(-1), m_endTime(-1),
m_restrictedStartTime(-1), m_restrictedEndTime(-1)
{
}
qint64 QmlProfilerTraceTime::startTime() const
{
return m_startTime;
return m_restrictedStartTime != -1 ? m_restrictedStartTime : m_startTime;
}
qint64 QmlProfilerTraceTime::endTime() const
{
return m_endTime;
return m_restrictedEndTime != -1 ? m_restrictedEndTime : m_endTime;
}
qint64 QmlProfilerTraceTime::duration() const
......@@ -82,18 +81,22 @@ qint64 QmlProfilerTraceTime::duration() const
return endTime() - startTime();
}
bool QmlProfilerTraceTime::isRestrictedToRange() const
{
return m_restrictedStartTime != -1 || m_restrictedEndTime != -1;
}
void QmlProfilerTraceTime::clear()
{
restrictToRange(-1, -1);
setTime(-1, -1);
}
void QmlProfilerTraceTime::setTime(qint64 startTime, qint64 endTime)
{
Q_ASSERT(startTime <= endTime);
if (startTime != m_startTime || endTime != m_endTime) {
m_startTime = startTime;
m_endTime = endTime;
}
QTC_ASSERT(startTime <= endTime, endTime = startTime);
m_startTime = startTime;
m_endTime = endTime;
}
void QmlProfilerTraceTime::decreaseStartTime(qint64 time)
......@@ -118,6 +121,13 @@ void QmlProfilerTraceTime::increaseEndTime(qint64 time)
}
}
void QmlProfilerTraceTime::restrictToRange(qint64 startTime, qint64 endTime)
{
QTC_ASSERT(endTime == -1 || startTime <= endTime, endTime = startTime);
m_restrictedStartTime = startTime;
m_restrictedEndTime = endTime;
}
} // namespace Internal
......@@ -153,6 +163,8 @@ QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *fin
d->state = Empty;
d->traceTime = new QmlProfilerTraceTime(this);
d->notesModel = new QmlProfilerNotesModel(this);
connect(d->model, &QmlProfilerDataModel::allTypesLoaded,
this, &QmlProfilerModelManager::processingDone);
}
QmlProfilerModelManager::~QmlProfilerModelManager()
......@@ -247,34 +259,17 @@ const char *QmlProfilerModelManager::featureName(ProfileFeature feature)
return ProfileFeatureNames[feature];
}
void QmlProfilerModelManager::addQmlEvent(Message message, RangeType rangeType, int detailType,
qint64 startTime, qint64 length, const QString &data,
const QmlEventLocation &location, qint64 ndata1,
qint64 ndata2, qint64 ndata3, qint64 ndata4,
qint64 ndata5)
{
// If trace start time was not explicitly set, use the first event
if (d->traceTime->startTime() == -1)
d->traceTime->setTime(startTime, startTime + d->traceTime->duration());
QTC_ASSERT(state() == AcquiringData, /**/);
d->model->addEvent(message, rangeType, detailType, startTime, length, data, location, ndata1,
ndata2, ndata3, ndata4, ndata5);
}
void QmlProfilerModelManager::addDebugMessage(qint64 timestamp, QtMsgType messageType,
const QString &text, const QmlEventLocation &location)
void QmlProfilerModelManager::addQmlEvent(const QmlEvent &event, const QmlEventType &type)
{
if (state() == AcquiringData)
d->model->addEvent(DebugMessage, MaximumRangeType, messageType, timestamp, 0, text,
location, 0, 0, 0, 0, 0);
QTC_ASSERT(state() == AcquiringData, return);
d->model->addEvent(event, type);
}
void QmlProfilerModelManager::acquiringDone()
{
QTC_ASSERT(state() == AcquiringData, /**/);
setState(ProcessingData);
d->model->processData();
d->model->finalize();
}
void QmlProfilerModelManager::processingDone()
......@@ -282,8 +277,13 @@ void QmlProfilerModelManager::processingDone()
QTC_ASSERT(state() == ProcessingData, /**/);
// Load notes after the timeline models have been initialized ...
// which happens on stateChanged(Done).
setState(Done);
foreach (const Finalizer &finalizer, d->finalizers)
finalizer();
d->notesModel->loadData();
setState(Done);
emit loadFinished();
}
......@@ -408,6 +408,26 @@ void QmlProfilerModelManager::clear()
setState(Empty);
}
void QmlProfilerModelManager::restrictToRange(qint64 startTime, qint64 endTime)
{
setState(ClearingData);
d->notesModel->saveData();
setVisibleFeatures(0);
startAcquiring();
d->model->replayEvents(startTime, endTime,
std::bind(&QmlProfilerModelManager::dispatch, this,
std::placeholders::_1, std::placeholders::_2));
d->notesModel->loadData();
d->traceTime->restrictToRange(startTime, endTime);
acquiringDone();
}
bool QmlProfilerModelManager::isRestrictedToRange() const
{
return d->traceTime->isRestrictedToRange();
}
void QmlProfilerModelManager::startAcquiring()
{
setState(AcquiringData);
......
......@@ -48,11 +48,11 @@ class QMLPROFILER_EXPORT QmlProfilerTraceTime : public QObject
Q_OBJECT
public:
explicit QmlProfilerTraceTime(QObject *parent);
~QmlProfilerTraceTime();
qint64 startTime() const;
qint64 endTime() const;
qint64 duration() const;
bool isRestrictedToRange() const;
public slots:
void clear();
......@@ -60,10 +60,14 @@ public slots:
void setTime(qint64 startTime, qint64 endTime);
void decreaseStartTime(qint64 time);
void increaseEndTime(qint64 time);
void restrictToRange(qint64 startTime, qint64 endTime);
private:
qint64 m_startTime;
qint64 m_endTime;
qint64 m_restrictedStartTime;
qint64 m_restrictedEndTime;
};
} // End internal namespace
......@@ -124,13 +128,11 @@ signals:
public slots:
void clear();
void restrictToRange(qint64 startTime, qint64 endTime);
bool isRestrictedToRange() const;
void startAcquiring();
void addQmlEvent(Message message, RangeType rangeType, int bindingType, qint64 startTime,
qint64 length, const QString &data, const QmlEventLocation &location,
qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5);
void addDebugMessage(qint64 timestamp, QtMsgType type, const QString &text,
const QmlEventLocation &location);
void addQmlEvent(const QmlEvent &event, const QmlEventType &type);
void save(const QString &filename);
void load(const QString &filename);
......
......@@ -54,6 +54,7 @@ void QmlProfilerRangeModel::clear()
m_expandedRowTypes.clear();
m_expandedRowTypes << -1;
m_data.clear();
m_stack.clear();
QmlProfilerTimelineModel::clear();
}
......@@ -66,8 +67,14 @@ void QmlProfilerRangeModel::loadEvent(const QmlEvent &event, const QmlEventType
{
Q_UNUSED(type);
// store starttime-based instance
m_data.insert(insert(event.timestamp(), event.duration(), event.typeIndex()),
QmlRangeEventStartInstance());
if (event.rangeStage() == RangeStart) {
int index = insertStart(event.timestamp(), event.typeIndex());
m_stack.append(index);
m_data.insert(index, QmlRangeEventStartInstance());
} else if (event.rangeStage() == RangeEnd) {
int index = m_stack.pop();
insertEnd(index, event.timestamp() - startTime(index));
}
}
void QmlProfilerRangeModel::finalize()
......
......@@ -33,6 +33,7 @@
#include <QVariantList>
#include <QColor>
#include <QStack>
namespace QmlProfiler {
class QmlProfilerModelManager;
......@@ -85,6 +86,7 @@ private:
void findBindingLoops();
QVector<QmlRangeEventStartInstance> m_data;
QStack<int> m_stack;
QVector<int> m_expandedRowTypes;
};
......
......@@ -68,7 +68,7 @@ public:
QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager, QObject *parent = 0);
~QmlProfilerStatisticsModel();
void setEventTypeAccepted(RangeType type, bool accepted);
void restrictToFeatures(qint64 features);
const QHash<int, QmlEventStats> &getData() const;
const QVector<QmlEventType> &getTypes() const;
......@@ -77,16 +77,15 @@ public:
int count() const;
void clear();
void limitToRange(qint64 rangeStart, qint64 rangeEnd);
void setRelativesModel(QmlProfilerStatisticsRelativesModel *childModel,
QmlProfilerStatisticsRelation relation);
QmlProfilerModelManager *modelManager() const;
signals:
void dataAvailable();
void notesAvailable(int typeIndex);
private:
void loadData(qint64 rangeStart = -1, qint64 rangeEnd = -1);
void loadEvent(const QmlEvent &event, const QmlEventType &type);
void finalize();
......@@ -133,11 +132,11 @@ protected:
QmlProfilerModelManager *m_modelManager;
// for level computation
QHash<int, qint64> m_endtimesPerLevel;
QHash<int, qint64> m_startTimesPerLevel;
int m_level = Constants::QML_MIN_LEVEL;
// compute parent-child relationship and call count
QHash<int, int> m_lastParent;
QHash<int, int> m_typesPerLevel;
const QmlProfilerStatisticsRelation m_relation;
};
......
......@@ -110,8 +110,6 @@ public:
QmlProfilerStatisticsRelativesView *m_eventParents;
QmlProfilerStatisticsModel *model;
qint64 rangeStart;
qint64 rangeEnd;
};
static void setViewDefaults(Utils::TreeView *view)
......@@ -228,8 +226,6 @@ QmlProfilerStatisticsView::QmlProfilerStatisticsView(QWidget *parent,
splitterVertical->setStretchFactor(1,2);
groupLayout->addWidget(splitterVertical);
setLayout(groupLayout);
d->rangeStart = d->rangeEnd = -1;
}
QmlProfilerStatisticsView::~QmlProfilerStatisticsView()
......@@ -243,14 +239,6 @@ void QmlProfilerStatisticsView::clear()
d->m_eventTree->clear();
d->m_eventChildren->clear();
d->m_eventParents->clear();
d->rangeStart = d->rangeEnd = -1;
}
void QmlProfilerStatisticsView::restrictToRange(qint64 rangeStart, qint64 rangeEnd)
{
d->rangeStart = rangeStart;
d->rangeEnd = rangeEnd;
d->model->limitToRange(rangeStart, rangeEnd);
}
QModelIndex QmlProfilerStatisticsView::selectedModelIndex() const
......@@ -285,7 +273,7 @@ void QmlProfilerStatisticsView::contextMenuEvent(QContextMenuEvent *ev)
menu.addSeparator();
getGlobalStatsAction = menu.addAction(tr("Show Full Range"));
if (!isRestrictedToRange())
if (!d->model->modelManager()->isRestrictedToRange())
getGlobalStatsAction->setEnabled(false);
QAction *selectedAction = menu.exec(position);
......@@ -327,18 +315,7 @@ void QmlProfilerStatisticsView::selectByTypeId(int typeIndex)
void QmlProfilerStatisticsView::onVisibleFeaturesChanged(quint64 features)
{