aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2016-12-29 15:02:06 +0100
committerUlf Hermann <ulf.hermann@qt.io>2017-02-24 11:55:53 +0000
commitc84f650fb2019df63e83316d14a7b97cadcdfa86 (patch)
tree18dcce0c54aaa4abaf0114436c4aed4049f578e7 /src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
parent78daf47a257a498cffc67cbff6820b5b93dba92e (diff)
QmlProfiler: Integrate QmlProfilerDataModel into model manager
There is no need to keep them separate as the data model is not accessed from the outside anymore. This removes a lot of indirection. Change-Id: I91da4dfa816295300c8cfcca22430d5c5b3298c0 Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp')
-rw-r--r--src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp226
1 files changed, 190 insertions, 36 deletions
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
index 4eca2d3c8a..5af506302c 100644
--- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
@@ -25,17 +25,19 @@
#include "qmlprofilermodelmanager.h"
#include "qmlprofilerconstants.h"
-#include "qmlprofilerdatamodel.h"
#include "qmlprofilertracefile.h"
#include "qmlprofilernotesmodel.h"
+#include "qmlprofilerdetailsrewriter.h"
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/runextensions.h>
#include <utils/qtcassert.h>
+#include <utils/temporaryfile.h>
#include <QDebug>
#include <QFile>
#include <QMessageBox>
+#include <QStack>
#include <functional>
@@ -136,7 +138,8 @@ void QmlProfilerTraceTime::restrictToRange(qint64 startTime, qint64 endTime)
class QmlProfilerModelManager::QmlProfilerModelManagerPrivate
{
public:
- QmlProfilerDataModel *model;
+ QmlProfilerModelManagerPrivate() : file("qmlprofiler-data") {}
+
QmlProfilerNotesModel *notesModel;
QmlProfilerTextMarkModel *textMarkModel;
@@ -155,7 +158,15 @@ public:
QHash<ProfileFeature, QVector<EventLoader> > eventLoaders;
QVector<Finalizer> finalizers;
+ QVector<QmlEventType> eventTypes;
+ QmlProfilerDetailsRewriter *detailsRewriter;
+
+ Utils::TemporaryFile file;
+ QDataStream eventStream;
+
void dispatch(const QmlEvent &event, const QmlEventType &type);
+ void rewriteType(int typeIndex);
+ int resolveStackTop();
};
@@ -169,17 +180,21 @@ QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) :
d->visibleFeatures = 0;
d->recordedFeatures = 0;
d->aggregateTraces = false;
- d->model = new QmlProfilerDataModel(this);
d->state = Empty;
d->traceTime = new QmlProfilerTraceTime(this);
d->notesModel = new QmlProfilerNotesModel(this);
d->textMarkModel = new QmlProfilerTextMarkModel(this);
- connect(d->model, &QmlProfilerDataModel::allTypesLoaded,
+
+ d->detailsRewriter = new QmlProfilerDetailsRewriter(this);
+ connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::rewriteDetailsString,
+ this, &QmlProfilerModelManager::detailsChanged);
+ connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::eventDetailsChanged,
this, &QmlProfilerModelManager::processingDone);
- connect(d->model, &QmlProfilerDataModel::traceFileError,
- this, [this]() {
- emit error(tr("Could not open a temporary file for storing QML traces."));
- });
+
+ if (d->file.open())
+ d->eventStream.setDevice(&d->file);
+ else
+ emit error(tr("Cannot open temporary trace file to store events."));
}
QmlProfilerModelManager::~QmlProfilerModelManager()
@@ -204,7 +219,7 @@ QmlProfilerTextMarkModel *QmlProfilerModelManager::textMarkModel() const
bool QmlProfilerModelManager::isEmpty() const
{
- return d->model->isEmpty();
+ return d->file.pos() == 0;
}
uint QmlProfilerModelManager::numLoadedEvents() const
@@ -214,7 +229,7 @@ uint QmlProfilerModelManager::numLoadedEvents() const
uint QmlProfilerModelManager::numLoadedEventTypes() const
{
- return d->model->eventTypes().count();
+ return d->eventTypes.count();
}
int QmlProfilerModelManager::registerModelProxy()
@@ -234,28 +249,28 @@ int QmlProfilerModelManager::numRegisteredFinalizers() const
void QmlProfilerModelManager::addEvents(const QVector<QmlEvent> &events)
{
- d->model->addEvents(events);
- const QVector<QmlEventType> &types = d->model->eventTypes();
- for (const QmlEvent &event : events)
- d->dispatch(event, types[event.typeIndex()]);
+ for (const QmlEvent &event : events) {
+ d->eventStream << event;
+ d->dispatch(event, d->eventTypes[event.typeIndex()]);
+ }
}
void QmlProfilerModelManager::addEvent(const QmlEvent &event)
{
- d->model->addEvent(event);
- d->dispatch(event, d->model->eventType(event.typeIndex()));
+ d->eventStream << event;
+ d->dispatch(event, d->eventTypes.at(event.typeIndex()));
}
void QmlProfilerModelManager::addEventTypes(const QVector<QmlEventType> &types)
{
- const int firstTypeId = d->model->eventTypes().count();
- d->model->addEventTypes(types);
- for (int i = 0, end = types.length(); i < end; ++i) {
- const QmlEventLocation &location = types[i].location();
+ const int firstTypeId = d->eventTypes.length();;
+ d->eventTypes.append(types);
+ for (int typeId = firstTypeId, end = d->eventTypes.length(); typeId < end; ++typeId) {
+ d->rewriteType(typeId);
+ const QmlEventLocation &location = d->eventTypes[typeId].location();
if (location.isValid()) {
- d->textMarkModel->addTextMarkId(
- firstTypeId + i, QmlEventLocation(
- d->model->findLocalFile(location.filename()), location.line(),
+ d->textMarkModel->addTextMarkId(typeId, QmlEventLocation(
+ findLocalFile(location.filename()), location.line(),
location.column()));
}
}
@@ -263,25 +278,97 @@ void QmlProfilerModelManager::addEventTypes(const QVector<QmlEventType> &types)
void QmlProfilerModelManager::addEventType(const QmlEventType &type)
{
- const int typeId = d->model->eventTypes().count();
- d->model->addEventType(type);
+ const int typeId = d->eventTypes.count();
+ d->eventTypes.append(type);
+ d->rewriteType(typeId);
const QmlEventLocation &location = type.location();
if (location.isValid()) {
d->textMarkModel->addTextMarkId(
- typeId, QmlEventLocation(d->model->findLocalFile(location.filename()),
+ typeId, QmlEventLocation(findLocalFile(location.filename()),
location.line(), location.column()));
}
}
const QVector<QmlEventType> &QmlProfilerModelManager::eventTypes() const
{
- return d->model->eventTypes();
+ return d->eventTypes;
}
-bool QmlProfilerModelManager::replayEvents(qint64 startTime, qint64 endTime,
+static bool isStateful(const QmlEventType &type)
+{
+ // Events of these types carry state that has to be taken into account when adding later events:
+ // PixmapCacheEvent: Total size of the cache and size of pixmap currently being loaded
+ // MemoryAllocation: Total size of the JS heap and the amount of it currently in use
+ const Message message = type.message();
+ return message == PixmapCacheEvent || message == MemoryAllocation;
+}
+
+bool QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd,
EventLoader loader) const
{
- return d->model->replayEvents(startTime, endTime, loader);
+ QStack<QmlEvent> stack;
+ QmlEvent event;
+ QFile file(d->file.fileName());
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+
+ QDataStream stream(&file);
+ bool crossedRangeStart = false;
+ while (!stream.atEnd()) {
+ stream >> event;
+ if (stream.status() == QDataStream::ReadPastEnd)
+ break;
+
+ const QmlEventType &type = d->eventTypes[event.typeIndex()];
+ if (rangeStart != -1 && rangeEnd != -1) {
+ // Double-check if rangeStart has been crossed. Some versions of Qt send dirty data.
+ if (event.timestamp() < rangeStart && !crossedRangeStart) {
+ if (type.rangeType() != MaximumRangeType) {
+ if (event.rangeStage() == RangeStart)
+ stack.push(event);
+ else if (event.rangeStage() == RangeEnd)
+ stack.pop();
+ continue;
+ } else if (isStateful(type)) {
+ event.setTimestamp(rangeStart);
+ } else {
+ continue;
+ }
+ } else {
+ if (!crossedRangeStart) {
+ foreach (QmlEvent stashed, stack) {
+ stashed.setTimestamp(rangeStart);
+ loader(stashed, d->eventTypes[stashed.typeIndex()]);
+ }
+ stack.clear();
+ crossedRangeStart = true;
+ }
+ if (event.timestamp() > rangeEnd) {
+ if (type.rangeType() != MaximumRangeType) {
+ if (event.rangeStage() == RangeEnd) {
+ if (stack.isEmpty()) {
+ QmlEvent endEvent(event);
+ endEvent.setTimestamp(rangeEnd);
+ loader(endEvent, d->eventTypes[event.typeIndex()]);
+ } else {
+ stack.pop();
+ }
+ } else if (event.rangeStage() == RangeStart) {
+ stack.push(event);
+ }
+ continue;
+ } else if (isStateful(type)) {
+ event.setTimestamp(rangeEnd);
+ } else {
+ continue;
+ }
+ }
+ }
+ }
+
+ loader(event, type);
+ }
+ return true;
}
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::dispatch(const QmlEvent &event,
@@ -292,6 +379,59 @@ void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::dispatch(const Qml
++numLoadedEvents;
}
+static QString getDisplayName(const QmlEventType &event)
+{
+ if (event.location().filename().isEmpty()) {
+ return QmlProfilerModelManager::tr("<bytecode>");
+ } else {
+ const QString filePath = QUrl(event.location().filename()).path();
+ return filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
+ QString::number(event.location().line());
+ }
+}
+
+static QString getInitialDetails(const QmlEventType &event)
+{
+ QString details = event.data();
+ // generate details string
+ if (!details.isEmpty()) {
+ details = details.replace(QLatin1Char('\n'),QLatin1Char(' ')).simplified();
+ if (details.isEmpty()) {
+ if (event.rangeType() == Javascript)
+ details = QmlProfilerModelManager::tr("anonymous function");
+ } else {
+ QRegExp rewrite(QLatin1String("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
+ bool match = rewrite.exactMatch(details);
+ if (match)
+ details = rewrite.cap(1) + QLatin1String(": ") + rewrite.cap(3);
+ if (details.startsWith(QLatin1String("file://")) ||
+ details.startsWith(QLatin1String("qrc:/")))
+ details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
+ }
+ } else if (event.rangeType() == Painting) {
+ // QtQuick1 animations always run in GUI thread.
+ details = QmlProfilerModelManager::tr("GUI Thread");
+ }
+
+ return details;
+}
+
+void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::rewriteType(int typeIndex)
+{
+ QmlEventType &type = eventTypes[typeIndex];
+ type.setDisplayName(getDisplayName(type));
+ type.setData(getInitialDetails(type));
+
+ const QmlEventLocation &location = type.location();
+ // There is no point in looking for invalid locations
+ if (!location.isValid())
+ return;
+
+ // Only bindings and signal handlers need rewriting
+ if (type.rangeType() == Binding || type.rangeType() == HandlingSignal)
+ detailsRewriter->requestDetailsForLocation(typeIndex, location);
+}
+
void QmlProfilerModelManager::announceFeatures(quint64 features, EventLoader eventLoader,
Finalizer finalizer)
{
@@ -363,7 +503,8 @@ void QmlProfilerModelManager::acquiringDone()
{
QTC_ASSERT(state() == AcquiringData, /**/);
setState(ProcessingData);
- d->model->finalize();
+ d->file.flush();
+ d->detailsRewriter->reloadDocuments();
}
void QmlProfilerModelManager::processingDone()
@@ -383,12 +524,12 @@ void QmlProfilerModelManager::processingDone()
void QmlProfilerModelManager::populateFileFinder(const ProjectExplorer::RunConfiguration *runConfiguration)
{
- d->model->populateFileFinder(runConfiguration);
+ d->detailsRewriter->populateFileFinder(runConfiguration);
}
QString QmlProfilerModelManager::findLocalFile(const QString &remoteFile)
{
- return d->model->findLocalFile(remoteFile);
+ return d->detailsRewriter->getLocalFile(remoteFile);
}
void QmlProfilerModelManager::save(const QString &filename)
@@ -534,6 +675,12 @@ void QmlProfilerModelManager::setState(QmlProfilerModelManager::State state)
emit stateChanged();
}
+void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newString)
+{
+ QTC_ASSERT(typeId < d->eventTypes.count(), return);
+ d->eventTypes[typeId].setData(newString);
+}
+
QmlProfilerModelManager::State QmlProfilerModelManager::state() const
{
return d->state;
@@ -544,7 +691,14 @@ void QmlProfilerModelManager::clear()
setState(ClearingData);
d->numLoadedEvents = 0;
d->numFinishedFinalizers = 0;
- d->model->clear();
+ d->file.remove();
+ d->eventStream.unsetDevice();
+ if (d->file.open())
+ d->eventStream.setDevice(&d->file);
+ else
+ emit error(tr("Cannot open temporary trace file to store events."));
+ d->eventTypes.clear();
+ d->detailsRewriter->clearRequests();
d->traceTime->clear();
d->notesModel->clear();
setVisibleFeatures(0);
@@ -563,9 +717,9 @@ void QmlProfilerModelManager::restrictToRange(qint64 startTime, qint64 endTime)
setVisibleFeatures(0);
startAcquiring();
- if (!d->model->replayEvents(startTime, endTime,
- std::bind(&QmlProfilerModelManagerPrivate::dispatch, d,
- std::placeholders::_1, std::placeholders::_2))) {
+ if (!replayEvents(startTime, endTime,
+ std::bind(&QmlProfilerModelManagerPrivate::dispatch, d, std::placeholders::_1,
+ std::placeholders::_2))) {
emit error(tr("Could not re-read events from temporary trace file. "
"The trace data is lost."));
clear();