QmlProfiler: Move parts of model manager and trace file to Timeline

This way we can use the trace file loading and saving mechanism for
other profilers.

Change-Id: I98ec1cdde6f7abcea152cabf72e64d4e696dfa59
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Ulf Hermann
2018-04-05 09:47:33 +02:00
parent 7099f21586
commit 1c2e0f387f
37 changed files with 1443 additions and 1001 deletions

View File

@@ -17,7 +17,9 @@ SOURCES += \
$$PWD/timelineabstractrenderer.cpp \ $$PWD/timelineabstractrenderer.cpp \
$$PWD/timelineoverviewrenderer.cpp \ $$PWD/timelineoverviewrenderer.cpp \
$$PWD/timelinetheme.cpp \ $$PWD/timelinetheme.cpp \
$$PWD/timelineformattime.cpp $$PWD/timelineformattime.cpp \
$$PWD/timelinetracefile.cpp \
$$PWD/timelinetracemanager.cpp
HEADERS += \ HEADERS += \
$$PWD/timeline_global.h \ $$PWD/timeline_global.h \
@@ -41,8 +43,11 @@ HEADERS += \
$$PWD/timelineoverviewrenderer.h \ $$PWD/timelineoverviewrenderer.h \
$$PWD/timelinetheme.h \ $$PWD/timelinetheme.h \
$$PWD/timelineformattime.h \ $$PWD/timelineformattime.h \
$$PWD/timelinetracefile.h \
$$PWD/timelinetracemanager.h \
$$PWD/traceevent.h \ $$PWD/traceevent.h \
$$PWD/traceeventtype.h $$PWD/traceeventtype.h \
$$PWD/tracestashfile.h
RESOURCES += \ RESOURCES += \
$$PWD/qml/timeline.qrc $$PWD/qml/timeline.qrc

View File

@@ -30,8 +30,10 @@ Project {
"timelinerenderstate.cpp", "timelinerenderstate.h", "timelinerenderstate_p.h", "timelinerenderstate.cpp", "timelinerenderstate.h", "timelinerenderstate_p.h",
"timelineselectionrenderpass.cpp", "timelineselectionrenderpass.h", "timelineselectionrenderpass.cpp", "timelineselectionrenderpass.h",
"timelinetheme.cpp", "timelinetheme.h", "timelinetheme.cpp", "timelinetheme.h",
"timelinetracefile.cpp", "timelinetracefile.h",
"timelinetracemanager.cpp", "timelinetracemanager.h",
"timelinezoomcontrol.cpp", "timelinezoomcontrol.h", "timelinezoomcontrol.cpp", "timelinezoomcontrol.h",
"traceevent.h", "traceeventtype.h", "traceevent.h", "traceeventtype.h", "tracestashfile.h"
] ]
} }

View File

@@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "timelinetracefile.h"
namespace Timeline {
TimelineTraceFile::TimelineTraceFile(QObject *parent) : QObject(parent)
{
}
void TimelineTraceFile::setFuture(const QFutureInterface<void> &future)
{
m_future = future;
m_future.setProgressRange(MinimumProgress, MaximumProgress);
m_future.setProgressValue(MinimumProgress);
}
QFutureInterface<void> &TimelineTraceFile::future()
{
return m_future;
}
bool TimelineTraceFile::isProgressUpdateNeeded() const
{
return m_future.isProgressUpdateNeeded() || m_future.progressValue() == 0;
}
void TimelineTraceFile::addProgressValue(int progressValue)
{
m_future.setProgressValue(qMax(static_cast<int>(MinimumProgress),
qMin(m_future.progressValue() + progressValue,
static_cast<int>(MaximumProgress))));
}
void TimelineTraceFile::setDeviceProgress(QIODevice *device)
{
m_future.setProgressValue(MinimumProgress + device->pos()
* (MaximumProgress - MinimumProgress) / device->size());
}
bool TimelineTraceFile::isCanceled() const
{
return m_future.isCanceled();
}
void TimelineTraceFile::setTraceTime(qint64 traceStart, qint64 traceEnd, qint64 measuredTime)
{
m_traceStart = traceStart;
m_traceEnd = traceEnd;
m_measuredTime = measuredTime;
}
} // namespace Timeline

View File

@@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "timeline_global.h"
#include <QFutureInterface>
#include <QObject>
QT_FORWARD_DECLARE_CLASS(QIODevice)
namespace Timeline {
class TimelineNotesModel;
class TimelineTraceManager;
class TIMELINE_EXPORT TimelineTraceFile : public QObject
{
Q_OBJECT
public:
enum ProgressValues {
MinimumProgress = 0,
MaximumProgress = 1000
};
explicit TimelineTraceFile(QObject *parent = 0);
void setTraceManager(TimelineTraceManager *traceManager) { m_traceManager = traceManager; }
TimelineTraceManager *traceManager() const { return m_traceManager; }
void setNotes(TimelineNotesModel *notes) { m_notes = notes; }
TimelineNotesModel *notes() const { return m_notes; }
void setTraceTime(qint64 startTime, qint64 endTime, qint64 measturedTime);
void setFuture(const QFutureInterface<void> &future);
QFutureInterface<void> &future();
bool isProgressUpdateNeeded() const;
void addProgressValue(int progressValue);
void setDeviceProgress(QIODevice *device);
virtual void save(QIODevice *device) = 0;
virtual void load(QIODevice *device) = 0;
void setTraceStart(qint64 traceStart) { m_traceStart = traceStart; }
qint64 traceStart() const { return m_traceStart; }
void setTraceEnd(qint64 traceEnd) { m_traceEnd = traceEnd; }
qint64 traceEnd() const { return m_traceEnd; }
qint64 measuredTime() const { return m_measuredTime; }
bool isCanceled() const;
quint64 loadedFeatures() const { return m_loadedFeatures; }
void addFeature(quint8 feature) { m_loadedFeatures |= (1ull << feature); }
signals:
void error(const QString &error);
void success();
void canceled();
private:
qint64 m_traceStart = -1;
qint64 m_traceEnd = -1;
qint64 m_measuredTime = -1;
quint64 m_loadedFeatures = 0;
QFutureInterface<void> m_future;
TimelineTraceManager *m_traceManager = nullptr;
TimelineNotesModel *m_notes = nullptr;
};
} // namespace Timeline

View File

@@ -0,0 +1,481 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "timelinetracemanager.h"
#include "timelinetracefile.h"
#include <utils/qtcassert.h>
#include <utils/temporaryfile.h>
#include <utils/runextensions.h>
#include <QFile>
#include <QDataStream>
namespace Timeline {
class TimelineTraceManager::TimelineTraceManagerPrivate
{
public:
TimelineNotesModel *notesModel = nullptr;
TimelineTraceManager::State state = Empty;
int numEvents = 0;
int numEventTypes = 0;
quint64 availableFeatures = 0;
quint64 visibleFeatures = 0;
quint64 recordedFeatures = 0;
bool aggregateTraces = false;
QHash<quint8, QVector<TraceEventLoader>> eventLoaders;
QVector<Initializer> initializers;
QVector<Finalizer> finalizers;
QVector<Clearer> clearers;
qint64 traceStart = -1;
qint64 traceEnd = -1;
qint64 restrictedTraceStart = -1;
qint64 restrictedTraceEnd = -1;
void dispatch(const TraceEvent &event, const TraceEventType &type);
void reset();
void setState(TimelineTraceManager *q, TimelineTraceManager::State state);
void updateTraceTime(qint64 time);
void restrictTraceTimeToRange(qint64 start, qint64 end);
};
TimelineTraceManager::TimelineTraceManager(QObject *parent) :
QObject(parent), d(new TimelineTraceManagerPrivate)
{
}
TimelineTraceManager::~TimelineTraceManager()
{
delete d;
}
TimelineNotesModel *TimelineTraceManager::notesModel() const
{
return d->notesModel;
}
bool TimelineTraceManager::isEmpty() const
{
return d->numEvents == 0;
}
int TimelineTraceManager::numEvents() const
{
return d->numEvents;
}
int TimelineTraceManager::numEventTypes() const
{
return d->numEventTypes;
}
void TimelineTraceManager::addEvent(const TraceEvent &event)
{
d->dispatch(event, lookupType(event.typeIndex()));
}
void TimelineTraceManager::addEventType(const TraceEventType &type)
{
const quint8 feature = type.feature();
d->recordedFeatures |= (1ull << feature);
++(d->numEventTypes);
}
void TimelineTraceManager::registerFeatures(quint64 features, TraceEventLoader eventLoader,
Initializer initializer, Finalizer finalizer,
Clearer clearer)
{
if ((features & d->availableFeatures) != features) {
d->availableFeatures |= features;
emit availableFeaturesChanged(d->availableFeatures);
}
if ((features & d->visibleFeatures) != features) {
d->visibleFeatures |= features;
emit visibleFeaturesChanged(d->visibleFeatures);
}
for (quint8 feature = 0; feature != sizeof(features) * 8; ++feature) {
if (features & (1ULL << feature)) {
if (eventLoader)
d->eventLoaders[feature].append(eventLoader);
}
}
if (initializer)
d->initializers.append(initializer);
if (finalizer)
d->finalizers.append(finalizer);
if (clearer)
d->clearers.append(clearer);
}
quint64 TimelineTraceManager::availableFeatures() const
{
return d->availableFeatures;
}
quint64 TimelineTraceManager::visibleFeatures() const
{
return d->visibleFeatures;
}
void TimelineTraceManager::setVisibleFeatures(quint64 features)
{
if (d->visibleFeatures != features) {
d->visibleFeatures = features;
emit visibleFeaturesChanged(d->visibleFeatures);
}
}
quint64 TimelineTraceManager::recordedFeatures() const
{
return d->recordedFeatures;
}
void TimelineTraceManager::setRecordedFeatures(quint64 features)
{
if (d->recordedFeatures != features) {
d->recordedFeatures = features;
emit recordedFeaturesChanged(d->recordedFeatures);
}
}
bool TimelineTraceManager::aggregateTraces() const
{
return d->aggregateTraces;
}
void TimelineTraceManager::setAggregateTraces(bool aggregateTraces)
{
d->aggregateTraces = aggregateTraces;
}
void TimelineTraceManager::initialize()
{
for (const Initializer &initializer : qAsConst(d->initializers))
initializer();
d->setState(this, AcquiringData);
}
void TimelineTraceManager::finalize()
{
QTC_CHECK(state() == AcquiringData);
// Load notes after the timeline models have been initialized ...
// which happens on stateChanged(Done).
for (const Finalizer &finalizer : qAsConst(d->finalizers))
finalizer();
d->setState(this, Done);
}
QFuture<void> TimelineTraceManager::save(const QString &filename)
{
QFile *file = new QFile(filename);
if (!file->open(QIODevice::WriteOnly)) {
delete file;
return Utils::runAsync([this, filename](QFutureInterface<void> &future) {
future.setProgressRange(0, 1);
future.setProgressValue(1);
emit error(tr("Could not open %1 for writing.").arg(filename));
emit saveFinished();
});
}
TimelineTraceFile *writer = createTraceFile();
writer->setTraceTime(traceStart(), traceEnd(), traceDuration());
writer->setTraceManager(this);
writer->setNotes(d->notesModel);
connect(writer, &QObject::destroyed, this, &TimelineTraceManager::saveFinished,
Qt::QueuedConnection);
connect(writer, &TimelineTraceFile::error, this, [this, file](const QString &message) {
file->close();
file->remove();
delete file;
emit error(message);
}, Qt::QueuedConnection);
connect(writer, &TimelineTraceFile::success, this, [file]() {
file->close();
delete file;
}, Qt::QueuedConnection);
connect(writer, &TimelineTraceFile::canceled, this, [file]() {
file->close();
file->remove();
delete file;
}, Qt::QueuedConnection);
return Utils::runAsync([file, writer] (QFutureInterface<void> &future) {
writer->setFuture(future);
writer->save(file);
writer->deleteLater();
});
}
QFuture<void> TimelineTraceManager::load(const QString &filename)
{
QFile *file = new QFile(filename, this);
if (!file->open(QIODevice::ReadOnly)) {
delete file;
return Utils::runAsync([this, filename] (QFutureInterface<void> &future) {
future.setProgressRange(0, 1);
future.setProgressValue(1);
emit error(tr("Could not open %1 for reading.").arg(filename));
emit loadFinished();
});
}
clearAll();
initialize();
TimelineTraceFile *reader = createTraceFile();
reader->setTraceManager(this);
reader->setNotes(d->notesModel);
connect(reader, &QObject::destroyed, this, &TimelineTraceManager::loadFinished,
Qt::QueuedConnection);
connect(reader, &TimelineTraceFile::success, this, [this, reader]() {
if (reader->traceStart() >= 0)
decreaseTraceStart(reader->traceStart());
if (reader->traceEnd() >= 0)
increaseTraceEnd(reader->traceEnd());
finalize();
delete reader;
}, Qt::QueuedConnection);
connect(reader, &TimelineTraceFile::error, this, [this, reader](const QString &message) {
clearAll();
delete reader;
emit error(message);
}, Qt::QueuedConnection);
connect(reader, &TimelineTraceFile::canceled, this, [this, reader]() {
clearAll();
delete reader;
}, Qt::QueuedConnection);
return Utils::runAsync([file, reader] (QFutureInterface<void> &future) {
reader->setFuture(future);
reader->load(file);
file->close();
file->deleteLater();
});
}
qint64 TimelineTraceManager::traceStart() const
{
return d->restrictedTraceStart != -1 ? d->restrictedTraceStart : d->traceStart;
}
qint64 TimelineTraceManager::traceEnd() const
{
return d->restrictedTraceEnd != -1 ? d->restrictedTraceEnd : d->traceEnd;
}
qint64 TimelineTraceManager::traceDuration() const
{
return traceEnd() - traceStart();
}
void TimelineTraceManager::decreaseTraceStart(qint64 start)
{
QTC_ASSERT(start >= 0, return);
if (d->traceStart > start || d->traceStart == -1) {
d->traceStart = start;
if (d->traceEnd == -1)
d->traceEnd = d->traceStart;
else
QTC_ASSERT(d->traceEnd >= d->traceStart, d->traceEnd = d->traceStart);
}
}
void TimelineTraceManager::increaseTraceEnd(qint64 end)
{
QTC_ASSERT(end >= 0, return);
if (d->traceEnd < end || d->traceEnd == -1) {
d->traceEnd = end;
if (d->traceStart == -1)
d->traceStart = d->traceEnd;
else
QTC_ASSERT(d->traceEnd >= d->traceStart, d->traceStart = d->traceEnd);
}
}
void TimelineTraceManager::TimelineTraceManagerPrivate::setState(
TimelineTraceManager *q, TimelineTraceManager::State newState)
{
// It's not an error, we are continuously calling "AcquiringData" for example
if (newState == state)
return;
switch (newState) {
case ClearingData:
QTC_CHECK(state == Done || state == Empty || state == AcquiringData);
break;
case Empty:
// if it's not empty, complain but go on
QTC_CHECK(q->isEmpty());
break;
case AcquiringData:
break;
case Done:
QTC_ASSERT(state == AcquiringData || state == Empty, return);
break;
default:
QTC_ASSERT(false, return);
break;
}
state = newState;
emit q->stateChanged(state);
}
void TimelineTraceManager::TimelineTraceManagerPrivate::updateTraceTime(qint64 time)
{
QTC_ASSERT(time >= 0, return);
if (traceStart > time || traceStart == -1)
traceStart = time;
if (traceEnd < time || traceEnd == -1)
traceEnd = time;
QTC_ASSERT(traceEnd >= traceStart, traceStart = traceEnd);
}
void TimelineTraceManager::TimelineTraceManagerPrivate::restrictTraceTimeToRange(qint64 start,
qint64 end)
{
QTC_ASSERT(end == -1 || start <= end, end = start);
restrictedTraceStart = start;
restrictedTraceEnd = end;
}
TimelineTraceManager::State TimelineTraceManager::state() const
{
return d->state;
}
void TimelineTraceManager::setNotesModel(TimelineNotesModel *notesModel)
{
d->notesModel = notesModel;
}
void TimelineTraceManager::clearEventStorage()
{
d->reset();
if (d->notesModel)
d->notesModel->clear();
setVisibleFeatures(0);
setRecordedFeatures(0);
}
void TimelineTraceManager::clearTypeStorage()
{
d->numEventTypes = 0;
d->recordedFeatures = 0;
}
void TimelineTraceManager::clear()
{
d->setState(this, ClearingData);
clearEventStorage();
d->setState(this, Empty);
}
void TimelineTraceManager::clearAll()
{
d->setState(this, ClearingData);
clearEventStorage();
clearTypeStorage();
d->setState(this, Empty);
}
void TimelineTraceManager::restrictToRange(qint64 startTime, qint64 endTime)
{
if (d->notesModel)
d->notesModel->stash();
d->setState(this, ClearingData);
d->reset();
setVisibleFeatures(0);
QFutureInterface<void> future;
replayEvents(startTime, endTime,
std::bind(&TimelineTraceManagerPrivate::dispatch, d, std::placeholders::_1,
std::placeholders::_2),
[this, startTime, endTime]() {
d->restrictTraceTimeToRange(startTime, endTime);
initialize();
}, [this]() {
if (d->notesModel)
d->notesModel->restore();
finalize();
}, [this](const QString &message) {
emit error(tr("Could not re-read events from temporary trace file: %1\n"
"The trace data is lost.").arg(message));
clearAll();
}, future);
}
bool TimelineTraceManager::isRestrictedToRange() const
{
return d->restrictedTraceStart != -1 || d->restrictedTraceEnd != -1;
}
void TimelineTraceManager::TimelineTraceManagerPrivate::dispatch(const TraceEvent &event,
const TraceEventType &type)
{
for (const TraceEventLoader &loader : eventLoaders[type.feature()])
loader(event, type);
if (event.timestamp() >= 0)
updateTraceTime(event.timestamp());
++numEvents;
}
void TimelineTraceManager::TimelineTraceManagerPrivate::reset()
{
restrictTraceTimeToRange(-1, -1);
traceStart = -1;
traceEnd = -1;
for (const Clearer &clearer : qAsConst(clearers))
clearer();
numEvents = 0;
}
} // namespace Timeline

View File

@@ -0,0 +1,128 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "timeline_global.h"
#include "timelinenotesmodel.h"
#include "traceevent.h"
#include "traceeventtype.h"
#include <QObject>
#include <QFuture>
#include <functional>
namespace Timeline {
class TimelineTraceFile;
class TIMELINE_EXPORT TimelineTraceManager : public QObject
{
Q_OBJECT
public:
enum State {
Empty,
AcquiringData,
ClearingData,
Done
};
typedef std::function<void(const TraceEvent &, const TraceEventType &)> TraceEventLoader;
typedef std::function<void()> Initializer;
typedef std::function<void()> Finalizer;
typedef std::function<void()> Clearer;
typedef std::function<void(const QString &)> ErrorHandler;
explicit TimelineTraceManager(QObject *parent = nullptr);
~TimelineTraceManager();
State state() const;
qint64 traceStart() const;
qint64 traceEnd() const;
qint64 traceDuration() const;
void decreaseTraceStart(qint64 start);
void increaseTraceEnd(qint64 end);
void setNotesModel(TimelineNotesModel *notesModel);
TimelineNotesModel *notesModel() const;
bool isEmpty() const;
int numEvents() const;
int numEventTypes() const;
void registerFeatures(quint64 features, TraceEventLoader eventLoader,
Initializer initializer = Initializer(),
Finalizer finalizer = Finalizer(),
Clearer clearer = Clearer());
quint64 availableFeatures() const;
quint64 visibleFeatures() const;
void setVisibleFeatures(quint64 features);
quint64 recordedFeatures() const;
void setRecordedFeatures(quint64 features);
bool aggregateTraces() const;
void setAggregateTraces(bool aggregateTraces);
virtual void initialize();
virtual void finalize();
virtual void clear();
void clearAll();
void restrictToRange(qint64 startTime, qint64 endTime);
bool isRestrictedToRange() const;
QFuture<void> save(const QString &filename);
QFuture<void> load(const QString &filename);
signals:
void error(const QString &error);
void stateChanged(State state);
void loadFinished();
void saveFinished();
void availableFeaturesChanged(quint64 features);
void visibleFeaturesChanged(quint64 features);
void recordedFeaturesChanged(quint64 features);
protected:
virtual void clearEventStorage();
virtual void clearTypeStorage();
void addEvent(const TraceEvent &event);
void addEventType(const TraceEventType &type);
virtual const TraceEventType &lookupType(int typeId) const = 0;
virtual TimelineTraceFile *createTraceFile() = 0;
virtual void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader,
Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler, QFutureInterface<void> &future) const = 0;
private:
class TimelineTraceManagerPrivate;
TimelineTraceManagerPrivate *d;
};
} // namespace Timeline

View File

@@ -0,0 +1,98 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utils/temporaryfile.h>
#include <QFile>
#include <QDataStream>
namespace Timeline {
template<typename Event>
class TraceStashFile
{
public:
TraceStashFile(const QString &pattern) : file(pattern) {}
bool open()
{
if (!file.open())
return false;
stream.setDevice(&file);
return true;
}
void append(const Event &event)
{
stream << event;
}
enum ReplayResult {
ReplaySuccess,
ReplayOpenFailed,
ReplayLoadFailed,
ReplayReadPastEnd
};
template<typename Loader>
ReplayResult replay(const Loader &loader) const
{
QFile readFile(file.fileName());
if (!readFile.open(QIODevice::ReadOnly))
return ReplayOpenFailed;
QDataStream readStream(&readFile);
Event event;
while (!readStream.atEnd()) {
readStream >> event;
if (readStream.status() == QDataStream::ReadPastEnd)
return ReplayReadPastEnd;
if (!loader(event))
return ReplayLoadFailed;
}
return ReplaySuccess;
}
void clear()
{
file.remove();
stream.unsetDevice();
}
bool flush()
{
return file.flush();
}
private:
Utils::TemporaryFile file;
QDataStream stream;
};
}

View File

@@ -111,6 +111,7 @@ void DebugMessagesModel::finalize()
{ {
setCollapsedRowCount(Constants::QML_MIN_LEVEL + 1); setCollapsedRowCount(Constants::QML_MIN_LEVEL + 1);
setExpandedRowCount(m_maximumMsgType + 2); setExpandedRowCount(m_maximumMsgType + 2);
QmlProfilerTimelineModel::finalize();
} }
void DebugMessagesModel::clear() void DebugMessagesModel::clear()

View File

@@ -51,20 +51,18 @@ FlameGraphModel::FlameGraphModel(QmlProfilerModelManager *modelManager,
m_compileStack.append(QmlEvent()); m_compileStack.append(QmlEvent());
m_callStackTop = &m_stackBottom; m_callStackTop = &m_stackBottom;
m_compileStackTop = &m_stackBottom; m_compileStackTop = &m_stackBottom;
connect(modelManager, &QmlProfilerModelManager::stateChanged,
this, &FlameGraphModel::onModelManagerStateChanged);
connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished, connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished,
this, &FlameGraphModel::onTypeDetailsFinished); this, &FlameGraphModel::onTypeDetailsFinished);
connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed, connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed,
this, [this](int typeId, int, int){loadNotes(typeId, true);}); this, [this](int typeId, int, int){loadNotes(typeId, true);});
m_acceptedFeatures = supportedFeatures(); m_acceptedFeatures = supportedFeatures();
modelManager->announceFeatures(m_acceptedFeatures, modelManager->registerFeatures(m_acceptedFeatures,
[this](const QmlEvent &event, const QmlEventType &type) { std::bind(&FlameGraphModel::loadEvent, this,
loadEvent(event, type); std::placeholders::_1, std::placeholders::_2),
}, [this](){ std::bind(&FlameGraphModel::beginResetModel, this),
finalize(); std::bind(&FlameGraphModel::finalize, this),
}); std::bind(&FlameGraphModel::clear, this));
} }
void FlameGraphModel::clear() void FlameGraphModel::clear()
@@ -108,9 +106,6 @@ void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
if (!(m_acceptedFeatures & (1ULL << type.feature()))) if (!(m_acceptedFeatures & (1ULL << type.feature())))
return; return;
if (m_stackBottom.children.isEmpty())
beginResetModel();
const bool isCompiling = (type.rangeType() == Compiling); const bool isCompiling = (type.rangeType() == Compiling);
QStack<QmlEvent> &stack = isCompiling ? m_compileStack : m_callStack; QStack<QmlEvent> &stack = isCompiling ? m_compileStack : m_callStack;
FlameGraphData *&stackTop = isCompiling ? m_compileStackTop : m_callStackTop; FlameGraphData *&stackTop = isCompiling ? m_compileStackTop : m_callStackTop;
@@ -152,12 +147,6 @@ void FlameGraphModel::finalize()
endResetModel(); endResetModel();
} }
void FlameGraphModel::onModelManagerStateChanged()
{
if (m_modelManager->state() == QmlProfilerModelManager::ClearingData)
clear();
}
void FlameGraphModel::onTypeDetailsFinished() void FlameGraphModel::onTypeDetailsFinished()
{ {
emit dataChanged(QModelIndex(), QModelIndex(), QVector<int>(1, DetailsRole)); emit dataChanged(QModelIndex(), QModelIndex(), QVector<int>(1, DetailsRole));
@@ -174,17 +163,19 @@ void FlameGraphModel::restrictToFeatures(quint64 visibleFeatures)
return; return;
clear(); clear();
beginResetModel();
if (!m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), QFutureInterface<void> future;
std::bind(&FlameGraphModel::loadEvent, m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(),
this, std::placeholders::_1, std::bind(&FlameGraphModel::loadEvent, this,
std::placeholders::_2))) { std::placeholders::_1, std::placeholders::_2),
emit m_modelManager->error(tr("Could not re-read events from temporary trace file.")); std::bind(&FlameGraphModel::beginResetModel, this),
std::bind(&FlameGraphModel::finalize, this),
[this](const QString &message) {
emit m_modelManager->error(tr("Could not re-read events from temporary trace file: %1")
.arg(message));
endResetModel(); endResetModel();
clear(); clear();
} else { }, future);
finalize();
}
} }
static QString nameForType(RangeType typeNumber) static QString nameForType(RangeType typeNumber)
@@ -207,7 +198,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
QString ret; QString ret;
if (!m_typeIdsWithNotes.contains(stats.typeIndex)) if (!m_typeIdsWithNotes.contains(stats.typeIndex))
return ret; return ret;
QmlProfilerNotesModel *notes = m_modelManager->notesModel(); Timeline::TimelineNotesModel *notes = m_modelManager->notesModel();
foreach (const QVariant &item, notes->byTypeId(stats.typeIndex)) { foreach (const QVariant &item, notes->byTypeId(stats.typeIndex)) {
if (ret.isEmpty()) if (ret.isEmpty())
ret = notes->text(item.toInt()); ret = notes->text(item.toInt());

View File

@@ -89,7 +89,6 @@ public:
void loadEvent(const QmlEvent &event, const QmlEventType &type); void loadEvent(const QmlEvent &event, const QmlEventType &type);
void finalize(); void finalize();
void onModelManagerStateChanged();
void onTypeDetailsFinished(); void onTypeDetailsFinished();
void restrictToFeatures(quint64 visibleFeatures); void restrictToFeatures(quint64 visibleFeatures);
void loadNotes(int typeId, bool emitSignal); void loadNotes(int typeId, bool emitSignal);

View File

@@ -165,6 +165,7 @@ void InputEventsModel::finalize()
{ {
setCollapsedRowCount(2); setCollapsedRowCount(2);
setExpandedRowCount(3); setExpandedRowCount(3);
QmlProfilerTimelineModel::finalize();
} }
void InputEventsModel::clear() void InputEventsModel::clear()

View File

@@ -36,8 +36,11 @@ MemoryUsageModel::MemoryUsageModel(QmlProfilerModelManager *manager,
Timeline::TimelineModelAggregator *parent) : Timeline::TimelineModelAggregator *parent) :
QmlProfilerTimelineModel(manager, MemoryAllocation, MaximumRangeType, ProfileMemory, parent) QmlProfilerTimelineModel(manager, MemoryAllocation, MaximumRangeType, ProfileMemory, parent)
{ {
// Announce additional features. The base class already announces the main feature. // Register additional features. The base class already registers the main feature.
announceFeatures(Constants::QML_JS_RANGE_FEATURES ^ (1 << ProfileCompiling)); // Don't register initializer, finalizer, or clearer as the base class has done so already.
modelManager()->registerFeatures(Constants::QML_JS_RANGE_FEATURES ^ (1 << ProfileCompiling),
std::bind(&QmlProfilerTimelineModel::loadEvent, this,
std::placeholders::_1, std::placeholders::_2));
} }
qint64 MemoryUsageModel::rowMaxValue(int rowNumber) const qint64 MemoryUsageModel::rowMaxValue(int rowNumber) const
@@ -246,6 +249,7 @@ void MemoryUsageModel::finalize()
computeNesting(); computeNesting();
setExpandedRowCount(3); setExpandedRowCount(3);
setCollapsedRowCount(3); setCollapsedRowCount(3);
QmlProfilerTimelineModel::finalize();
} }
void MemoryUsageModel::clear() void MemoryUsageModel::clear()

View File

@@ -391,6 +391,7 @@ void PixmapCacheModel::finalize()
computeMaxCacheSize(); computeMaxCacheSize();
flattenLoads(); flattenLoads();
computeNesting(); computeNesting();
QmlProfilerTimelineModel::finalize();
} }
void PixmapCacheModel::clear() void PixmapCacheModel::clear()

View File

@@ -102,6 +102,7 @@ void QmlProfilerAnimationsModel::finalize()
computeNesting(); computeNesting();
setExpandedRowCount((m_maxGuiThreadAnimations == 0 || m_maxRenderThreadAnimations == 0) ? 2 : 3); setExpandedRowCount((m_maxGuiThreadAnimations == 0 || m_maxRenderThreadAnimations == 0) ? 2 : 3);
setCollapsedRowCount(expandedRowCount()); setCollapsedRowCount(expandedRowCount());
QmlProfilerTimelineModel::finalize();
} }
int QmlProfilerAnimationsModel::rowFromThreadId(int threadId) const int QmlProfilerAnimationsModel::rowFromThreadId(int threadId) const

View File

@@ -30,9 +30,9 @@
#include "qmlprofilerdetailsrewriter.h" #include "qmlprofilerdetailsrewriter.h"
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <timeline/tracestashfile.h>
#include <utils/runextensions.h> #include <utils/runextensions.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/temporaryfile.h>
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
@@ -43,7 +43,6 @@
#include <functional> #include <functional>
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal {
static const char *ProfileFeatureNames[] = { static const char *ProfileFeatureNames[] = {
QT_TRANSLATE_NOOP("MainView", "JavaScript"), QT_TRANSLATE_NOOP("MainView", "JavaScript"),
@@ -62,65 +61,44 @@ static const char *ProfileFeatureNames[] = {
Q_STATIC_ASSERT(sizeof(ProfileFeatureNames) == sizeof(char *) * MaximumProfileFeature); Q_STATIC_ASSERT(sizeof(ProfileFeatureNames) == sizeof(char *) * MaximumProfileFeature);
} // namespace Internal
using namespace Internal;
class QmlProfilerModelManager::QmlProfilerModelManagerPrivate class QmlProfilerModelManager::QmlProfilerModelManagerPrivate
{ {
public: public:
QmlProfilerModelManagerPrivate() : file("qmlprofiler-data") {} QmlProfilerModelManagerPrivate() : file("qmlprofiler-data") {}
QmlProfilerNotesModel *notesModel = nullptr; Internal::QmlProfilerTextMarkModel *textMarkModel = nullptr;
QmlProfilerTextMarkModel *textMarkModel = nullptr;
QmlProfilerModelManager::State state = Empty;
int numFinishedFinalizers = 0;
int numLoadedEvents = 0;
quint64 availableFeatures = 0;
quint64 visibleFeatures = 0;
quint64 recordedFeatures = 0;
bool aggregateTraces = false;
QHash<ProfileFeature, QVector<EventLoader> > eventLoaders;
QVector<Finalizer> finalizers;
QVector<QmlEventType> eventTypes; QVector<QmlEventType> eventTypes;
QmlProfilerDetailsRewriter *detailsRewriter = nullptr; Internal::QmlProfilerDetailsRewriter *detailsRewriter = nullptr;
Utils::TemporaryFile file; Timeline::TraceStashFile<QmlEvent> file;
QDataStream eventStream;
qint64 traceStart = -1; void writeToStream(const QmlEvent &event);
qint64 traceEnd = -1; void addEventType(const QmlEventType &eventType);
qint64 restrictedTraceStart = -1; void handleError(const QString &message);
qint64 restrictedTraceEnd = -1;
void dispatch(const QmlEvent &event, const QmlEventType &type);
void rewriteType(int typeIndex); void rewriteType(int typeIndex);
int resolveStackTop(); int resolveStackTop();
void updateTraceTime(qint64 time);
void restrictTraceTimeToRange(qint64 start, qint64 end);
}; };
QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) : QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) :
QObject(parent), d(new QmlProfilerModelManagerPrivate) Timeline::TimelineTraceManager(parent), d(new QmlProfilerModelManagerPrivate)
{ {
d->notesModel = new QmlProfilerNotesModel(this); setNotesModel(new QmlProfilerNotesModel(this));
d->textMarkModel = new QmlProfilerTextMarkModel(this); d->textMarkModel = new Internal::QmlProfilerTextMarkModel(this);
d->detailsRewriter = new QmlProfilerDetailsRewriter(this); d->detailsRewriter = new Internal::QmlProfilerDetailsRewriter(this);
connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::rewriteDetailsString, connect(d->detailsRewriter, &Internal::QmlProfilerDetailsRewriter::rewriteDetailsString,
this, &QmlProfilerModelManager::detailsChanged); this, &QmlProfilerModelManager::typeDetailsChanged);
connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::eventDetailsChanged, connect(d->detailsRewriter, &Internal::QmlProfilerDetailsRewriter::eventDetailsChanged,
this, &QmlProfilerModelManager::typeDetailsFinished); this, &QmlProfilerModelManager::typeDetailsFinished);
if (d->file.open()) if (!d->file.open())
d->eventStream.setDevice(&d->file);
else
emit error(tr("Cannot open temporary trace file to store events.")); emit error(tr("Cannot open temporary trace file to store events."));
quint64 allFeatures = 0;
for (quint8 i = 0; i <= MaximumProfileFeature; ++i)
allFeatures |= (1ull << i);
} }
QmlProfilerModelManager::~QmlProfilerModelManager() QmlProfilerModelManager::~QmlProfilerModelManager()
@@ -128,123 +106,36 @@ QmlProfilerModelManager::~QmlProfilerModelManager()
delete d; delete d;
} }
qint64 QmlProfilerModelManager::traceStart() const Internal::QmlProfilerTextMarkModel *QmlProfilerModelManager::textMarkModel() const
{
return d->restrictedTraceStart != -1 ? d->restrictedTraceStart : d->traceStart;
}
qint64 QmlProfilerModelManager::traceEnd() const
{
return d->restrictedTraceEnd != -1 ? d->restrictedTraceEnd : d->traceEnd;
}
qint64 QmlProfilerModelManager::traceDuration() const
{
return traceEnd() - traceStart();
}
void QmlProfilerModelManager::decreaseTraceStart(qint64 start)
{
QTC_ASSERT(start >= 0, return);
if (d->traceStart > start || d->traceStart == -1) {
d->traceStart = start;
if (d->traceEnd == -1)
d->traceEnd = d->traceStart;
else
QTC_ASSERT(d->traceEnd >= d->traceStart, d->traceEnd = d->traceStart);
}
}
void QmlProfilerModelManager::increaseTraceEnd(qint64 end)
{
QTC_ASSERT(end >= 0, return);
if (d->traceEnd < end || d->traceEnd == -1) {
d->traceEnd = end;
if (d->traceStart == -1)
d->traceStart = d->traceEnd;
else
QTC_ASSERT(d->traceEnd >= d->traceStart, d->traceStart = d->traceEnd);
}
}
QmlProfilerNotesModel *QmlProfilerModelManager::notesModel() const
{
return d->notesModel;
}
QmlProfilerTextMarkModel *QmlProfilerModelManager::textMarkModel() const
{ {
return d->textMarkModel; return d->textMarkModel;
} }
bool QmlProfilerModelManager::isEmpty() const void QmlProfilerModelManager::registerFeatures(quint64 features, QmlEventLoader eventLoader,
Initializer initializer, Finalizer finalizer,
Clearer clearer)
{ {
return d->file.pos() == 0; const TraceEventLoader traceEventLoader = eventLoader ? [eventLoader](
} const Timeline::TraceEvent &event, const Timeline::TraceEventType &type) {
return eventLoader(static_cast<const QmlEvent &>(event),
static_cast<const QmlEventType &>(type));
} : TraceEventLoader();
int QmlProfilerModelManager::numEvents() const Timeline::TimelineTraceManager::registerFeatures(features, traceEventLoader, initializer,
{ finalizer, clearer);
return d->numLoadedEvents;
}
int QmlProfilerModelManager::numEventTypes() const
{
return d->eventTypes.count();
}
int QmlProfilerModelManager::numFinishedFinalizers() const
{
return d->numFinishedFinalizers;
}
int QmlProfilerModelManager::numRegisteredFinalizers() const
{
return d->finalizers.count();
} }
void QmlProfilerModelManager::addEvents(const QVector<QmlEvent> &events) void QmlProfilerModelManager::addEvents(const QVector<QmlEvent> &events)
{ {
for (const QmlEvent &event : events) { for (const QmlEvent &event : events)
d->eventStream << event; addEvent(event);
d->updateTraceTime(event.timestamp());
d->dispatch(event, d->eventTypes[event.typeIndex()]);
}
}
void QmlProfilerModelManager::addEvent(const QmlEvent &event)
{
d->eventStream << event;
d->updateTraceTime(event.timestamp());
QTC_ASSERT(event.typeIndex() < d->eventTypes.size(),
d->eventTypes.resize(event.typeIndex() + 1));
d->dispatch(event, d->eventTypes.at(event.typeIndex()));
} }
void QmlProfilerModelManager::addEventTypes(const QVector<QmlEventType> &types) void QmlProfilerModelManager::addEventTypes(const QVector<QmlEventType> &types)
{ {
const int firstTypeId = d->eventTypes.length();; for (const QmlEventType &type : types) {
d->eventTypes.append(types); d->addEventType(type);
for (int typeId = firstTypeId, end = d->eventTypes.length(); typeId < end; ++typeId) { TimelineTraceManager::addEventType(type);
d->rewriteType(typeId);
const QmlEventLocation &location = d->eventTypes[typeId].location();
if (location.isValid()) {
d->textMarkModel->addTextMarkId(typeId, QmlEventLocation(
findLocalFile(location.filename()), location.line(),
location.column()));
}
}
}
void QmlProfilerModelManager::addEventType(const QmlEventType &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(findLocalFile(location.filename()),
location.line(), location.column()));
} }
} }
@@ -253,6 +144,15 @@ const QmlEventType &QmlProfilerModelManager::eventType(int typeId) const
return d->eventTypes.at(typeId); return d->eventTypes.at(typeId);
} }
void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd,
TraceEventLoader loader, Initializer initializer,
Finalizer finalizer, ErrorHandler errorHandler,
QFutureInterface<void> &future) const
{
replayEvents(rangeStart, rangeEnd, static_cast<QmlEventLoader>(loader), initializer, finalizer,
errorHandler, future);
}
static bool isStateful(const QmlEventType &type) static bool isStateful(const QmlEventType &type)
{ {
// Events of these types carry state that has to be taken into account when adding later events: // Events of these types carry state that has to be taken into account when adding later events:
@@ -262,40 +162,46 @@ static bool isStateful(const QmlEventType &type)
return message == PixmapCacheEvent || message == MemoryAllocation; return message == PixmapCacheEvent || message == MemoryAllocation;
} }
bool QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd, void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd,
EventLoader loader) const QmlEventLoader loader, Initializer initializer,
Finalizer finalizer, ErrorHandler errorHandler,
QFutureInterface<void> &future) const
{ {
if (initializer)
initializer();
QStack<QmlEvent> stack; QStack<QmlEvent> stack;
QmlEvent event; bool crossedRangeStart = false;
QFile file(d->file.fileName());
if (!file.open(QIODevice::ReadOnly)) const auto result = d->file.replay([&](const QmlEvent &event) {
if (future.isCanceled())
return false; 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()]; const QmlEventType &type = d->eventTypes[event.typeIndex()];
if (rangeStart != -1 && rangeEnd != -1) {
// No restrictions: load all events
if (rangeStart == -1 || rangeEnd == -1) {
loader(event, type);
return true;
}
// Double-check if rangeStart has been crossed. Some versions of Qt send dirty data. // Double-check if rangeStart has been crossed. Some versions of Qt send dirty data.
qint64 adjustedTimestamp = event.timestamp();
if (event.timestamp() < rangeStart && !crossedRangeStart) { if (event.timestamp() < rangeStart && !crossedRangeStart) {
if (type.rangeType() != MaximumRangeType) { if (type.rangeType() != MaximumRangeType) {
if (event.rangeStage() == RangeStart) if (event.rangeStage() == RangeStart)
stack.push(event); stack.push(event);
else if (event.rangeStage() == RangeEnd) else if (event.rangeStage() == RangeEnd)
stack.pop(); stack.pop();
continue; return true;
} else if (isStateful(type)) { } else if (isStateful(type)) {
event.setTimestamp(rangeStart); adjustedTimestamp = rangeStart;
} else { } else {
continue; return true;
} }
} else { } else {
if (!crossedRangeStart) { if (!crossedRangeStart) {
foreach (QmlEvent stashed, stack) { for (QmlEvent stashed : stack) {
stashed.setTimestamp(rangeStart); stashed.setTimestamp(rangeStart);
loader(stashed, d->eventTypes[stashed.typeIndex()]); loader(stashed, d->eventTypes[stashed.typeIndex()]);
} }
@@ -315,29 +221,43 @@ bool QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd,
} else if (event.rangeStage() == RangeStart) { } else if (event.rangeStage() == RangeStart) {
stack.push(event); stack.push(event);
} }
continue; return true;
} else if (isStateful(type)) { } else if (isStateful(type)) {
event.setTimestamp(rangeEnd); adjustedTimestamp = rangeEnd;
} else { } else {
continue; return true;
}
} }
} }
} }
if (adjustedTimestamp != event.timestamp()) {
QmlEvent adjusted(event);
adjusted.setTimestamp(adjustedTimestamp);
loader(adjusted, type);
} else {
loader(event, type); loader(event, type);
} }
return true; return true;
} });
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::dispatch(const QmlEvent &event, switch (result) {
const QmlEventType &type) case Timeline::TraceStashFile<QmlEvent>::ReplaySuccess:
{ if (finalizer)
for (const EventLoader &loader : eventLoaders.value( finalizer();
static_cast<ProfileFeature>(type.feature()))) { break;
loader(event, type); case Timeline::TraceStashFile<QmlEvent>::ReplayOpenFailed:
if (errorHandler)
errorHandler(tr("Could not re-open temporary trace file"));
break;
case Timeline::TraceStashFile<QmlEvent>::ReplayLoadFailed:
if (errorHandler)
errorHandler(tr("Could not load events from temporary trace file"));
break;
case Timeline::TraceStashFile<QmlEvent>::ReplayReadPastEnd:
if (errorHandler)
errorHandler(tr("Read past end in temporary trace file"));
break;
} }
++numLoadedEvents;
} }
static QString getDisplayName(const QmlEventType &event) static QString getDisplayName(const QmlEventType &event)
@@ -374,6 +294,30 @@ static QString getInitialDetails(const QmlEventType &event)
return details; return details;
} }
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::writeToStream(const QmlEvent &event)
{
file.append(event);
}
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::addEventType(const QmlEventType &type)
{
const int typeId = eventTypes.count();
eventTypes.append(type);
rewriteType(typeId);
const QmlEventLocation &location = type.location();
if (location.isValid()) {
textMarkModel->addTextMarkId(typeId, QmlEventLocation(
detailsRewriter->getLocalFile(location.filename()),
location.line(), location.column()));
}
}
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::handleError(const QString &message)
{
// What to do here?
qWarning() << message;
}
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::rewriteType(int typeIndex) void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::rewriteType(int typeIndex)
{ {
QmlEventType &type = eventTypes[typeIndex]; QmlEventType &type = eventTypes[typeIndex];
@@ -390,85 +334,6 @@ void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::rewriteType(int ty
detailsRewriter->requestDetailsForLocation(typeIndex, location); detailsRewriter->requestDetailsForLocation(typeIndex, location);
} }
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::updateTraceTime(qint64 time)
{
QTC_ASSERT(time >= 0, return);
if (traceStart > time || traceStart == -1)
traceStart = time;
if (traceEnd < time || traceEnd == -1)
traceEnd = time;
QTC_ASSERT(traceEnd >= traceStart, traceStart = traceEnd);
}
void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::restrictTraceTimeToRange(qint64 start, qint64 end)
{
QTC_ASSERT(end == -1 || start <= end, end = start);
restrictedTraceStart = start;
restrictedTraceEnd = end;
}
void QmlProfilerModelManager::announceFeatures(quint64 features, EventLoader eventLoader,
Finalizer finalizer)
{
if ((features & d->availableFeatures) != features) {
d->availableFeatures |= features;
emit availableFeaturesChanged(d->availableFeatures);
}
if ((features & d->visibleFeatures) != features) {
d->visibleFeatures |= features;
emit visibleFeaturesChanged(d->visibleFeatures);
}
for (int feature = 0; feature != MaximumProfileFeature; ++feature) {
if (features & (1ULL << feature))
d->eventLoaders[static_cast<ProfileFeature>(feature)].append(eventLoader);
}
d->finalizers.append(finalizer);
}
quint64 QmlProfilerModelManager::availableFeatures() const
{
return d->availableFeatures;
}
quint64 QmlProfilerModelManager::visibleFeatures() const
{
return d->visibleFeatures;
}
void QmlProfilerModelManager::setVisibleFeatures(quint64 features)
{
if (d->visibleFeatures != features) {
d->visibleFeatures = features;
emit visibleFeaturesChanged(d->visibleFeatures);
}
}
quint64 QmlProfilerModelManager::recordedFeatures() const
{
return d->recordedFeatures;
}
void QmlProfilerModelManager::setRecordedFeatures(quint64 features)
{
if (d->recordedFeatures != features) {
d->recordedFeatures = features;
emit recordedFeaturesChanged(d->recordedFeatures);
}
}
bool QmlProfilerModelManager::aggregateTraces() const
{
return d->aggregateTraces;
}
void QmlProfilerModelManager::setAggregateTraces(bool aggregateTraces)
{
d->aggregateTraces = aggregateTraces;
}
const char *QmlProfilerModelManager::featureName(ProfileFeature feature) const char *QmlProfilerModelManager::featureName(ProfileFeature feature)
{ {
return ProfileFeatureNames[feature]; return ProfileFeatureNames[feature];
@@ -477,18 +342,14 @@ const char *QmlProfilerModelManager::featureName(ProfileFeature feature)
void QmlProfilerModelManager::finalize() void QmlProfilerModelManager::finalize()
{ {
QTC_ASSERT(state() == AcquiringData, /**/); QTC_ASSERT(state() == AcquiringData, /**/);
d->file.flush(); if (!d->file.flush())
emit error(tr("Failed to flush temporary trace file"));
d->detailsRewriter->reloadDocuments(); d->detailsRewriter->reloadDocuments();
// Load notes after the timeline models have been initialized ... // Load notes after the timeline models have been initialized ...
// which happens on stateChanged(Done). // which happens on stateChanged(Done).
foreach (const Finalizer &finalizer, d->finalizers) { TimelineTraceManager::finalize();
finalizer();
++d->numFinishedFinalizers;
}
setState(Done);
} }
void QmlProfilerModelManager::populateFileFinder(const ProjectExplorer::Target *target) void QmlProfilerModelManager::populateFileFinder(const ProjectExplorer::Target *target)
@@ -501,146 +362,6 @@ QString QmlProfilerModelManager::findLocalFile(const QString &remoteFile)
return d->detailsRewriter->getLocalFile(remoteFile); return d->detailsRewriter->getLocalFile(remoteFile);
} }
void QmlProfilerModelManager::save(const QString &filename)
{
QFile *file = new QFile(filename);
if (!file->open(QIODevice::WriteOnly)) {
emit error(tr("Could not open %1 for writing.").arg(filename));
delete file;
emit saveFinished();
return;
}
d->notesModel->stash();
QmlProfilerFileWriter *writer = new QmlProfilerFileWriter(this);
writer->setTraceTime(traceStart(), traceEnd(), traceDuration());
writer->setData(this);
writer->setNotes(d->notesModel->notes());
connect(writer, &QObject::destroyed, this, &QmlProfilerModelManager::saveFinished,
Qt::QueuedConnection);
connect(writer, &QmlProfilerFileWriter::error, this, [this, file](const QString &message) {
file->close();
file->remove();
delete file;
emit error(message);
}, Qt::QueuedConnection);
connect(writer, &QmlProfilerFileWriter::success, this, [file]() {
file->close();
delete file;
}, Qt::QueuedConnection);
connect(writer, &QmlProfilerFileWriter::canceled, this, [file]() {
file->close();
file->remove();
delete file;
}, Qt::QueuedConnection);
QFuture<void> result = Utils::runAsync([file, writer] (QFutureInterface<void> &future) {
writer->setFuture(&future);
if (file->fileName().endsWith(QLatin1String(Constants::QtdFileExtension)))
writer->saveQtd(file);
else
writer->saveQzt(file);
writer->deleteLater();
});
Core::ProgressManager::addTask(result, tr("Saving Trace Data"), Constants::TASK_SAVE,
Core::ProgressManager::ShowInApplicationIcon);
}
void QmlProfilerModelManager::load(const QString &filename)
{
bool isQtd = filename.endsWith(QLatin1String(Constants::QtdFileExtension));
QFile *file = new QFile(filename, this);
if (!file->open(isQtd ? (QIODevice::ReadOnly | QIODevice::Text) : QIODevice::ReadOnly)) {
emit error(tr("Could not open %1 for reading.").arg(filename));
delete file;
emit loadFinished();
return;
}
clear();
setState(AcquiringData);
QmlProfilerFileReader *reader = new QmlProfilerFileReader(this);
connect(reader, &QObject::destroyed, this, &QmlProfilerModelManager::loadFinished,
Qt::QueuedConnection);
connect(reader, &QmlProfilerFileReader::typesLoaded,
this, &QmlProfilerModelManager::addEventTypes);
connect(reader, &QmlProfilerFileReader::notesLoaded,
d->notesModel, &QmlProfilerNotesModel::setNotes);
connect(reader, &QmlProfilerFileReader::qmlEventsLoaded,
this, &QmlProfilerModelManager::addEvents);
connect(reader, &QmlProfilerFileReader::success, this, [this, reader]() {
if (reader->traceStart() >= 0)
decreaseTraceStart(reader->traceStart());
if (reader->traceEnd() >= 0)
increaseTraceEnd(reader->traceEnd());
setRecordedFeatures(reader->loadedFeatures());
delete reader;
finalize();
}, Qt::QueuedConnection);
connect(reader, &QmlProfilerFileReader::error, this, [this, reader](const QString &message) {
clear();
delete reader;
emit error(message);
}, Qt::QueuedConnection);
connect(reader, &QmlProfilerFileReader::canceled, this, [this, reader]() {
clear();
delete reader;
}, Qt::QueuedConnection);
QFuture<void> result = Utils::runAsync([isQtd, file, reader] (QFutureInterface<void> &future) {
reader->setFuture(&future);
if (isQtd)
reader->loadQtd(file);
else
reader->loadQzt(file);
file->close();
file->deleteLater();
});
Core::ProgressManager::addTask(result, tr("Loading Trace Data"), Constants::TASK_LOAD);
}
void QmlProfilerModelManager::setState(QmlProfilerModelManager::State state)
{
// It's not an error, we are continuously calling "AcquiringData" for example
if (d->state == state)
return;
switch (state) {
case ClearingData:
QTC_ASSERT(d->state == Done || d->state == Empty || d->state == AcquiringData, /**/);
break;
case Empty:
// if it's not empty, complain but go on
QTC_ASSERT(isEmpty(), /**/);
break;
case AcquiringData:
break;
case Done:
QTC_ASSERT(d->state == AcquiringData || d->state == Empty, return);
break;
default:
emit error(tr("Trying to set unknown state in events list."));
break;
}
d->state = state;
emit stateChanged();
}
void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newString) void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newString)
{ {
QTC_ASSERT(typeId < d->eventTypes.count(), return); QTC_ASSERT(typeId < d->eventTypes.count(), return);
@@ -648,76 +369,40 @@ void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newStrin
emit typeDetailsChanged(typeId); emit typeDetailsChanged(typeId);
} }
QmlProfilerModelManager::State QmlProfilerModelManager::state() const const Timeline::TraceEventType &QmlProfilerModelManager::lookupType(int typeIndex) const
{ {
return d->state; return eventType(typeIndex);
} }
void QmlProfilerModelManager::doClearEvents() void QmlProfilerModelManager::clearEventStorage()
{ {
d->numLoadedEvents = 0; TimelineTraceManager::clearEventStorage();
d->numFinishedFinalizers = 0; d->file.clear();
d->file.remove(); if (!d->file.open())
d->eventStream.unsetDevice(); emit error(tr("Failed to reset temporary trace file"));
if (d->file.open())
d->eventStream.setDevice(&d->file);
else
emit error(tr("Cannot open temporary trace file to store events."));
d->restrictTraceTimeToRange(-1, -1);
d->traceStart = -1;
d->traceEnd = -1;
d->notesModel->clear();
setVisibleFeatures(0);
setRecordedFeatures(0);
} }
void QmlProfilerModelManager::clearEvents() void QmlProfilerModelManager::clearTypeStorage()
{ {
setState(ClearingData); TimelineTraceManager::clearTypeStorage();
doClearEvents();
setState(Empty);
}
void QmlProfilerModelManager::clear()
{
setState(ClearingData);
doClearEvents();
d->eventTypes.clear(); d->eventTypes.clear();
d->detailsRewriter->clear();
setState(Empty);
} }
void QmlProfilerModelManager::restrictToRange(qint64 startTime, qint64 endTime) void QmlProfilerModelManager::addEventType(const QmlEventType &type)
{ {
d->notesModel->stash(); d->addEventType(type);
const QVector<QmlNote> notes = d->notesModel->notes(); TimelineTraceManager::addEventType(type);
d->notesModel->clear();
setState(ClearingData);
setVisibleFeatures(0);
startAcquiring();
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();
} else {
d->notesModel->setNotes(notes);
d->restrictTraceTimeToRange(startTime, endTime);
finalize();
}
} }
bool QmlProfilerModelManager::isRestrictedToRange() const void QmlProfilerModelManager::addEvent(const QmlEvent &event)
{ {
return d->restrictedTraceStart != -1 || d->restrictedTraceEnd != -1; d->writeToStream(event);
TimelineTraceManager::addEvent(event);
} }
void QmlProfilerModelManager::startAcquiring() Timeline::TimelineTraceFile *QmlProfilerModelManager::createTraceFile()
{ {
setState(AcquiringData); return new Internal::QmlProfilerTraceFile(this);
} }
} // namespace QmlProfiler } // namespace QmlProfiler

View File

@@ -33,6 +33,7 @@
#include "qmlprofilertextmark.h" #include "qmlprofilertextmark.h"
#include <utils/fileinprojectfinder.h> #include <utils/fileinprojectfinder.h>
#include <timeline/timelinetracemanager.h>
#include <QObject> #include <QObject>
#include <functional> #include <functional>
@@ -44,94 +45,54 @@ class QmlProfilerModelManager;
class QmlProfilerNotesModel; class QmlProfilerNotesModel;
// Interface between the Data Model and the Engine/Tool // Interface between the Data Model and the Engine/Tool
class QMLPROFILER_EXPORT QmlProfilerModelManager : public QObject class QMLPROFILER_EXPORT QmlProfilerModelManager : public Timeline::TimelineTraceManager
{ {
Q_OBJECT Q_OBJECT
public: public:
enum State { typedef std::function<void(const QmlEvent &, const QmlEventType &)> QmlEventLoader;
Empty,
AcquiringData,
ClearingData,
Done
};
typedef std::function<void(const QmlEvent &, const QmlEventType &)> EventLoader;
typedef std::function<void()> Finalizer;
explicit QmlProfilerModelManager(QObject *parent = nullptr); explicit QmlProfilerModelManager(QObject *parent = nullptr);
~QmlProfilerModelManager() override; ~QmlProfilerModelManager() override;
State state() const;
qint64 traceStart() const;
qint64 traceEnd() const;
qint64 traceDuration() const;
void decreaseTraceStart(qint64 start);
void increaseTraceEnd(qint64 end);
QmlProfilerNotesModel *notesModel() const;
Internal::QmlProfilerTextMarkModel *textMarkModel() const; Internal::QmlProfilerTextMarkModel *textMarkModel() const;
bool isEmpty() const; void registerFeatures(quint64 features, QmlEventLoader eventLoader,
int numEvents() const; Initializer initializer = nullptr, Finalizer finalizer = nullptr,
int numEventTypes() const; Clearer clearer = nullptr);
void announceFeatures(quint64 features, EventLoader eventLoader, Finalizer finalizer);
int numFinishedFinalizers() const;
int numRegisteredFinalizers() const;
void addEvents(const QVector<QmlEvent> &events); void addEvents(const QVector<QmlEvent> &events);
void addEvent(const QmlEvent &event);
void addEventTypes(const QVector<QmlEventType> &types); void addEventTypes(const QVector<QmlEventType> &types);
void addEventType(const QmlEventType &type);
const QmlEventType &eventType(int typeId) const; const QmlEventType &eventType(int typeId) const;
bool replayEvents(qint64 rangeStart, qint64 rangeEnd, EventLoader loader) const; void replayEvents(qint64 rangeStart, qint64 rangeEnd, QmlEventLoader loader,
Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler, QFutureInterface<void> &future) const;
quint64 availableFeatures() const; void finalize() override;
quint64 visibleFeatures() const;
void setVisibleFeatures(quint64 features);
quint64 recordedFeatures() const;
void setRecordedFeatures(quint64 features);
bool aggregateTraces() const;
void setAggregateTraces(bool aggregateTraces);
void finalize();
void populateFileFinder(const ProjectExplorer::Target *target = nullptr); void populateFileFinder(const ProjectExplorer::Target *target = nullptr);
QString findLocalFile(const QString &remoteFile); QString findLocalFile(const QString &remoteFile);
static const char *featureName(ProfileFeature feature); static const char *featureName(ProfileFeature feature);
void clearEvents(); void addEventType(const QmlEventType &type);
void clear(); void addEvent(const QmlEvent &event);
void restrictToRange(qint64 startTime, qint64 endTime);
bool isRestrictedToRange() const;
void startAcquiring();
void save(const QString &filename);
void load(const QString &filename);
signals: signals:
void error(const QString &error);
void stateChanged();
void loadFinished();
void saveFinished();
void availableFeaturesChanged(quint64 features);
void visibleFeaturesChanged(quint64 features);
void recordedFeaturesChanged(quint64 features);
void typeDetailsChanged(int typeId); void typeDetailsChanged(int typeId);
void typeDetailsFinished(); void typeDetailsFinished();
private: private:
void setState(State state);
void detailsChanged(int typeId, const QString &newString); void detailsChanged(int typeId, const QString &newString);
void doClearEvents();
void clearEventStorage() override;
void clearTypeStorage() override;
const Timeline::TraceEventType &lookupType(int typeId) const override;
Timeline::TimelineTraceFile *createTraceFile() override;
void replayEvents(qint64 rangeStart, qint64 rangeEnd, TraceEventLoader loader,
Initializer initializer, Finalizer finalizer,
ErrorHandler errorHandler, QFutureInterface<void> &future) const override;
class QmlProfilerModelManagerPrivate; class QmlProfilerModelManagerPrivate;
QmlProfilerModelManagerPrivate *d; QmlProfilerModelManagerPrivate *d;

View File

@@ -132,6 +132,11 @@ void QmlProfilerNotesModel::setNotes(const QVector<QmlNote> &notes)
m_notes = notes; m_notes = notes;
} }
void QmlProfilerNotesModel::addNote(const QmlNote &note)
{
m_notes.append(note);
}
void QmlProfilerNotesModel::clear() void QmlProfilerNotesModel::clear()
{ {
TimelineNotesModel::clear(); TimelineNotesModel::clear();

View File

@@ -42,6 +42,7 @@ public:
const QVector<QmlNote> &notes() const; const QVector<QmlNote> &notes() const;
void setNotes(const QVector<QmlNote> &notes); void setNotes(const QVector<QmlNote> &notes);
void addNote(const QmlNote &note);
void clear() override; void clear() override;
protected: protected:

View File

@@ -103,6 +103,8 @@ void QmlProfilerRangeModel::finalize()
if (supportsBindingLoops()) if (supportsBindingLoops())
findBindingLoops(); findBindingLoops();
QmlProfilerTimelineModel::finalize();
} }
void QmlProfilerRangeModel::computeNestingContracted() void QmlProfilerRangeModel::computeNestingContracted()

View File

@@ -66,19 +66,17 @@ double QmlProfilerStatisticsModel::durationSelfPercent(int typeId) const
QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager) QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager)
: m_modelManager(modelManager) : m_modelManager(modelManager)
{ {
connect(modelManager, &QmlProfilerModelManager::stateChanged,
this, &QmlProfilerStatisticsModel::modelManagerStateChanged);
connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed, connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed,
this, &QmlProfilerStatisticsModel::notesChanged); this, &QmlProfilerStatisticsModel::notesChanged);
m_acceptedTypes << Compiling << Creating << Binding << HandlingSignal << Javascript; m_acceptedTypes << Compiling << Creating << Binding << HandlingSignal << Javascript;
modelManager->announceFeatures(Constants::QML_JS_RANGE_FEATURES, modelManager->registerFeatures(Constants::QML_JS_RANGE_FEATURES,
[this](const QmlEvent &event, const QmlEventType &type) { std::bind(&QmlProfilerStatisticsModel::loadEvent, this,
loadEvent(event, type); std::placeholders::_1, std::placeholders::_2),
}, [this]() { std::bind(&QmlProfilerStatisticsModel::beginResetModel, this),
finalize(); std::bind(&QmlProfilerStatisticsModel::finalize, this),
}); std::bind(&QmlProfilerStatisticsModel::clear, this));
} }
void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features) void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features)
@@ -102,18 +100,20 @@ void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features)
return; return;
clear(); clear();
beginResetModel(); QFutureInterface<void> future;
if (!m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(),
std::bind(&QmlProfilerStatisticsModel::loadEvent, std::bind(&QmlProfilerStatisticsModel::loadEvent, this,
this, std::placeholders::_1, std::placeholders::_1, std::placeholders::_2),
std::placeholders::_2))) { std::bind(&QmlProfilerStatisticsModel::beginResetModel, this),
endResetModel(); [this]() {
emit m_modelManager->error(tr("Could not re-read events from temporary trace file."));
clear();
} else {
finalize(); finalize();
notesChanged(QmlProfilerStatisticsModel::s_invalidTypeId); // Reload notes notesChanged(QmlProfilerStatisticsModel::s_invalidTypeId); // Reload notes
} }, [this](const QString &message) {
endResetModel();
emit m_modelManager->error(tr("Could not re-read events from temporary trace file: %1")
.arg(message));
clear();
}, future);
} }
bool QmlProfilerStatisticsModel::isRestrictedToRange() const bool QmlProfilerStatisticsModel::isRestrictedToRange() const
@@ -381,20 +381,6 @@ QVariant QmlProfilerStatisticsModel::headerData(int section, Qt::Orientation ori
} }
} }
void QmlProfilerStatisticsModel::modelManagerStateChanged()
{
switch (m_modelManager->state()) {
case QmlProfilerModelManager::ClearingData:
clear();
break;
case QmlProfilerModelManager::AcquiringData:
beginResetModel();
break;
default:
break;
}
}
void QmlProfilerStatisticsModel::typeDetailsChanged(int typeIndex) void QmlProfilerStatisticsModel::typeDetailsChanged(int typeIndex)
{ {
const QModelIndex index = createIndex(typeIndex, MainDetails); const QModelIndex index = createIndex(typeIndex, MainDetails);
@@ -404,7 +390,7 @@ void QmlProfilerStatisticsModel::typeDetailsChanged(int typeIndex)
void QmlProfilerStatisticsModel::notesChanged(int typeIndex) void QmlProfilerStatisticsModel::notesChanged(int typeIndex)
{ {
static const QVector<int> noteRoles({Qt::ToolTipRole, Qt::TextColorRole}); static const QVector<int> noteRoles({Qt::ToolTipRole, Qt::TextColorRole});
const QmlProfilerNotesModel *notesModel = m_modelManager->notesModel(); const Timeline::TimelineNotesModel *notesModel = m_modelManager->notesModel();
if (typeIndex == s_invalidTypeId) { if (typeIndex == s_invalidTypeId) {
m_notes.clear(); m_notes.clear();
for (int noteId = 0; noteId < notesModel->count(); ++noteId) { for (int noteId = 0; noteId < notesModel->count(); ++noteId) {

View File

@@ -152,7 +152,6 @@ private:
void loadEvent(const QmlEvent &event, const QmlEventType &type); void loadEvent(const QmlEvent &event, const QmlEventType &type);
void finalize(); void finalize();
void modelManagerStateChanged();
void typeDetailsChanged(int typeIndex); void typeDetailsChanged(int typeIndex);
void notesChanged(int typeIndex); void notesChanged(int typeIndex);

View File

@@ -35,15 +35,19 @@ QmlProfilerTimelineModel::QmlProfilerTimelineModel(QmlProfilerModelManager *mode
m_modelManager(modelManager) m_modelManager(modelManager)
{ {
setDisplayName(tr(QmlProfilerModelManager::featureName(mainFeature))); setDisplayName(tr(QmlProfilerModelManager::featureName(mainFeature)));
connect(modelManager, &QmlProfilerModelManager::stateChanged,
this, &QmlProfilerTimelineModel::dataChanged);
connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished, connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished,
this, &Timeline::TimelineModel::labelsChanged); this, &Timeline::TimelineModel::labelsChanged);
connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished, connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished,
this, &Timeline::TimelineModel::detailsChanged); this, &Timeline::TimelineModel::detailsChanged);
connect(modelManager, &QmlProfilerModelManager::visibleFeaturesChanged, connect(modelManager, &QmlProfilerModelManager::visibleFeaturesChanged,
this, &QmlProfilerTimelineModel::onVisibleFeaturesChanged); this, &QmlProfilerTimelineModel::onVisibleFeaturesChanged);
announceFeatures(1ULL << m_mainFeature);
m_modelManager->registerFeatures(1ULL << m_mainFeature,
std::bind(&QmlProfilerTimelineModel::loadEvent, this,
std::placeholders::_1, std::placeholders::_2),
std::bind(&QmlProfilerTimelineModel::initialize, this),
std::bind(&QmlProfilerTimelineModel::finalize, this),
std::bind(&QmlProfilerTimelineModel::clear, this));
} }
RangeType QmlProfilerTimelineModel::rangeType() const RangeType QmlProfilerTimelineModel::rangeType() const
@@ -79,31 +83,6 @@ QmlProfilerModelManager *QmlProfilerTimelineModel::modelManager() const
return m_modelManager; return m_modelManager;
} }
void QmlProfilerTimelineModel::announceFeatures(quint64 features)
{
m_modelManager->announceFeatures(
features, [this](const QmlEvent &event, const QmlEventType &type) {
loadEvent(event, type);
}, [this]() {
finalize();
});
}
void QmlProfilerTimelineModel::dataChanged()
{
switch (m_modelManager->state()) {
case QmlProfilerModelManager::Done:
emit contentChanged();
break;
case QmlProfilerModelManager::ClearingData:
clear();
break;
default:
emit contentChanged();
break;
}
}
void QmlProfilerTimelineModel::onVisibleFeaturesChanged(quint64 features) void QmlProfilerTimelineModel::onVisibleFeaturesChanged(quint64 features)
{ {
setHidden(!(features & (1ULL << m_mainFeature))); setHidden(!(features & (1ULL << m_mainFeature)));
@@ -129,4 +108,13 @@ QVariantMap QmlProfilerTimelineModel::locationFromTypeId(int index) const
return result; return result;
} }
void QmlProfilerTimelineModel::initialize()
{
}
void QmlProfilerTimelineModel::finalize()
{
emit contentChanged();
}
} // namespace QmlProfiler } // namespace QmlProfiler

View File

@@ -55,13 +55,10 @@ public:
QVariantMap locationFromTypeId(int index) const; QVariantMap locationFromTypeId(int index) const;
virtual void loadEvent(const QmlEvent &event, const QmlEventType &type) = 0; virtual void loadEvent(const QmlEvent &event, const QmlEventType &type) = 0;
virtual void finalize() = 0; virtual void initialize();
virtual void finalize();
protected:
void announceFeatures(quint64 features);
private: private:
void dataChanged();
void onVisibleFeaturesChanged(quint64 features); void onVisibleFeaturesChanged(quint64 features);
const Message m_message; const Message m_message;

View File

@@ -65,6 +65,7 @@
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
#include <coreplugin/helpmanager.h> #include <coreplugin/helpmanager.h>
#include <coreplugin/modemanager.h> #include <coreplugin/modemanager.h>
#include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/imode.h> #include <coreplugin/imode.h>
#include <coreplugin/actionmanager/command.h> #include <coreplugin/actionmanager/command.h>
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
@@ -505,14 +506,14 @@ void QmlProfilerTool::showTimeLineSearch()
void QmlProfilerTool::clearEvents() void QmlProfilerTool::clearEvents()
{ {
d->m_profilerModelManager->clearEvents(); d->m_profilerModelManager->clear();
d->m_profilerConnections->clearEvents(); d->m_profilerConnections->clearEvents();
setRecordedFeatures(0); setRecordedFeatures(0);
} }
void QmlProfilerTool::clearData() void QmlProfilerTool::clearData()
{ {
d->m_profilerModelManager->clear(); d->m_profilerModelManager->clearAll();
d->m_profilerConnections->clearBufferedData(); d->m_profilerConnections->clearBufferedData();
setRecordedFeatures(0); setRecordedFeatures(0);
} }
@@ -666,7 +667,9 @@ void QmlProfilerTool::showSaveDialog()
filename += zFile; filename += zFile;
saveLastTraceFile(filename); saveLastTraceFile(filename);
Debugger::enableMainWindow(false); Debugger::enableMainWindow(false);
d->m_profilerModelManager->save(filename); Core::ProgressManager::addTask(d->m_profilerModelManager->save(filename),
tr("Saving Trace Data"), TASK_SAVE,
Core::ProgressManager::ShowInApplicationIcon);
} }
} }
@@ -690,7 +693,8 @@ void QmlProfilerTool::showLoadDialog()
connect(d->m_profilerModelManager, &QmlProfilerModelManager::recordedFeaturesChanged, connect(d->m_profilerModelManager, &QmlProfilerModelManager::recordedFeaturesChanged,
this, &QmlProfilerTool::setRecordedFeatures); this, &QmlProfilerTool::setRecordedFeatures);
d->m_profilerModelManager->populateFileFinder(); d->m_profilerModelManager->populateFileFinder();
d->m_profilerModelManager->load(filename); Core::ProgressManager::addTask(d->m_profilerModelManager->load(filename),
tr("Loading Trace Data"), TASK_LOAD);
} }
} }
@@ -740,7 +744,7 @@ void QmlProfilerTool::clientsDisconnected()
d->m_profilerState->currentState() == QmlProfilerStateManager::Idle) { d->m_profilerState->currentState() == QmlProfilerStateManager::Idle) {
showNonmodalWarning(tr("Application finished before loading profiled data.\n" showNonmodalWarning(tr("Application finished before loading profiled data.\n"
"Please use the stop button instead.")); "Please use the stop button instead."));
d->m_profilerModelManager->clear(); d->m_profilerModelManager->clearAll();
} }
} }
} }
@@ -912,7 +916,7 @@ void QmlProfilerTool::serverRecordingChanged()
if (!d->m_profilerModelManager->aggregateTraces() || if (!d->m_profilerModelManager->aggregateTraces() ||
d->m_profilerModelManager->state() == QmlProfilerModelManager::Done) d->m_profilerModelManager->state() == QmlProfilerModelManager::Done)
clearEvents(); clearEvents();
d->m_profilerModelManager->startAcquiring(); d->m_profilerModelManager->initialize();
} else { } else {
d->m_recordingTimer.stop(); d->m_recordingTimer.stop();
if (!d->m_profilerModelManager->aggregateTraces()) if (!d->m_profilerModelManager->aggregateTraces())

View File

@@ -24,6 +24,8 @@
****************************************************************************/ ****************************************************************************/
#include "qmlprofilertracefile.h" #include "qmlprofilertracefile.h"
#include "qmlprofilernotesmodel.h"
#include "qmlprofilerconstants.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -114,13 +116,7 @@ static QString qmlTypeAsString(Message message, RangeType rangeType)
return QString::number((int)rangeType); return QString::number((int)rangeType);
} }
QmlProfilerTraceFile::QmlProfilerTraceFile(QObject *parent) : Timeline::TimelineTraceFile(parent)
QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
QObject(parent),
m_traceStart(-1),
m_traceEnd(-1),
m_future(0),
m_loadedFeatures(0)
{ {
static int meta[] = { static int meta[] = {
qRegisterMetaType<QVector<QmlEvent> >(), qRegisterMetaType<QVector<QmlEvent> >(),
@@ -130,16 +126,25 @@ QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
Q_UNUSED(meta); Q_UNUSED(meta);
} }
void QmlProfilerFileReader::setFuture(QFutureInterface<void> *future) void QmlProfilerTraceFile::load(QIODevice *device)
{ {
m_future = future; const QFile *file = qobject_cast<QFile *>(device);
if (m_future) { if (file && file->fileName().endsWith(Constants::QtdFileExtension))
m_future->setProgressRange(0, 1000); loadQtd(device);
m_future->setProgressValue(0); else
} loadQzt(device);
} }
void QmlProfilerFileReader::loadQtd(QIODevice *device) void QmlProfilerTraceFile::save(QIODevice *device)
{
const QFile *file = qobject_cast<QFile *>(device);
if (file && file->fileName().endsWith(Constants::QtdFileExtension))
saveQtd(device);
else
saveQzt(device);
}
void QmlProfilerTraceFile::loadQtd(QIODevice *device)
{ {
QXmlStreamReader stream(device); QXmlStreamReader stream(device);
@@ -158,14 +163,13 @@ void QmlProfilerFileReader::loadQtd(QIODevice *device)
else else
validVersion = false; validVersion = false;
if (attributes.hasAttribute(_("traceStart"))) if (attributes.hasAttribute(_("traceStart")))
m_traceStart = attributes.value(_("traceStart")).toLongLong(); setTraceStart(attributes.value(_("traceStart")).toLongLong());
if (attributes.hasAttribute(_("traceEnd"))) if (attributes.hasAttribute(_("traceEnd")))
m_traceEnd = attributes.value(_("traceEnd")).toLongLong(); setTraceEnd(attributes.value(_("traceEnd")).toLongLong());
} }
if (elementName == _("eventData")) { if (elementName == _("eventData")) {
loadEventTypes(stream); loadEventTypes(stream);
emit typesLoaded(m_eventTypes);
break; break;
} }
@@ -176,7 +180,6 @@ void QmlProfilerFileReader::loadQtd(QIODevice *device)
if (elementName == _("noteData")) { if (elementName == _("noteData")) {
loadNotes(stream); loadNotes(stream);
emit notesLoaded(m_notes);
break; break;
} }
@@ -194,7 +197,7 @@ void QmlProfilerFileReader::loadQtd(QIODevice *device)
emit success(); emit success();
} }
void QmlProfilerFileReader::loadQzt(QIODevice *device) void QmlProfilerTraceFile::loadQzt(QIODevice *device)
{ {
QDataStream stream(device); QDataStream stream(device);
stream.setVersion(QDataStream::Qt_5_5); stream.setVersion(QDataStream::Qt_5_5);
@@ -215,44 +218,48 @@ void QmlProfilerFileReader::loadQzt(QIODevice *device)
} }
stream.setVersion(dataStreamVersion); stream.setVersion(dataStreamVersion);
stream >> m_traceStart >> m_traceEnd; qint64 traceStart, traceEnd;
stream >> traceStart >> traceEnd;
setTraceStart(traceStart);
setTraceEnd(traceEnd);
QBuffer buffer; QBuffer buffer;
QDataStream bufferStream(&buffer); QDataStream bufferStream(&buffer);
bufferStream.setVersion(dataStreamVersion); bufferStream.setVersion(dataStreamVersion);
QByteArray data; QByteArray data;
updateProgress(device); setDeviceProgress(device);
if (!isCanceled()) { if (!isCanceled()) {
stream >> data; stream >> data;
buffer.setData(qUncompress(data)); buffer.setData(qUncompress(data));
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);
QVector<QmlEventType> eventTypes;
quint32 numEventTypes; quint32 numEventTypes;
bufferStream >> numEventTypes; bufferStream >> numEventTypes;
if (numEventTypes > std::numeric_limits<int>::max()) { if (numEventTypes > std::numeric_limits<int>::max()) {
emit error(tr("Excessive number of event types: %1").arg(numEventTypes)); emit error(tr("Excessive number of event types: %1").arg(numEventTypes));
return; return;
} }
QTC_ASSERT(m_eventTypes.isEmpty(), m_eventTypes.clear()); eventTypes.reserve(static_cast<int>(numEventTypes));
m_eventTypes.reserve(static_cast<int>(numEventTypes));
QmlEventType type; QmlEventType type;
for (int typeId = 0; typeId < static_cast<int>(numEventTypes); ++typeId) { for (int typeId = 0; typeId < static_cast<int>(numEventTypes); ++typeId) {
bufferStream >> type; bufferStream >> type;
m_eventTypes.append(type); eventTypes.append(type);
} }
buffer.close(); buffer.close();
emit typesLoaded(m_eventTypes); modelManager()->addEventTypes(eventTypes);
updateProgress(device); setDeviceProgress(device);
} }
if (!isCanceled()) { if (!isCanceled()) {
stream >> data; stream >> data;
buffer.setData(qUncompress(data)); buffer.setData(qUncompress(data));
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);
bufferStream >> m_notes; QVector<QmlNote> notes;
bufferStream >> notes;
buffer.close(); buffer.close();
emit notesLoaded(m_notes); qmlNotes()->setNotes(notes);
updateProgress(device); setDeviceProgress(device);
} }
QVector<QmlEvent> eventBuffer; QVector<QmlEvent> eventBuffer;
@@ -264,11 +271,11 @@ void QmlProfilerFileReader::loadQzt(QIODevice *device)
QmlEvent event; QmlEvent event;
bufferStream >> event; bufferStream >> event;
if (bufferStream.status() == QDataStream::Ok) { if (bufferStream.status() == QDataStream::Ok) {
if (event.typeIndex() >= m_eventTypes.length()) { if (event.typeIndex() >= traceManager()->numEventTypes()) {
emit error(tr("Invalid type index %1").arg(event.typeIndex())); emit error(tr("Invalid type index %1").arg(event.typeIndex()));
return; return;
} }
m_loadedFeatures |= (1ULL << m_eventTypes[event.typeIndex()].feature()); addFeature(modelManager()->eventType(event.typeIndex()).feature());
if (event.timestamp() < 0) if (event.timestamp() < 0)
event.setTimestamp(0); event.setTimestamp(0);
} else if (bufferStream.status() == QDataStream::ReadPastEnd) { } else if (bufferStream.status() == QDataStream::ReadPastEnd) {
@@ -281,28 +288,35 @@ void QmlProfilerFileReader::loadQzt(QIODevice *device)
} }
eventBuffer.append(event); eventBuffer.append(event);
} }
emit qmlEventsLoaded(eventBuffer); modelManager()->addEvents(eventBuffer);
eventBuffer.clear(); eventBuffer.clear();
buffer.close(); buffer.close();
updateProgress(device); setDeviceProgress(device);
} }
if (isCanceled()) { if (isCanceled()) {
emit canceled(); emit canceled();
} else { } else {
emit qmlEventsLoaded(eventBuffer); modelManager()->addEvents(eventBuffer);
emit success(); emit success();
} }
} }
quint64 QmlProfilerFileReader::loadedFeatures() const void QmlProfilerTraceFile::addEventsProgress(qint64 timestamp)
{ {
return m_loadedFeatures; addProgressValue(static_cast<float>(timestamp) / static_cast<float>(traceEnd() - traceStart())
* ProgressEvents);
} }
void QmlProfilerFileReader::loadEventTypes(QXmlStreamReader &stream) void QmlProfilerTraceFile::addStageProgress(QmlProfilerTraceFile::ProgressValues stage)
{
addProgressValue(stage);
}
void QmlProfilerTraceFile::loadEventTypes(QXmlStreamReader &stream)
{ {
QTC_ASSERT(stream.name() == _("eventData"), return); QTC_ASSERT(stream.name() == _("eventData"), return);
QVector<QmlEventType> eventTypes;
int typeIndex = -1; int typeIndex = -1;
@@ -330,7 +344,7 @@ void QmlProfilerFileReader::loadEventTypes(QXmlStreamReader &stream)
switch (token) { switch (token) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
if (elementName == _("event")) { if (elementName == _("event")) {
updateProgress(stream.device()); setDeviceProgress(stream.device());
clearType(); clearType();
const QXmlStreamAttributes attributes = stream.attributes(); const QXmlStreamAttributes attributes = stream.attributes();
@@ -407,20 +421,21 @@ void QmlProfilerFileReader::loadEventTypes(QXmlStreamReader &stream)
case QXmlStreamReader::EndElement: { case QXmlStreamReader::EndElement: {
if (elementName == _("event")) { if (elementName == _("event")) {
if (typeIndex >= 0) { if (typeIndex >= 0) {
if (typeIndex >= m_eventTypes.size()) if (typeIndex >= eventTypes.length())
m_eventTypes.resize(typeIndex + 1); eventTypes.resize(typeIndex + 1);
QmlEventType type(messageAndRange.first, messageAndRange.second, detailType, QmlEventType type(messageAndRange.first, messageAndRange.second, detailType,
QmlEventLocation(filename, line, column), data, displayName); QmlEventLocation(filename, line, column), data, displayName);
m_eventTypes[typeIndex] = type; eventTypes[typeIndex] = type;
quint8 feature = type.feature(); const quint8 feature = type.feature();
if (feature != MaximumProfileFeature) if (feature != MaximumProfileFeature)
m_loadedFeatures |= (1ULL << static_cast<uint>(feature)); addFeature(feature);
} }
break; break;
} }
if (elementName == _("eventData")) { if (elementName == _("eventData")) {
// done reading eventData // done reading eventData
modelManager()->addEventTypes(eventTypes);
return; return;
} }
break; break;
@@ -497,7 +512,7 @@ QVector<QmlEvent> EventList::finalize()
return result; return result;
} }
void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream) void QmlProfilerTraceFile::loadEvents(QXmlStreamReader &stream)
{ {
QTC_ASSERT(stream.name() == _("profilerDataModel"), return); QTC_ASSERT(stream.name() == _("profilerDataModel"), return);
EventList events; EventList events;
@@ -510,7 +525,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream)
switch (token) { switch (token) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
if (elementName == _("range")) { if (elementName == _("range")) {
updateProgress(stream.device()); setDeviceProgress(stream.device());
QmlEvent event; QmlEvent event;
const QXmlStreamAttributes attributes = stream.attributes(); const QXmlStreamAttributes attributes = stream.attributes();
@@ -574,7 +589,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream)
case QXmlStreamReader::EndElement: { case QXmlStreamReader::EndElement: {
if (elementName == _("profilerDataModel")) { if (elementName == _("profilerDataModel")) {
// done reading profilerDataModel // done reading profilerDataModel
emit qmlEventsLoaded(events.finalize()); modelManager()->addEvents(events.finalize());
return; return;
} }
break; break;
@@ -584,7 +599,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream)
} }
} }
void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream) void QmlProfilerTraceFile::loadNotes(QXmlStreamReader &stream)
{ {
QmlNote currentNote; QmlNote currentNote;
while (!stream.atEnd() && !stream.hasError() && !isCanceled()) { while (!stream.atEnd() && !stream.hasError() && !isCanceled()) {
@@ -595,7 +610,7 @@ void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream)
switch (token) { switch (token) {
case QXmlStreamReader::StartElement: { case QXmlStreamReader::StartElement: {
if (elementName == _("note")) { if (elementName == _("note")) {
updateProgress(stream.device()); setDeviceProgress(stream.device());
QXmlStreamAttributes attrs = stream.attributes(); QXmlStreamAttributes attrs = stream.attributes();
int collapsedRow = attrs.hasAttribute(_("collapsedRow")) ? int collapsedRow = attrs.hasAttribute(_("collapsedRow")) ?
attrs.value(_("collapsedRow")).toInt() : -1; attrs.value(_("collapsedRow")).toInt() : -1;
@@ -613,7 +628,7 @@ void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream)
} }
case QXmlStreamReader::EndElement: { case QXmlStreamReader::EndElement: {
if (elementName == _("note")) { if (elementName == _("note")) {
m_notes.append(currentNote); qmlNotes()->addNote(currentNote);
} else if (elementName == _("noteData")) { } else if (elementName == _("noteData")) {
return; return;
} }
@@ -625,55 +640,7 @@ void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream)
} }
} }
void QmlProfilerFileReader::updateProgress(QIODevice *device) void QmlProfilerTraceFile::saveQtd(QIODevice *device)
{
if (!m_future)
return;
m_future->setProgressValue(device->pos() * 1000 / device->size());
}
bool QmlProfilerFileReader::isCanceled() const
{
return m_future && m_future->isCanceled();
}
QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
QObject(parent),
m_startTime(0),
m_endTime(0),
m_measuredTime(0),
m_future(0)
{
}
void QmlProfilerFileWriter::setTraceTime(qint64 startTime, qint64 endTime, qint64 measuredTime)
{
m_startTime = startTime;
m_endTime = endTime;
m_measuredTime = measuredTime;
}
void QmlProfilerFileWriter::setData(const QmlProfilerModelManager *model)
{
m_modelManager = model;
}
void QmlProfilerFileWriter::setNotes(const QVector<QmlNote> &notes)
{
m_notes = notes;
}
void QmlProfilerFileWriter::setFuture(QFutureInterface<void> *future)
{
m_future = future;
if (m_future) {
m_future->setProgressRange(0, ProgressTotal);
m_future->setProgressValue(0);
}
}
void QmlProfilerFileWriter::saveQtd(QIODevice *device)
{ {
QXmlStreamWriter stream(device); QXmlStreamWriter stream(device);
@@ -683,15 +650,16 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device)
stream.writeStartElement(_("trace")); stream.writeStartElement(_("trace"));
stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION)); stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION));
stream.writeAttribute(_("traceStart"), QString::number(m_startTime)); stream.writeAttribute(_("traceStart"), QString::number(traceStart()));
stream.writeAttribute(_("traceEnd"), QString::number(m_endTime)); stream.writeAttribute(_("traceEnd"), QString::number(traceEnd()));
stream.writeStartElement(_("eventData")); stream.writeStartElement(_("eventData"));
stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime)); stream.writeAttribute(_("totalTime"), QString::number(measuredTime()));
for (int typeIndex = 0, end = m_modelManager->numEventTypes(); const QmlProfilerModelManager *manager = modelManager();
typeIndex < end && !isCanceled(); ++typeIndex) { for (int typeIndex = 0, end = manager->numEventTypes(); typeIndex < end && !isCanceled();
++typeIndex) {
const QmlEventType &type = m_modelManager->eventType(typeIndex); const QmlEventType &type = manager->eventType(typeIndex);
stream.writeStartElement(_("event")); stream.writeStartElement(_("event"));
stream.writeAttribute(_("index"), QString::number(typeIndex)); stream.writeAttribute(_("index"), QString::number(typeIndex));
@@ -734,19 +702,17 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device)
} }
stream.writeEndElement(); stream.writeEndElement();
} }
updateProgress(ProgressTypes); addStageProgress(ProgressTypes);
stream.writeEndElement(); // eventData stream.writeEndElement(); // eventData
if (!isCanceled()) { if (isCanceled()) {
stream.writeStartElement(_("profilerDataModel")); emit canceled();
return;
}
QStack<QmlEvent> stack; QStack<QmlEvent> stack;
const bool success = m_modelManager->replayEvents( qint64 lastProgressTimestamp = traceStart();
-1, -1, [this, &stack, &stream](const QmlEvent &event, modelManager()->replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
const QmlEventType &type) {
if (isCanceled())
return;
if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) { if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) {
stack.push(event); stack.push(event);
return; return;
@@ -811,23 +777,19 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device)
stream.writeEndElement(); stream.writeEndElement();
// Update the progress roughly every 4k events. It doesn't have to be precise. if (isProgressUpdateNeeded()) {
if ((event.timestamp() & 0xfff) == 0) addEventsProgress(event.timestamp() - lastProgressTimestamp);
updateProgress(event.timestamp()); lastProgressTimestamp = event.timestamp();
});
if (!success) {
emit error(tr("Could not re-read events from temporary trace file. Saving failed."));
return;
} }
}, [&stream](){
stream.writeStartElement(_("profilerDataModel"));
}, [this, &stream]() {
stream.writeEndElement(); // profilerDataModel stream.writeEndElement(); // profilerDataModel
}
if (!isCanceled()) { if (!isCanceled()) {
stream.writeStartElement(_("noteData")); stream.writeStartElement(_("noteData"));
for (int noteIndex = 0; noteIndex < m_notes.size() && !isCanceled(); ++noteIndex) { const QVector<QmlNote> &notes = qmlNotes()->notes();
for (int noteIndex = 0; noteIndex < notes.length() && !isCanceled(); ++noteIndex) {
const QmlNote &note = m_notes[noteIndex]; const QmlNote &note = notes[noteIndex];
stream.writeStartElement(_("note")); stream.writeStartElement(_("note"));
stream.writeAttribute(_("startTime"), QString::number(note.startTime())); stream.writeAttribute(_("startTime"), QString::number(note.startTime()));
stream.writeAttribute(_("duration"), QString::number(note.duration())); stream.writeAttribute(_("duration"), QString::number(note.duration()));
@@ -837,60 +799,66 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device)
stream.writeEndElement(); // note stream.writeEndElement(); // note
} }
stream.writeEndElement(); // noteData stream.writeEndElement(); // noteData
updateProgress(ProgressNotes); addStageProgress(ProgressNotes);
} }
stream.writeEndElement(); // trace stream.writeEndElement(); // trace
stream.writeEndDocument(); stream.writeEndDocument();
if (isCanceled()) { if (isCanceled())
emit canceled(); emit canceled();
} else if (stream.hasError()) { else if (stream.hasError())
emit error(tr("Error writing trace file.")); emit error(tr("Error writing trace file."));
} else { else
emit success(); emit success();
} }, [this](const QString &message) {
emit error(tr("Could not re-read events from temporary trace file: %s\nSaving failed.")
.arg(message));
}, future());
} }
void QmlProfilerFileWriter::saveQzt(QFile *file) void QmlProfilerTraceFile::saveQzt(QIODevice *device)
{ {
QDataStream stream(file); QDataStream stream(device);
stream.setVersion(QDataStream::Qt_5_5); stream.setVersion(QDataStream::Qt_5_5);
stream << QByteArray("QMLPROFILER"); stream << QByteArray("QMLPROFILER");
stream << static_cast<qint32>(QDataStream::Qt_DefaultCompiledVersion); stream << static_cast<qint32>(QDataStream::Qt_DefaultCompiledVersion);
stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
stream << m_startTime << m_endTime; stream << traceStart() << traceEnd();
QBuffer buffer; QBuffer buffer;
QDataStream bufferStream(&buffer); QDataStream bufferStream(&buffer);
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
if (!isCanceled()) { if (!isCanceled()) {
const int numEventTypes = m_modelManager->numEventTypes(); const QmlProfilerModelManager *manager = modelManager();
const int numEventTypes = manager->numEventTypes();
bufferStream << static_cast<quint32>(numEventTypes); bufferStream << static_cast<quint32>(numEventTypes);
for (int typeId = 0; typeId < numEventTypes; ++typeId) for (int typeId = 0; typeId < numEventTypes; ++typeId)
bufferStream << m_modelManager->eventType(typeId); bufferStream << manager->eventType(typeId);
stream << qCompress(buffer.data()); stream << qCompress(buffer.data());
buffer.close(); buffer.close();
buffer.buffer().clear(); buffer.buffer().clear();
updateProgress(ProgressTypes); addStageProgress(ProgressTypes);
} }
if (!isCanceled()) { if (!isCanceled()) {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
bufferStream << m_notes; bufferStream << qmlNotes()->notes();
stream << qCompress(buffer.data()); stream << qCompress(buffer.data());
buffer.close(); buffer.close();
buffer.buffer().clear(); buffer.buffer().clear();
updateProgress(ProgressNotes); addStageProgress(ProgressNotes);
} }
if (!isCanceled()) { if (isCanceled()) {
buffer.open(QIODevice::WriteOnly); emit canceled();
const bool success = m_modelManager->replayEvents( return;
-1, -1, [this, &stream, &buffer, &bufferStream](const QmlEvent &event, }
const QmlEventType &type) {
qint64 lastProgressTimestamp = traceStart();
modelManager()->replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
Q_UNUSED(type); Q_UNUSED(type);
bufferStream << event; bufferStream << event;
// 32MB buffer should be plenty for efficient compression // 32MB buffer should be plenty for efficient compression
@@ -898,46 +866,38 @@ void QmlProfilerFileWriter::saveQzt(QFile *file)
stream << qCompress(buffer.data()); stream << qCompress(buffer.data());
buffer.close(); buffer.close();
buffer.buffer().clear(); buffer.buffer().clear();
if (isCanceled())
return;
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
updateProgress(event.timestamp()); if (isProgressUpdateNeeded()) {
} addEventsProgress(event.timestamp() - lastProgressTimestamp);
}); lastProgressTimestamp = event.timestamp();
if (!success) {
emit error(tr("Could not re-read events from temporary trace file. Saving failed."));
return;
} }
} }
}, [&]() {
buffer.open(QIODevice::WriteOnly);
}, [&]() {
if (isCanceled()) { if (isCanceled()) {
emit canceled(); emit canceled();
} else { } else {
stream << qCompress(buffer.data()); stream << qCompress(buffer.data());
buffer.close(); buffer.close();
buffer.buffer().clear(); buffer.buffer().clear();
updateProgress(m_endTime); addEventsProgress(traceEnd() - lastProgressTimestamp);
emit success(); emit success();
} }
}, [this](const QString &message) {
emit error(tr("Could not re-read events from temporary trace file: %s\nSaving failed.")
.arg(message));
}, future());
} }
void QmlProfilerFileWriter::updateProgress(qint64 timestamp) QmlProfilerModelManager *QmlProfilerTraceFile::modelManager()
{ {
if (!m_future) return static_cast<QmlProfilerModelManager *>(traceManager());
return;
if (timestamp < 0) {
m_future->setProgressValue(m_future->progressValue() - timestamp);
} else {
m_future->setProgressValue(m_future->progressValue()
+ float(m_endTime - timestamp) / float(m_endTime - m_startTime)
* ProgressEvents);
}
} }
bool QmlProfilerFileWriter::isCanceled() const QmlProfilerNotesModel *QmlProfilerTraceFile::qmlNotes()
{ {
return m_future && m_future->isCanceled(); return static_cast<QmlProfilerNotesModel *>(notes());
} }
} // namespace Internal } // namespace Internal

View File

@@ -32,6 +32,8 @@
#include "qmlevent.h" #include "qmlevent.h"
#include "qmlprofilermodelmanager.h" #include "qmlprofilermodelmanager.h"
#include <timeline/timelinetracefile.h>
#include <QFutureInterface> #include <QFutureInterface>
#include <QObject> #include <QObject>
#include <QVector> #include <QVector>
@@ -44,81 +46,36 @@ QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
class QmlProfilerFileReader : public QObject class QmlProfilerTraceFile : public Timeline::TimelineTraceFile
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlProfilerFileReader(QObject *parent = 0); explicit QmlProfilerTraceFile(QObject *parent = nullptr);
void setFuture(QFutureInterface<void> *future); void load(QIODevice *device) final;
void save(QIODevice *device) final;
void loadQtd(QIODevice *device);
void loadQzt(QIODevice *device);
quint64 loadedFeatures() const;
qint64 traceStart() const { return m_traceStart; }
qint64 traceEnd() const { return m_traceEnd; }
signals:
void typesLoaded(const QVector<QmlProfiler::QmlEventType> &types);
void notesLoaded(const QVector<QmlProfiler::QmlNote> &notes);
void qmlEventsLoaded(const QVector<QmlProfiler::QmlEvent> &event);
void error(const QString &error);
void success();
void canceled();
private: private:
QmlProfilerModelManager *modelManager();
QmlProfilerNotesModel *qmlNotes();
void loadQtd(QIODevice *device);
void loadQzt(QIODevice *device);
void saveQtd(QIODevice *device);
void saveQzt(QIODevice *device);
void loadEventTypes(QXmlStreamReader &reader); void loadEventTypes(QXmlStreamReader &reader);
void loadEvents(QXmlStreamReader &reader); void loadEvents(QXmlStreamReader &reader);
void loadNotes(QXmlStreamReader &reader); void loadNotes(QXmlStreamReader &reader);
void updateProgress(QIODevice *device);
bool isCanceled() const;
qint64 m_traceStart, m_traceEnd;
QFutureInterface<void> *m_future;
QVector<QmlEventType> m_eventTypes;
QVector<QmlNote> m_notes;
quint64 m_loadedFeatures;
};
class QmlProfilerFileWriter : public QObject
{
Q_OBJECT
public:
explicit QmlProfilerFileWriter(QObject *parent = 0);
void setTraceTime(qint64 startTime, qint64 endTime, qint64 measturedTime);
void setData(const QmlProfilerModelManager *model);
void setNotes(const QVector<QmlNote> &notes);
void setFuture(QFutureInterface<void> *future);
void saveQtd(QIODevice *device);
void saveQzt(QFile *file);
signals:
void error(const QString &error);
void success();
void canceled();
private:
void updateProgress(qint64 timestamp);
bool isCanceled() const;
enum ProgressValues { enum ProgressValues {
ProgressTypes = -128, ProgressTypes = 128,
ProgressNotes = -32, ProgressNotes = 32,
ProgressEvents = 1024, ProgressEvents = MaximumProgress - ProgressTypes - ProgressNotes - MinimumProgress,
ProgressTotal = ProgressEvents - ProgressTypes - ProgressNotes
}; };
qint64 m_startTime, m_endTime, m_measuredTime; void addEventsProgress(qint64 timestamp);
QFutureInterface<void> *m_future; void addStageProgress(ProgressValues stage);
const QmlProfilerModelManager *m_modelManager;
QVector<QmlNote> m_notes;
}; };

View File

@@ -425,7 +425,7 @@ bool TraceViewFindSupport::findOne(const QString &txt, Core::FindFlags findFlags
bool forwardSearch = !(findFlags & Core::FindBackward); bool forwardSearch = !(findFlags & Core::FindBackward);
int increment = forwardSearch ? +1 : -1; int increment = forwardSearch ? +1 : -1;
int current = forwardSearch ? start : start - 1; int current = forwardSearch ? start : start - 1;
QmlProfilerNotesModel *model = m_modelManager->notesModel(); Timeline::TimelineNotesModel *model = m_modelManager->notesModel();
while (current >= 0 && current < model->count()) { while (current >= 0 && current < model->count()) {
QTextDocument doc(model->text(current)); // for automatic handling of WholeWords option QTextDocument doc(model->text(current)); // for automatic handling of WholeWords option
if (!doc.find(regexp, 0, flags).isNull()) { if (!doc.find(regexp, 0, flags).isNull()) {

View File

@@ -219,6 +219,7 @@ void SceneGraphTimelineModel::finalize()
{ {
computeNesting(); computeNesting();
flattenLoads(); flattenLoads();
QmlProfilerTimelineModel::finalize();
} }
void SceneGraphTimelineModel::flattenLoads() void SceneGraphTimelineModel::flattenLoads()

View File

@@ -39,7 +39,7 @@ DebugMessagesModelTest::DebugMessagesModelTest(QObject *parent) :
void DebugMessagesModelTest::initTestCase() void DebugMessagesModelTest::initTestCase()
{ {
manager.startAcquiring(); manager.initialize();
QmlEvent event; QmlEvent event;
event.setTypeIndex(-1); event.setTypeIndex(-1);

View File

@@ -45,7 +45,7 @@ int FlameGraphModelTest::generateData(QmlProfilerModelManager *manager,
int rangeModelId = rangeModel->modelId(); int rangeModelId = rangeModel->modelId();
manager->notesModel()->addTimelineModel(rangeModel); manager->notesModel()->addTimelineModel(rangeModel);
manager->startAcquiring(); manager->initialize();
QmlEvent event; QmlEvent event;
event.setTypeIndex(-1); event.setTypeIndex(-1);
@@ -87,7 +87,8 @@ int FlameGraphModelTest::generateData(QmlProfilerModelManager *manager,
manager->finalize(); manager->finalize();
manager->notesModel()->setNotes(QVector<QmlNote>({QmlNote(0, 2, 1, 20, "dings")})); static_cast<QmlProfilerNotesModel *>(manager->notesModel())
->setNotes(QVector<QmlNote>({QmlNote(0, 2, 1, 20, "dings")}));
manager->notesModel()->restore(); manager->notesModel()->restore();
return rangeModelId; return rangeModelId;
@@ -220,7 +221,7 @@ void FlameGraphModelTest::testNotes()
void FlameGraphModelTest::cleanupTestCase() void FlameGraphModelTest::cleanupTestCase()
{ {
manager.clear(); manager.clearAll();
QCOMPARE(model.rowCount(), 0); QCOMPARE(model.rowCount(), 0);
} }

View File

@@ -160,7 +160,7 @@ void FlameGraphViewTest::testContextMenu()
void FlameGraphViewTest::cleanupTestCase() void FlameGraphViewTest::cleanupTestCase()
{ {
manager.clear(); manager.clearAll();
} }
} // namespace Internal } // namespace Internal

View File

@@ -48,7 +48,7 @@ InputEventsModelTest::InputEventsModelTest(QObject *parent) :
void InputEventsModelTest::initTestCase() void InputEventsModelTest::initTestCase()
{ {
manager.startAcquiring(); manager.initialize();
QmlEvent event; QmlEvent event;
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {

View File

@@ -36,7 +36,7 @@ MemoryUsageModelTest::MemoryUsageModelTest(QObject *parent) : QObject(parent),
void MemoryUsageModelTest::initTestCase() void MemoryUsageModelTest::initTestCase()
{ {
manager.startAcquiring(); manager.initialize();
qint64 timestamp = 0; qint64 timestamp = 0;
@@ -233,7 +233,7 @@ void MemoryUsageModelTest::testAccepted()
void MemoryUsageModelTest::cleanupTestCase() void MemoryUsageModelTest::cleanupTestCase()
{ {
manager.clear(); manager.clearAll();
QCOMPARE(model.count(), 0); QCOMPARE(model.count(), 0);
} }

View File

@@ -37,7 +37,7 @@ PixmapCacheModelTest::PixmapCacheModelTest(QObject *parent) : QObject(parent),
void PixmapCacheModelTest::initTestCase() void PixmapCacheModelTest::initTestCase()
{ {
manager.startAcquiring(); manager.initialize();
manager.decreaseTraceStart(1); manager.decreaseTraceStart(1);
manager.increaseTraceEnd(300); manager.increaseTraceEnd(300);
@@ -313,7 +313,7 @@ void PixmapCacheModelTest::testLabels()
void PixmapCacheModelTest::cleanupTestCase() void PixmapCacheModelTest::cleanupTestCase()
{ {
manager.clear(); manager.clearAll();
QCOMPARE(model.count(), 0); QCOMPARE(model.count(), 0);
} }

View File

@@ -42,7 +42,7 @@ static int frameRate(int i)
void QmlProfilerAnimationsModelTest::initTestCase() void QmlProfilerAnimationsModelTest::initTestCase()
{ {
manager.startAcquiring(); manager.initialize();
QmlEventType type(Event, MaximumRangeType, AnimationFrame); QmlEventType type(Event, MaximumRangeType, AnimationFrame);
QmlEvent event; QmlEvent event;

View File

@@ -69,6 +69,7 @@ void QmlProfilerTraceClientTest::testMessageReceived()
} }
traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected); traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected);
QFutureInterface<void> future;
modelManager.replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { modelManager.replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) {
qint64 timestamp; qint64 timestamp;
qint32 message; qint32 message;
@@ -77,10 +78,19 @@ void QmlProfilerTraceClientTest::testMessageReceived()
QCOMPARE(event.timestamp(), timestamp); QCOMPARE(event.timestamp(), timestamp);
QCOMPARE(type.message(), static_cast<Message>(message)); QCOMPARE(type.message(), static_cast<Message>(message));
QCOMPARE(type.rangeType(), static_cast<RangeType>(rangeType)); QCOMPARE(type.rangeType(), static_cast<RangeType>(rangeType));
}); }, nullptr, [this]() {
modelManager.clearAll();
modelManager.clear();
traceClient.clear(); traceClient.clear();
}, [this](const QString &message) {
if (message == QmlProfilerModelManager::tr("Read past end in temporary trace file")) {
// Ignore read-past-end errors: Our test traces are somewhat dirty and don't end on
// packet boundaries
modelManager.clearAll();
traceClient.clear();
} else {
QFAIL(message.toUtf8().constData());
}
}, future);
} }
QVERIFY(checkStream.atEnd()); QVERIFY(checkStream.atEnd());

View File

@@ -37,15 +37,15 @@ QmlProfilerTraceViewTest::QmlProfilerTraceViewTest(QObject *parent) :
void QmlProfilerTraceViewTest::testStateChanges() void QmlProfilerTraceViewTest::testStateChanges()
{ {
// Standard acquire-process-clear work flow // Standard acquire-process-clear work flow
modelManager.startAcquiring(); modelManager.initialize();
QVERIFY(traceView.isSuspended()); QVERIFY(traceView.isSuspended());
modelManager.finalize(); modelManager.finalize();
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
modelManager.clear(); modelManager.clearAll();
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
// Restrict to range // Restrict to range
modelManager.startAcquiring(); modelManager.initialize();
QVERIFY(traceView.isSuspended()); QVERIFY(traceView.isSuspended());
modelManager.finalize(); modelManager.finalize();
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
@@ -53,13 +53,13 @@ void QmlProfilerTraceViewTest::testStateChanges()
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
modelManager.restrictToRange(-1, -1); modelManager.restrictToRange(-1, -1);
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
modelManager.clear(); modelManager.clearAll();
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
// Abort Acquiring // Abort Acquiring
modelManager.startAcquiring(); modelManager.initialize();
QVERIFY(traceView.isSuspended()); QVERIFY(traceView.isSuspended());
modelManager.clear(); modelManager.clearAll();
QVERIFY(!traceView.isSuspended()); QVERIFY(!traceView.isSuspended());
} }