diff --git a/src/libs/timeline/timeline.pro b/src/libs/timeline/timeline.pro index 678f4e8ac3e..aa8a1e1e34b 100644 --- a/src/libs/timeline/timeline.pro +++ b/src/libs/timeline/timeline.pro @@ -17,7 +17,9 @@ SOURCES += \ $$PWD/timelineabstractrenderer.cpp \ $$PWD/timelineoverviewrenderer.cpp \ $$PWD/timelinetheme.cpp \ - $$PWD/timelineformattime.cpp + $$PWD/timelineformattime.cpp \ + $$PWD/timelinetracefile.cpp \ + $$PWD/timelinetracemanager.cpp HEADERS += \ $$PWD/timeline_global.h \ @@ -41,8 +43,11 @@ HEADERS += \ $$PWD/timelineoverviewrenderer.h \ $$PWD/timelinetheme.h \ $$PWD/timelineformattime.h \ + $$PWD/timelinetracefile.h \ + $$PWD/timelinetracemanager.h \ $$PWD/traceevent.h \ - $$PWD/traceeventtype.h + $$PWD/traceeventtype.h \ + $$PWD/tracestashfile.h RESOURCES += \ $$PWD/qml/timeline.qrc diff --git a/src/libs/timeline/timeline.qbs b/src/libs/timeline/timeline.qbs index 8b000958dc0..584197e3f4f 100644 --- a/src/libs/timeline/timeline.qbs +++ b/src/libs/timeline/timeline.qbs @@ -30,8 +30,10 @@ Project { "timelinerenderstate.cpp", "timelinerenderstate.h", "timelinerenderstate_p.h", "timelineselectionrenderpass.cpp", "timelineselectionrenderpass.h", "timelinetheme.cpp", "timelinetheme.h", + "timelinetracefile.cpp", "timelinetracefile.h", + "timelinetracemanager.cpp", "timelinetracemanager.h", "timelinezoomcontrol.cpp", "timelinezoomcontrol.h", - "traceevent.h", "traceeventtype.h", + "traceevent.h", "traceeventtype.h", "tracestashfile.h" ] } diff --git a/src/libs/timeline/timelinetracefile.cpp b/src/libs/timeline/timelinetracefile.cpp new file mode 100644 index 00000000000..45fc4bbbe17 --- /dev/null +++ b/src/libs/timeline/timelinetracefile.cpp @@ -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 &future) +{ + m_future = future; + m_future.setProgressRange(MinimumProgress, MaximumProgress); + m_future.setProgressValue(MinimumProgress); +} + +QFutureInterface &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(MinimumProgress), + qMin(m_future.progressValue() + progressValue, + static_cast(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 diff --git a/src/libs/timeline/timelinetracefile.h b/src/libs/timeline/timelinetracefile.h new file mode 100644 index 00000000000..950b9f21d78 --- /dev/null +++ b/src/libs/timeline/timelinetracefile.h @@ -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 +#include + +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 &future); + QFutureInterface &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 m_future; + + TimelineTraceManager *m_traceManager = nullptr; + TimelineNotesModel *m_notes = nullptr; +}; + +} // namespace Timeline diff --git a/src/libs/timeline/timelinetracemanager.cpp b/src/libs/timeline/timelinetracemanager.cpp new file mode 100644 index 00000000000..e568c87a4b3 --- /dev/null +++ b/src/libs/timeline/timelinetracemanager.cpp @@ -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 +#include +#include + +#include +#include + +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> eventLoaders; + QVector initializers; + QVector finalizers; + QVector 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 TimelineTraceManager::save(const QString &filename) +{ + QFile *file = new QFile(filename); + if (!file->open(QIODevice::WriteOnly)) { + delete file; + return Utils::runAsync([this, filename](QFutureInterface &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 &future) { + writer->setFuture(future); + writer->save(file); + writer->deleteLater(); + }); +} + +QFuture TimelineTraceManager::load(const QString &filename) +{ + QFile *file = new QFile(filename, this); + if (!file->open(QIODevice::ReadOnly)) { + delete file; + return Utils::runAsync([this, filename] (QFutureInterface &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 &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 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 diff --git a/src/libs/timeline/timelinetracemanager.h b/src/libs/timeline/timelinetracemanager.h new file mode 100644 index 00000000000..a8ace50a149 --- /dev/null +++ b/src/libs/timeline/timelinetracemanager.h @@ -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 +#include + +#include + +namespace Timeline { + +class TimelineTraceFile; +class TIMELINE_EXPORT TimelineTraceManager : public QObject +{ + Q_OBJECT +public: + enum State { + Empty, + AcquiringData, + ClearingData, + Done + }; + + typedef std::function TraceEventLoader; + typedef std::function Initializer; + typedef std::function Finalizer; + typedef std::function Clearer; + typedef std::function 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 save(const QString &filename); + QFuture 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 &future) const = 0; + +private: + class TimelineTraceManagerPrivate; + TimelineTraceManagerPrivate *d; +}; + +} // namespace Timeline diff --git a/src/libs/timeline/tracestashfile.h b/src/libs/timeline/tracestashfile.h new file mode 100644 index 00000000000..d860ece9221 --- /dev/null +++ b/src/libs/timeline/tracestashfile.h @@ -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 + +#include +#include + +namespace Timeline { + +template +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 + 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; +}; + +} diff --git a/src/plugins/qmlprofiler/debugmessagesmodel.cpp b/src/plugins/qmlprofiler/debugmessagesmodel.cpp index 82578df8ec4..792ae23b092 100644 --- a/src/plugins/qmlprofiler/debugmessagesmodel.cpp +++ b/src/plugins/qmlprofiler/debugmessagesmodel.cpp @@ -111,6 +111,7 @@ void DebugMessagesModel::finalize() { setCollapsedRowCount(Constants::QML_MIN_LEVEL + 1); setExpandedRowCount(m_maximumMsgType + 2); + QmlProfilerTimelineModel::finalize(); } void DebugMessagesModel::clear() diff --git a/src/plugins/qmlprofiler/flamegraphmodel.cpp b/src/plugins/qmlprofiler/flamegraphmodel.cpp index 41e0b141558..d8a3315576b 100644 --- a/src/plugins/qmlprofiler/flamegraphmodel.cpp +++ b/src/plugins/qmlprofiler/flamegraphmodel.cpp @@ -51,20 +51,18 @@ FlameGraphModel::FlameGraphModel(QmlProfilerModelManager *modelManager, m_compileStack.append(QmlEvent()); m_callStackTop = &m_stackBottom; m_compileStackTop = &m_stackBottom; - connect(modelManager, &QmlProfilerModelManager::stateChanged, - this, &FlameGraphModel::onModelManagerStateChanged); connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished, this, &FlameGraphModel::onTypeDetailsFinished); connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed, this, [this](int typeId, int, int){loadNotes(typeId, true);}); m_acceptedFeatures = supportedFeatures(); - modelManager->announceFeatures(m_acceptedFeatures, - [this](const QmlEvent &event, const QmlEventType &type) { - loadEvent(event, type); - }, [this](){ - finalize(); - }); + modelManager->registerFeatures(m_acceptedFeatures, + std::bind(&FlameGraphModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2), + std::bind(&FlameGraphModel::beginResetModel, this), + std::bind(&FlameGraphModel::finalize, this), + std::bind(&FlameGraphModel::clear, this)); } void FlameGraphModel::clear() @@ -108,9 +106,6 @@ void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type) if (!(m_acceptedFeatures & (1ULL << type.feature()))) return; - if (m_stackBottom.children.isEmpty()) - beginResetModel(); - const bool isCompiling = (type.rangeType() == Compiling); QStack &stack = isCompiling ? m_compileStack : m_callStack; FlameGraphData *&stackTop = isCompiling ? m_compileStackTop : m_callStackTop; @@ -152,12 +147,6 @@ void FlameGraphModel::finalize() endResetModel(); } -void FlameGraphModel::onModelManagerStateChanged() -{ - if (m_modelManager->state() == QmlProfilerModelManager::ClearingData) - clear(); -} - void FlameGraphModel::onTypeDetailsFinished() { emit dataChanged(QModelIndex(), QModelIndex(), QVector(1, DetailsRole)); @@ -174,17 +163,19 @@ void FlameGraphModel::restrictToFeatures(quint64 visibleFeatures) return; clear(); - beginResetModel(); - if (!m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), - std::bind(&FlameGraphModel::loadEvent, - this, std::placeholders::_1, - std::placeholders::_2))) { - emit m_modelManager->error(tr("Could not re-read events from temporary trace file.")); + + QFutureInterface future; + m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), + std::bind(&FlameGraphModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2), + 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(); clear(); - } else { - finalize(); - } + }, future); } static QString nameForType(RangeType typeNumber) @@ -207,7 +198,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const QString ret; if (!m_typeIdsWithNotes.contains(stats.typeIndex)) return ret; - QmlProfilerNotesModel *notes = m_modelManager->notesModel(); + Timeline::TimelineNotesModel *notes = m_modelManager->notesModel(); foreach (const QVariant &item, notes->byTypeId(stats.typeIndex)) { if (ret.isEmpty()) ret = notes->text(item.toInt()); diff --git a/src/plugins/qmlprofiler/flamegraphmodel.h b/src/plugins/qmlprofiler/flamegraphmodel.h index d0040666b7a..666b498cacf 100644 --- a/src/plugins/qmlprofiler/flamegraphmodel.h +++ b/src/plugins/qmlprofiler/flamegraphmodel.h @@ -89,7 +89,6 @@ public: void loadEvent(const QmlEvent &event, const QmlEventType &type); void finalize(); - void onModelManagerStateChanged(); void onTypeDetailsFinished(); void restrictToFeatures(quint64 visibleFeatures); void loadNotes(int typeId, bool emitSignal); diff --git a/src/plugins/qmlprofiler/inputeventsmodel.cpp b/src/plugins/qmlprofiler/inputeventsmodel.cpp index ae8e46f405d..01268c56202 100644 --- a/src/plugins/qmlprofiler/inputeventsmodel.cpp +++ b/src/plugins/qmlprofiler/inputeventsmodel.cpp @@ -165,6 +165,7 @@ void InputEventsModel::finalize() { setCollapsedRowCount(2); setExpandedRowCount(3); + QmlProfilerTimelineModel::finalize(); } void InputEventsModel::clear() diff --git a/src/plugins/qmlprofiler/memoryusagemodel.cpp b/src/plugins/qmlprofiler/memoryusagemodel.cpp index 5c878a0410a..2e8876fcbaf 100644 --- a/src/plugins/qmlprofiler/memoryusagemodel.cpp +++ b/src/plugins/qmlprofiler/memoryusagemodel.cpp @@ -36,8 +36,11 @@ MemoryUsageModel::MemoryUsageModel(QmlProfilerModelManager *manager, Timeline::TimelineModelAggregator *parent) : QmlProfilerTimelineModel(manager, MemoryAllocation, MaximumRangeType, ProfileMemory, parent) { - // Announce additional features. The base class already announces the main feature. - announceFeatures(Constants::QML_JS_RANGE_FEATURES ^ (1 << ProfileCompiling)); + // Register additional features. The base class already registers the main feature. + // 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 @@ -246,6 +249,7 @@ void MemoryUsageModel::finalize() computeNesting(); setExpandedRowCount(3); setCollapsedRowCount(3); + QmlProfilerTimelineModel::finalize(); } void MemoryUsageModel::clear() diff --git a/src/plugins/qmlprofiler/pixmapcachemodel.cpp b/src/plugins/qmlprofiler/pixmapcachemodel.cpp index b21e2f7cfff..be7e7227c32 100644 --- a/src/plugins/qmlprofiler/pixmapcachemodel.cpp +++ b/src/plugins/qmlprofiler/pixmapcachemodel.cpp @@ -391,6 +391,7 @@ void PixmapCacheModel::finalize() computeMaxCacheSize(); flattenLoads(); computeNesting(); + QmlProfilerTimelineModel::finalize(); } void PixmapCacheModel::clear() diff --git a/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp b/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp index f743d2a5100..d207ec96da0 100644 --- a/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofileranimationsmodel.cpp @@ -102,6 +102,7 @@ void QmlProfilerAnimationsModel::finalize() computeNesting(); setExpandedRowCount((m_maxGuiThreadAnimations == 0 || m_maxRenderThreadAnimations == 0) ? 2 : 3); setCollapsedRowCount(expandedRowCount()); + QmlProfilerTimelineModel::finalize(); } int QmlProfilerAnimationsModel::rowFromThreadId(int threadId) const diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp index 2f5afbd8acd..f60e843b403 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp @@ -30,9 +30,9 @@ #include "qmlprofilerdetailsrewriter.h" #include +#include #include #include -#include #include #include @@ -43,7 +43,6 @@ #include namespace QmlProfiler { -namespace Internal { static const char *ProfileFeatureNames[] = { QT_TRANSLATE_NOOP("MainView", "JavaScript"), @@ -62,65 +61,44 @@ static const char *ProfileFeatureNames[] = { Q_STATIC_ASSERT(sizeof(ProfileFeatureNames) == sizeof(char *) * MaximumProfileFeature); -} // namespace Internal - -using namespace Internal; - class QmlProfilerModelManager::QmlProfilerModelManagerPrivate { public: QmlProfilerModelManagerPrivate() : file("qmlprofiler-data") {} - QmlProfilerNotesModel *notesModel = 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 > eventLoaders; - QVector finalizers; + Internal::QmlProfilerTextMarkModel *textMarkModel = nullptr; QVector eventTypes; - QmlProfilerDetailsRewriter *detailsRewriter = nullptr; + Internal::QmlProfilerDetailsRewriter *detailsRewriter = nullptr; - Utils::TemporaryFile file; - QDataStream eventStream; + Timeline::TraceStashFile file; - qint64 traceStart = -1; - qint64 traceEnd = -1; - qint64 restrictedTraceStart = -1; - qint64 restrictedTraceEnd = -1; + void writeToStream(const QmlEvent &event); + void addEventType(const QmlEventType &eventType); + void handleError(const QString &message); - void dispatch(const QmlEvent &event, const QmlEventType &type); void rewriteType(int typeIndex); int resolveStackTop(); - void updateTraceTime(qint64 time); - void restrictTraceTimeToRange(qint64 start, qint64 end); }; QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) : - QObject(parent), d(new QmlProfilerModelManagerPrivate) + Timeline::TimelineTraceManager(parent), d(new QmlProfilerModelManagerPrivate) { - d->notesModel = new QmlProfilerNotesModel(this); - d->textMarkModel = new QmlProfilerTextMarkModel(this); + setNotesModel(new QmlProfilerNotesModel(this)); + d->textMarkModel = new Internal::QmlProfilerTextMarkModel(this); - d->detailsRewriter = new QmlProfilerDetailsRewriter(this); - connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::rewriteDetailsString, - this, &QmlProfilerModelManager::detailsChanged); - connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::eventDetailsChanged, + d->detailsRewriter = new Internal::QmlProfilerDetailsRewriter(this); + connect(d->detailsRewriter, &Internal::QmlProfilerDetailsRewriter::rewriteDetailsString, + this, &QmlProfilerModelManager::typeDetailsChanged); + connect(d->detailsRewriter, &Internal::QmlProfilerDetailsRewriter::eventDetailsChanged, this, &QmlProfilerModelManager::typeDetailsFinished); - if (d->file.open()) - d->eventStream.setDevice(&d->file); - else + if (!d->file.open()) 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() @@ -128,123 +106,36 @@ QmlProfilerModelManager::~QmlProfilerModelManager() delete d; } -qint64 QmlProfilerModelManager::traceStart() 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 +Internal::QmlProfilerTextMarkModel *QmlProfilerModelManager::textMarkModel() const { 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(event), + static_cast(type)); + } : TraceEventLoader(); -int QmlProfilerModelManager::numEvents() const -{ - 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(); + Timeline::TimelineTraceManager::registerFeatures(features, traceEventLoader, initializer, + finalizer, clearer); } void QmlProfilerModelManager::addEvents(const QVector &events) { - for (const QmlEvent &event : events) { - d->eventStream << 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())); + for (const QmlEvent &event : events) + addEvent(event); } void QmlProfilerModelManager::addEventTypes(const QVector &types) { - const int firstTypeId = d->eventTypes.length();; - d->eventTypes.append(types); - for (int typeId = firstTypeId, end = d->eventTypes.length(); typeId < end; ++typeId) { - d->rewriteType(typeId); - const QmlEventLocation &location = d->eventTypes[typeId].location(); - if (location.isValid()) { - d->textMarkModel->addTextMarkId(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())); + for (const QmlEventType &type : types) { + d->addEventType(type); + TimelineTraceManager::addEventType(type); } } @@ -253,6 +144,15 @@ const QmlEventType &QmlProfilerModelManager::eventType(int typeId) const return d->eventTypes.at(typeId); } +void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd, + TraceEventLoader loader, Initializer initializer, + Finalizer finalizer, ErrorHandler errorHandler, + QFutureInterface &future) const +{ + replayEvents(rangeStart, rangeEnd, static_cast(loader), initializer, finalizer, + errorHandler, future); +} + static bool isStateful(const QmlEventType &type) { // Events of these types carry state that has to be taken into account when adding later events: @@ -262,82 +162,102 @@ static bool isStateful(const QmlEventType &type) return message == PixmapCacheEvent || message == MemoryAllocation; } -bool QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd, - EventLoader loader) const +void QmlProfilerModelManager::replayEvents(qint64 rangeStart, qint64 rangeEnd, + QmlEventLoader loader, Initializer initializer, + Finalizer finalizer, ErrorHandler errorHandler, + QFutureInterface &future) const { - QStack stack; - QmlEvent event; - QFile file(d->file.fileName()); - if (!file.open(QIODevice::ReadOnly)) - return false; + if (initializer) + initializer(); - QDataStream stream(&file); + QStack stack; bool crossedRangeStart = false; - while (!stream.atEnd()) { - stream >> event; - if (stream.status() == QDataStream::ReadPastEnd) - break; + + const auto result = d->file.replay([&](const QmlEvent &event) { + if (future.isCanceled()) + return false; const QmlEventType &type = d->eventTypes[event.typeIndex()]; - if (rangeStart != -1 && rangeEnd != -1) { - // Double-check if rangeStart has been crossed. Some versions of Qt send dirty data. - if (event.timestamp() < rangeStart && !crossedRangeStart) { - if (type.rangeType() != MaximumRangeType) { - if (event.rangeStage() == RangeStart) - stack.push(event); - else if (event.rangeStage() == RangeEnd) - stack.pop(); - continue; - } else if (isStateful(type)) { - event.setTimestamp(rangeStart); - } else { - continue; - } + + // 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. + qint64 adjustedTimestamp = event.timestamp(); + if (event.timestamp() < rangeStart && !crossedRangeStart) { + if (type.rangeType() != MaximumRangeType) { + if (event.rangeStage() == RangeStart) + stack.push(event); + else if (event.rangeStage() == RangeEnd) + stack.pop(); + return true; + } else if (isStateful(type)) { + adjustedTimestamp = rangeStart; } else { - if (!crossedRangeStart) { - foreach (QmlEvent stashed, stack) { - stashed.setTimestamp(rangeStart); - loader(stashed, d->eventTypes[stashed.typeIndex()]); - } - stack.clear(); - crossedRangeStart = true; + return true; + } + } else { + if (!crossedRangeStart) { + for (QmlEvent stashed : stack) { + stashed.setTimestamp(rangeStart); + loader(stashed, d->eventTypes[stashed.typeIndex()]); } - if (event.timestamp() > rangeEnd) { - if (type.rangeType() != MaximumRangeType) { - if (event.rangeStage() == RangeEnd) { - if (stack.isEmpty()) { - QmlEvent endEvent(event); - endEvent.setTimestamp(rangeEnd); - loader(endEvent, d->eventTypes[event.typeIndex()]); - } else { - stack.pop(); - } - } else if (event.rangeStage() == RangeStart) { - stack.push(event); + stack.clear(); + crossedRangeStart = true; + } + if (event.timestamp() > rangeEnd) { + if (type.rangeType() != MaximumRangeType) { + if (event.rangeStage() == RangeEnd) { + if (stack.isEmpty()) { + QmlEvent endEvent(event); + endEvent.setTimestamp(rangeEnd); + loader(endEvent, d->eventTypes[event.typeIndex()]); + } else { + stack.pop(); } - continue; - } else if (isStateful(type)) { - event.setTimestamp(rangeEnd); - } else { - continue; + } else if (event.rangeStage() == RangeStart) { + stack.push(event); } + return true; + } else if (isStateful(type)) { + adjustedTimestamp = rangeEnd; + } else { + return true; } } } - loader(event, type); - } - return true; -} + if (adjustedTimestamp != event.timestamp()) { + QmlEvent adjusted(event); + adjusted.setTimestamp(adjustedTimestamp); + loader(adjusted, type); + } else { + loader(event, type); + } + return true; + }); -void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::dispatch(const QmlEvent &event, - const QmlEventType &type) -{ - for (const EventLoader &loader : eventLoaders.value( - static_cast(type.feature()))) { - loader(event, type); + switch (result) { + case Timeline::TraceStashFile::ReplaySuccess: + if (finalizer) + finalizer(); + break; + case Timeline::TraceStashFile::ReplayOpenFailed: + if (errorHandler) + errorHandler(tr("Could not re-open temporary trace file")); + break; + case Timeline::TraceStashFile::ReplayLoadFailed: + if (errorHandler) + errorHandler(tr("Could not load events from temporary trace file")); + break; + case Timeline::TraceStashFile::ReplayReadPastEnd: + if (errorHandler) + errorHandler(tr("Read past end in temporary trace file")); + break; } - ++numLoadedEvents; } static QString getDisplayName(const QmlEventType &event) @@ -374,6 +294,30 @@ static QString getInitialDetails(const QmlEventType &event) 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) { QmlEventType &type = eventTypes[typeIndex]; @@ -390,85 +334,6 @@ void QmlProfilerModelManager::QmlProfilerModelManagerPrivate::rewriteType(int ty 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(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) { return ProfileFeatureNames[feature]; @@ -477,18 +342,14 @@ const char *QmlProfilerModelManager::featureName(ProfileFeature feature) void QmlProfilerModelManager::finalize() { QTC_ASSERT(state() == AcquiringData, /**/); - d->file.flush(); + if (!d->file.flush()) + emit error(tr("Failed to flush temporary trace file")); d->detailsRewriter->reloadDocuments(); // Load notes after the timeline models have been initialized ... // which happens on stateChanged(Done). - foreach (const Finalizer &finalizer, d->finalizers) { - finalizer(); - ++d->numFinishedFinalizers; - } - - setState(Done); + TimelineTraceManager::finalize(); } void QmlProfilerModelManager::populateFileFinder(const ProjectExplorer::Target *target) @@ -501,146 +362,6 @@ QString QmlProfilerModelManager::findLocalFile(const QString &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 result = Utils::runAsync([file, writer] (QFutureInterface &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 result = Utils::runAsync([isQtd, file, reader] (QFutureInterface &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) { QTC_ASSERT(typeId < d->eventTypes.count(), return); @@ -648,76 +369,40 @@ void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newStrin 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; - d->numFinishedFinalizers = 0; - d->file.remove(); - d->eventStream.unsetDevice(); - if (d->file.open()) - d->eventStream.setDevice(&d->file); - else - emit error(tr("Cannot open temporary trace file to store events.")); - d->restrictTraceTimeToRange(-1, -1); - d->traceStart = -1; - d->traceEnd = -1; - d->notesModel->clear(); - setVisibleFeatures(0); - setRecordedFeatures(0); + TimelineTraceManager::clearEventStorage(); + d->file.clear(); + if (!d->file.open()) + emit error(tr("Failed to reset temporary trace file")); } -void QmlProfilerModelManager::clearEvents() +void QmlProfilerModelManager::clearTypeStorage() { - setState(ClearingData); - doClearEvents(); - setState(Empty); -} - -void QmlProfilerModelManager::clear() -{ - setState(ClearingData); - doClearEvents(); + TimelineTraceManager::clearTypeStorage(); d->eventTypes.clear(); - d->detailsRewriter->clear(); - setState(Empty); } -void QmlProfilerModelManager::restrictToRange(qint64 startTime, qint64 endTime) +void QmlProfilerModelManager::addEventType(const QmlEventType &type) { - d->notesModel->stash(); - const QVector notes = d->notesModel->notes(); - 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(); - } + d->addEventType(type); + TimelineTraceManager::addEventType(type); } -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 diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h index 00b525cbf30..bfa2a04f896 100644 --- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h +++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h @@ -33,6 +33,7 @@ #include "qmlprofilertextmark.h" #include +#include #include #include @@ -44,94 +45,54 @@ class QmlProfilerModelManager; class QmlProfilerNotesModel; // Interface between the Data Model and the Engine/Tool -class QMLPROFILER_EXPORT QmlProfilerModelManager : public QObject +class QMLPROFILER_EXPORT QmlProfilerModelManager : public Timeline::TimelineTraceManager { Q_OBJECT public: - enum State { - Empty, - AcquiringData, - ClearingData, - Done - }; - - typedef std::function EventLoader; - typedef std::function Finalizer; + typedef std::function QmlEventLoader; explicit QmlProfilerModelManager(QObject *parent = nullptr); ~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; - bool isEmpty() const; - int numEvents() const; - int numEventTypes() const; - - void announceFeatures(quint64 features, EventLoader eventLoader, Finalizer finalizer); - - int numFinishedFinalizers() const; - int numRegisteredFinalizers() const; + void registerFeatures(quint64 features, QmlEventLoader eventLoader, + Initializer initializer = nullptr, Finalizer finalizer = nullptr, + Clearer clearer = nullptr); void addEvents(const QVector &events); - void addEvent(const QmlEvent &event); - void addEventTypes(const QVector &types); - void addEventType(const QmlEventType &type); 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 &future) const; - quint64 availableFeatures() const; - quint64 visibleFeatures() const; - void setVisibleFeatures(quint64 features); - quint64 recordedFeatures() const; - void setRecordedFeatures(quint64 features); - bool aggregateTraces() const; - void setAggregateTraces(bool aggregateTraces); - - void finalize(); + void finalize() override; void populateFileFinder(const ProjectExplorer::Target *target = nullptr); QString findLocalFile(const QString &remoteFile); static const char *featureName(ProfileFeature feature); - void clearEvents(); - void clear(); - void restrictToRange(qint64 startTime, qint64 endTime); - bool isRestrictedToRange() const; - - void startAcquiring(); - - void save(const QString &filename); - void load(const QString &filename); + void addEventType(const QmlEventType &type); + void addEvent(const QmlEvent &event); 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 typeDetailsFinished(); private: - void setState(State state); 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 &future) const override; class QmlProfilerModelManagerPrivate; QmlProfilerModelManagerPrivate *d; diff --git a/src/plugins/qmlprofiler/qmlprofilernotesmodel.cpp b/src/plugins/qmlprofiler/qmlprofilernotesmodel.cpp index 2d6c0c09f4a..61c4540719e 100644 --- a/src/plugins/qmlprofiler/qmlprofilernotesmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilernotesmodel.cpp @@ -132,6 +132,11 @@ void QmlProfilerNotesModel::setNotes(const QVector ¬es) m_notes = notes; } +void QmlProfilerNotesModel::addNote(const QmlNote ¬e) +{ + m_notes.append(note); +} + void QmlProfilerNotesModel::clear() { TimelineNotesModel::clear(); diff --git a/src/plugins/qmlprofiler/qmlprofilernotesmodel.h b/src/plugins/qmlprofiler/qmlprofilernotesmodel.h index d131dc74c4e..3c11260e30a 100644 --- a/src/plugins/qmlprofiler/qmlprofilernotesmodel.h +++ b/src/plugins/qmlprofiler/qmlprofilernotesmodel.h @@ -42,6 +42,7 @@ public: const QVector ¬es() const; void setNotes(const QVector ¬es); + void addNote(const QmlNote ¬e); void clear() override; protected: diff --git a/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp b/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp index 07a5d20e0d9..cfa992b6e0e 100644 --- a/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerrangemodel.cpp @@ -103,6 +103,8 @@ void QmlProfilerRangeModel::finalize() if (supportsBindingLoops()) findBindingLoops(); + + QmlProfilerTimelineModel::finalize(); } void QmlProfilerRangeModel::computeNestingContracted() diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp index ef8a1ac882a..56f0920fac4 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp @@ -66,19 +66,17 @@ double QmlProfilerStatisticsModel::durationSelfPercent(int typeId) const QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager) : m_modelManager(modelManager) { - connect(modelManager, &QmlProfilerModelManager::stateChanged, - this, &QmlProfilerStatisticsModel::modelManagerStateChanged); connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed, this, &QmlProfilerStatisticsModel::notesChanged); m_acceptedTypes << Compiling << Creating << Binding << HandlingSignal << Javascript; - modelManager->announceFeatures(Constants::QML_JS_RANGE_FEATURES, - [this](const QmlEvent &event, const QmlEventType &type) { - loadEvent(event, type); - }, [this]() { - finalize(); - }); + modelManager->registerFeatures(Constants::QML_JS_RANGE_FEATURES, + std::bind(&QmlProfilerStatisticsModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2), + std::bind(&QmlProfilerStatisticsModel::beginResetModel, this), + std::bind(&QmlProfilerStatisticsModel::finalize, this), + std::bind(&QmlProfilerStatisticsModel::clear, this)); } void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features) @@ -102,18 +100,20 @@ void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features) return; clear(); - beginResetModel(); - if (!m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), - std::bind(&QmlProfilerStatisticsModel::loadEvent, - this, std::placeholders::_1, - std::placeholders::_2))) { - endResetModel(); - emit m_modelManager->error(tr("Could not re-read events from temporary trace file.")); - clear(); - } else { + QFutureInterface future; + m_modelManager->replayEvents(m_modelManager->traceStart(), m_modelManager->traceEnd(), + std::bind(&QmlProfilerStatisticsModel::loadEvent, this, + std::placeholders::_1, std::placeholders::_2), + std::bind(&QmlProfilerStatisticsModel::beginResetModel, this), + [this]() { finalize(); 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 @@ -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) { const QModelIndex index = createIndex(typeIndex, MainDetails); @@ -404,7 +390,7 @@ void QmlProfilerStatisticsModel::typeDetailsChanged(int typeIndex) void QmlProfilerStatisticsModel::notesChanged(int typeIndex) { static const QVector noteRoles({Qt::ToolTipRole, Qt::TextColorRole}); - const QmlProfilerNotesModel *notesModel = m_modelManager->notesModel(); + const Timeline::TimelineNotesModel *notesModel = m_modelManager->notesModel(); if (typeIndex == s_invalidTypeId) { m_notes.clear(); for (int noteId = 0; noteId < notesModel->count(); ++noteId) { diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h index bf4c04dbb40..6691dcbf923 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h @@ -152,7 +152,6 @@ private: void loadEvent(const QmlEvent &event, const QmlEventType &type); void finalize(); - void modelManagerStateChanged(); void typeDetailsChanged(int typeIndex); void notesChanged(int typeIndex); diff --git a/src/plugins/qmlprofiler/qmlprofilertimelinemodel.cpp b/src/plugins/qmlprofiler/qmlprofilertimelinemodel.cpp index 8cc9ffc0707..938f979f398 100644 --- a/src/plugins/qmlprofiler/qmlprofilertimelinemodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertimelinemodel.cpp @@ -35,15 +35,19 @@ QmlProfilerTimelineModel::QmlProfilerTimelineModel(QmlProfilerModelManager *mode m_modelManager(modelManager) { setDisplayName(tr(QmlProfilerModelManager::featureName(mainFeature))); - connect(modelManager, &QmlProfilerModelManager::stateChanged, - this, &QmlProfilerTimelineModel::dataChanged); connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished, this, &Timeline::TimelineModel::labelsChanged); connect(modelManager, &QmlProfilerModelManager::typeDetailsFinished, this, &Timeline::TimelineModel::detailsChanged); connect(modelManager, &QmlProfilerModelManager::visibleFeaturesChanged, 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 @@ -79,31 +83,6 @@ QmlProfilerModelManager *QmlProfilerTimelineModel::modelManager() const 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) { setHidden(!(features & (1ULL << m_mainFeature))); @@ -129,4 +108,13 @@ QVariantMap QmlProfilerTimelineModel::locationFromTypeId(int index) const return result; } +void QmlProfilerTimelineModel::initialize() +{ +} + +void QmlProfilerTimelineModel::finalize() +{ + emit contentChanged(); +} + } // namespace QmlProfiler diff --git a/src/plugins/qmlprofiler/qmlprofilertimelinemodel.h b/src/plugins/qmlprofiler/qmlprofilertimelinemodel.h index 12d2229586e..0724f181cc9 100644 --- a/src/plugins/qmlprofiler/qmlprofilertimelinemodel.h +++ b/src/plugins/qmlprofiler/qmlprofilertimelinemodel.h @@ -55,13 +55,10 @@ public: QVariantMap locationFromTypeId(int index) const; virtual void loadEvent(const QmlEvent &event, const QmlEventType &type) = 0; - virtual void finalize() = 0; - -protected: - void announceFeatures(quint64 features); + virtual void initialize(); + virtual void finalize(); private: - void dataChanged(); void onVisibleFeaturesChanged(quint64 features); const Message m_message; diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp index e7542f6e52d..0fba9affef2 100644 --- a/src/plugins/qmlprofiler/qmlprofilertool.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -505,14 +506,14 @@ void QmlProfilerTool::showTimeLineSearch() void QmlProfilerTool::clearEvents() { - d->m_profilerModelManager->clearEvents(); + d->m_profilerModelManager->clear(); d->m_profilerConnections->clearEvents(); setRecordedFeatures(0); } void QmlProfilerTool::clearData() { - d->m_profilerModelManager->clear(); + d->m_profilerModelManager->clearAll(); d->m_profilerConnections->clearBufferedData(); setRecordedFeatures(0); } @@ -666,7 +667,9 @@ void QmlProfilerTool::showSaveDialog() filename += zFile; saveLastTraceFile(filename); 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, this, &QmlProfilerTool::setRecordedFeatures); 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) { showNonmodalWarning(tr("Application finished before loading profiled data.\n" "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() || d->m_profilerModelManager->state() == QmlProfilerModelManager::Done) clearEvents(); - d->m_profilerModelManager->startAcquiring(); + d->m_profilerModelManager->initialize(); } else { d->m_recordingTimer.stop(); if (!d->m_profilerModelManager->aggregateTraces()) diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp index ec1fe76e1d7..83b70235595 100644 --- a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp @@ -24,6 +24,8 @@ ****************************************************************************/ #include "qmlprofilertracefile.h" +#include "qmlprofilernotesmodel.h" +#include "qmlprofilerconstants.h" #include @@ -114,13 +116,7 @@ static QString qmlTypeAsString(Message message, RangeType rangeType) return QString::number((int)rangeType); } - -QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) : - QObject(parent), - m_traceStart(-1), - m_traceEnd(-1), - m_future(0), - m_loadedFeatures(0) +QmlProfilerTraceFile::QmlProfilerTraceFile(QObject *parent) : Timeline::TimelineTraceFile(parent) { static int meta[] = { qRegisterMetaType >(), @@ -130,16 +126,25 @@ QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) : Q_UNUSED(meta); } -void QmlProfilerFileReader::setFuture(QFutureInterface *future) +void QmlProfilerTraceFile::load(QIODevice *device) { - m_future = future; - if (m_future) { - m_future->setProgressRange(0, 1000); - m_future->setProgressValue(0); - } + const QFile *file = qobject_cast(device); + if (file && file->fileName().endsWith(Constants::QtdFileExtension)) + loadQtd(device); + else + loadQzt(device); } -void QmlProfilerFileReader::loadQtd(QIODevice *device) +void QmlProfilerTraceFile::save(QIODevice *device) +{ + const QFile *file = qobject_cast(device); + if (file && file->fileName().endsWith(Constants::QtdFileExtension)) + saveQtd(device); + else + saveQzt(device); +} + +void QmlProfilerTraceFile::loadQtd(QIODevice *device) { QXmlStreamReader stream(device); @@ -158,14 +163,13 @@ void QmlProfilerFileReader::loadQtd(QIODevice *device) else validVersion = false; if (attributes.hasAttribute(_("traceStart"))) - m_traceStart = attributes.value(_("traceStart")).toLongLong(); + setTraceStart(attributes.value(_("traceStart")).toLongLong()); if (attributes.hasAttribute(_("traceEnd"))) - m_traceEnd = attributes.value(_("traceEnd")).toLongLong(); + setTraceEnd(attributes.value(_("traceEnd")).toLongLong()); } if (elementName == _("eventData")) { loadEventTypes(stream); - emit typesLoaded(m_eventTypes); break; } @@ -176,7 +180,6 @@ void QmlProfilerFileReader::loadQtd(QIODevice *device) if (elementName == _("noteData")) { loadNotes(stream); - emit notesLoaded(m_notes); break; } @@ -194,7 +197,7 @@ void QmlProfilerFileReader::loadQtd(QIODevice *device) emit success(); } -void QmlProfilerFileReader::loadQzt(QIODevice *device) +void QmlProfilerTraceFile::loadQzt(QIODevice *device) { QDataStream stream(device); stream.setVersion(QDataStream::Qt_5_5); @@ -215,44 +218,48 @@ void QmlProfilerFileReader::loadQzt(QIODevice *device) } stream.setVersion(dataStreamVersion); - stream >> m_traceStart >> m_traceEnd; + qint64 traceStart, traceEnd; + stream >> traceStart >> traceEnd; + setTraceStart(traceStart); + setTraceEnd(traceEnd); QBuffer buffer; QDataStream bufferStream(&buffer); bufferStream.setVersion(dataStreamVersion); QByteArray data; - updateProgress(device); + setDeviceProgress(device); if (!isCanceled()) { stream >> data; buffer.setData(qUncompress(data)); buffer.open(QIODevice::ReadOnly); + QVector eventTypes; quint32 numEventTypes; bufferStream >> numEventTypes; if (numEventTypes > std::numeric_limits::max()) { emit error(tr("Excessive number of event types: %1").arg(numEventTypes)); return; } - QTC_ASSERT(m_eventTypes.isEmpty(), m_eventTypes.clear()); - m_eventTypes.reserve(static_cast(numEventTypes)); + eventTypes.reserve(static_cast(numEventTypes)); QmlEventType type; for (int typeId = 0; typeId < static_cast(numEventTypes); ++typeId) { bufferStream >> type; - m_eventTypes.append(type); + eventTypes.append(type); } buffer.close(); - emit typesLoaded(m_eventTypes); - updateProgress(device); + modelManager()->addEventTypes(eventTypes); + setDeviceProgress(device); } if (!isCanceled()) { stream >> data; buffer.setData(qUncompress(data)); buffer.open(QIODevice::ReadOnly); - bufferStream >> m_notes; + QVector notes; + bufferStream >> notes; buffer.close(); - emit notesLoaded(m_notes); - updateProgress(device); + qmlNotes()->setNotes(notes); + setDeviceProgress(device); } QVector eventBuffer; @@ -264,11 +271,11 @@ void QmlProfilerFileReader::loadQzt(QIODevice *device) QmlEvent event; bufferStream >> event; 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())); return; } - m_loadedFeatures |= (1ULL << m_eventTypes[event.typeIndex()].feature()); + addFeature(modelManager()->eventType(event.typeIndex()).feature()); if (event.timestamp() < 0) event.setTimestamp(0); } else if (bufferStream.status() == QDataStream::ReadPastEnd) { @@ -281,28 +288,35 @@ void QmlProfilerFileReader::loadQzt(QIODevice *device) } eventBuffer.append(event); } - emit qmlEventsLoaded(eventBuffer); + modelManager()->addEvents(eventBuffer); eventBuffer.clear(); buffer.close(); - updateProgress(device); + setDeviceProgress(device); } if (isCanceled()) { emit canceled(); } else { - emit qmlEventsLoaded(eventBuffer); + modelManager()->addEvents(eventBuffer); emit success(); } } -quint64 QmlProfilerFileReader::loadedFeatures() const +void QmlProfilerTraceFile::addEventsProgress(qint64 timestamp) { - return m_loadedFeatures; + addProgressValue(static_cast(timestamp) / static_cast(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); + QVector eventTypes; int typeIndex = -1; @@ -330,7 +344,7 @@ void QmlProfilerFileReader::loadEventTypes(QXmlStreamReader &stream) switch (token) { case QXmlStreamReader::StartElement: { if (elementName == _("event")) { - updateProgress(stream.device()); + setDeviceProgress(stream.device()); clearType(); const QXmlStreamAttributes attributes = stream.attributes(); @@ -407,20 +421,21 @@ void QmlProfilerFileReader::loadEventTypes(QXmlStreamReader &stream) case QXmlStreamReader::EndElement: { if (elementName == _("event")) { if (typeIndex >= 0) { - if (typeIndex >= m_eventTypes.size()) - m_eventTypes.resize(typeIndex + 1); + if (typeIndex >= eventTypes.length()) + eventTypes.resize(typeIndex + 1); QmlEventType type(messageAndRange.first, messageAndRange.second, detailType, QmlEventLocation(filename, line, column), data, displayName); - m_eventTypes[typeIndex] = type; - quint8 feature = type.feature(); + eventTypes[typeIndex] = type; + const quint8 feature = type.feature(); if (feature != MaximumProfileFeature) - m_loadedFeatures |= (1ULL << static_cast(feature)); + addFeature(feature); } break; } if (elementName == _("eventData")) { // done reading eventData + modelManager()->addEventTypes(eventTypes); return; } break; @@ -497,7 +512,7 @@ QVector EventList::finalize() return result; } -void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream) +void QmlProfilerTraceFile::loadEvents(QXmlStreamReader &stream) { QTC_ASSERT(stream.name() == _("profilerDataModel"), return); EventList events; @@ -510,7 +525,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream) switch (token) { case QXmlStreamReader::StartElement: { if (elementName == _("range")) { - updateProgress(stream.device()); + setDeviceProgress(stream.device()); QmlEvent event; const QXmlStreamAttributes attributes = stream.attributes(); @@ -574,7 +589,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream) case QXmlStreamReader::EndElement: { if (elementName == _("profilerDataModel")) { // done reading profilerDataModel - emit qmlEventsLoaded(events.finalize()); + modelManager()->addEvents(events.finalize()); return; } break; @@ -584,7 +599,7 @@ void QmlProfilerFileReader::loadEvents(QXmlStreamReader &stream) } } -void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream) +void QmlProfilerTraceFile::loadNotes(QXmlStreamReader &stream) { QmlNote currentNote; while (!stream.atEnd() && !stream.hasError() && !isCanceled()) { @@ -595,7 +610,7 @@ void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream) switch (token) { case QXmlStreamReader::StartElement: { if (elementName == _("note")) { - updateProgress(stream.device()); + setDeviceProgress(stream.device()); QXmlStreamAttributes attrs = stream.attributes(); int collapsedRow = attrs.hasAttribute(_("collapsedRow")) ? attrs.value(_("collapsedRow")).toInt() : -1; @@ -613,7 +628,7 @@ void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream) } case QXmlStreamReader::EndElement: { if (elementName == _("note")) { - m_notes.append(currentNote); + qmlNotes()->addNote(currentNote); } else if (elementName == _("noteData")) { return; } @@ -625,55 +640,7 @@ void QmlProfilerFileReader::loadNotes(QXmlStreamReader &stream) } } -void QmlProfilerFileReader::updateProgress(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 ¬es) -{ - m_notes = notes; -} - -void QmlProfilerFileWriter::setFuture(QFutureInterface *future) -{ - m_future = future; - if (m_future) { - m_future->setProgressRange(0, ProgressTotal); - m_future->setProgressValue(0); - } -} - -void QmlProfilerFileWriter::saveQtd(QIODevice *device) +void QmlProfilerTraceFile::saveQtd(QIODevice *device) { QXmlStreamWriter stream(device); @@ -683,15 +650,16 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device) stream.writeStartElement(_("trace")); stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION)); - stream.writeAttribute(_("traceStart"), QString::number(m_startTime)); - stream.writeAttribute(_("traceEnd"), QString::number(m_endTime)); + stream.writeAttribute(_("traceStart"), QString::number(traceStart())); + stream.writeAttribute(_("traceEnd"), QString::number(traceEnd())); stream.writeStartElement(_("eventData")); - stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime)); - for (int typeIndex = 0, end = m_modelManager->numEventTypes(); - typeIndex < end && !isCanceled(); ++typeIndex) { + stream.writeAttribute(_("totalTime"), QString::number(measuredTime())); + const QmlProfilerModelManager *manager = modelManager(); + 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.writeAttribute(_("index"), QString::number(typeIndex)); @@ -734,210 +702,202 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device) } stream.writeEndElement(); } - updateProgress(ProgressTypes); + addStageProgress(ProgressTypes); stream.writeEndElement(); // eventData - if (!isCanceled()) { - stream.writeStartElement(_("profilerDataModel")); - - QStack stack; - const bool success = m_modelManager->replayEvents( - -1, -1, [this, &stack, &stream](const QmlEvent &event, - const QmlEventType &type) { - if (isCanceled()) - return; - - if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) { - stack.push(event); - return; - } - - stream.writeStartElement(_("range")); - if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeEnd) { - QmlEvent start = stack.pop(); - stream.writeAttribute(_("startTime"), QString::number(start.timestamp())); - stream.writeAttribute(_("duration"), - QString::number(event.timestamp() - start.timestamp())); - } else { - stream.writeAttribute(_("startTime"), QString::number(event.timestamp())); - } - - stream.writeAttribute(_("eventIndex"), QString::number(event.typeIndex())); - - if (type.message() == Event) { - if (type.detailType() == AnimationFrame) { - // special: animation event - stream.writeAttribute(_("framerate"), QString::number(event.number(0))); - stream.writeAttribute(_("animationcount"), - QString::number(event.number(1))); - stream.writeAttribute(_("thread"), QString::number(event.number(2))); - } else if (type.detailType() == Key || type.detailType() == Mouse) { - // special: input event - stream.writeAttribute(_("type"), QString::number(event.number(0))); - stream.writeAttribute(_("data1"), QString::number(event.number(1))); - stream.writeAttribute(_("data2"), QString::number(event.number(2))); - } - } - - // special: pixmap cache event - if (type.message() == PixmapCacheEvent) { - if (type.detailType() == PixmapSizeKnown) { - stream.writeAttribute(_("width"), QString::number(event.number(0))); - stream.writeAttribute(_("height"), QString::number(event.number(1))); - } - - if (type.detailType() == PixmapReferenceCountChanged - || type.detailType() == PixmapCacheCountChanged) - stream.writeAttribute(_("refCount"), QString::number(event.number(2))); - } - - if (type.message() == SceneGraphFrame) { - // special: scenegraph frame events - for (int i = 0; i < 5; ++i) { - qint64 number = event.number(i); - if (number <= 0) - continue; - stream.writeAttribute(QString::fromLatin1("timing%1").arg(i + 1), - QString::number(number)); - } - } - - // special: memory allocation event - if (type.message() == MemoryAllocation) - stream.writeAttribute(_("amount"), QString::number(event.number(0))); - - if (type.message() == DebugMessage) - stream.writeAttribute(_("text"), event.string()); - - stream.writeEndElement(); - - // Update the progress roughly every 4k events. It doesn't have to be precise. - if ((event.timestamp() & 0xfff) == 0) - updateProgress(event.timestamp()); - }); - if (!success) { - emit error(tr("Could not re-read events from temporary trace file. Saving failed.")); - return; - } - - stream.writeEndElement(); // profilerDataModel - } - - if (!isCanceled()) { - stream.writeStartElement(_("noteData")); - for (int noteIndex = 0; noteIndex < m_notes.size() && !isCanceled(); ++noteIndex) { - - const QmlNote ¬e = m_notes[noteIndex]; - stream.writeStartElement(_("note")); - stream.writeAttribute(_("startTime"), QString::number(note.startTime())); - stream.writeAttribute(_("duration"), QString::number(note.duration())); - stream.writeAttribute(_("eventIndex"), QString::number(note.typeIndex())); - stream.writeAttribute(_("collapsedRow"), QString::number(note.collapsedRow())); - stream.writeCharacters(note.text()); - stream.writeEndElement(); // note - } - stream.writeEndElement(); // noteData - updateProgress(ProgressNotes); - } - - stream.writeEndElement(); // trace - stream.writeEndDocument(); - if (isCanceled()) { emit canceled(); - } else if (stream.hasError()) { - emit error(tr("Error writing trace file.")); - } else { - emit success(); + return; } + + QStack stack; + qint64 lastProgressTimestamp = traceStart(); + modelManager()->replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { + if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeStart) { + stack.push(event); + return; + } + + stream.writeStartElement(_("range")); + if (type.rangeType() != MaximumRangeType && event.rangeStage() == RangeEnd) { + QmlEvent start = stack.pop(); + stream.writeAttribute(_("startTime"), QString::number(start.timestamp())); + stream.writeAttribute(_("duration"), + QString::number(event.timestamp() - start.timestamp())); + } else { + stream.writeAttribute(_("startTime"), QString::number(event.timestamp())); + } + + stream.writeAttribute(_("eventIndex"), QString::number(event.typeIndex())); + + if (type.message() == Event) { + if (type.detailType() == AnimationFrame) { + // special: animation event + stream.writeAttribute(_("framerate"), QString::number(event.number(0))); + stream.writeAttribute(_("animationcount"), + QString::number(event.number(1))); + stream.writeAttribute(_("thread"), QString::number(event.number(2))); + } else if (type.detailType() == Key || type.detailType() == Mouse) { + // special: input event + stream.writeAttribute(_("type"), QString::number(event.number(0))); + stream.writeAttribute(_("data1"), QString::number(event.number(1))); + stream.writeAttribute(_("data2"), QString::number(event.number(2))); + } + } + + // special: pixmap cache event + if (type.message() == PixmapCacheEvent) { + if (type.detailType() == PixmapSizeKnown) { + stream.writeAttribute(_("width"), QString::number(event.number(0))); + stream.writeAttribute(_("height"), QString::number(event.number(1))); + } + + if (type.detailType() == PixmapReferenceCountChanged + || type.detailType() == PixmapCacheCountChanged) + stream.writeAttribute(_("refCount"), QString::number(event.number(2))); + } + + if (type.message() == SceneGraphFrame) { + // special: scenegraph frame events + for (int i = 0; i < 5; ++i) { + qint64 number = event.number(i); + if (number <= 0) + continue; + stream.writeAttribute(QString::fromLatin1("timing%1").arg(i + 1), + QString::number(number)); + } + } + + // special: memory allocation event + if (type.message() == MemoryAllocation) + stream.writeAttribute(_("amount"), QString::number(event.number(0))); + + if (type.message() == DebugMessage) + stream.writeAttribute(_("text"), event.string()); + + stream.writeEndElement(); + + if (isProgressUpdateNeeded()) { + addEventsProgress(event.timestamp() - lastProgressTimestamp); + lastProgressTimestamp = event.timestamp(); + } + }, [&stream](){ + stream.writeStartElement(_("profilerDataModel")); + }, [this, &stream]() { + stream.writeEndElement(); // profilerDataModel + if (!isCanceled()) { + stream.writeStartElement(_("noteData")); + const QVector ¬es = qmlNotes()->notes(); + for (int noteIndex = 0; noteIndex < notes.length() && !isCanceled(); ++noteIndex) { + const QmlNote ¬e = notes[noteIndex]; + stream.writeStartElement(_("note")); + stream.writeAttribute(_("startTime"), QString::number(note.startTime())); + stream.writeAttribute(_("duration"), QString::number(note.duration())); + stream.writeAttribute(_("eventIndex"), QString::number(note.typeIndex())); + stream.writeAttribute(_("collapsedRow"), QString::number(note.collapsedRow())); + stream.writeCharacters(note.text()); + stream.writeEndElement(); // note + } + stream.writeEndElement(); // noteData + addStageProgress(ProgressNotes); + } + + stream.writeEndElement(); // trace + stream.writeEndDocument(); + + if (isCanceled()) + emit canceled(); + else if (stream.hasError()) + emit error(tr("Error writing trace file.")); + else + 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 << QByteArray("QMLPROFILER"); stream << static_cast(QDataStream::Qt_DefaultCompiledVersion); stream.setVersion(QDataStream::Qt_DefaultCompiledVersion); - stream << m_startTime << m_endTime; + stream << traceStart() << traceEnd(); QBuffer buffer; QDataStream bufferStream(&buffer); buffer.open(QIODevice::WriteOnly); if (!isCanceled()) { - const int numEventTypes = m_modelManager->numEventTypes(); + const QmlProfilerModelManager *manager = modelManager(); + const int numEventTypes = manager->numEventTypes(); bufferStream << static_cast(numEventTypes); for (int typeId = 0; typeId < numEventTypes; ++typeId) - bufferStream << m_modelManager->eventType(typeId); + bufferStream << manager->eventType(typeId); stream << qCompress(buffer.data()); buffer.close(); buffer.buffer().clear(); - updateProgress(ProgressTypes); + addStageProgress(ProgressTypes); } if (!isCanceled()) { buffer.open(QIODevice::WriteOnly); - bufferStream << m_notes; + bufferStream << qmlNotes()->notes(); stream << qCompress(buffer.data()); buffer.close(); buffer.buffer().clear(); - updateProgress(ProgressNotes); - } - - if (!isCanceled()) { - buffer.open(QIODevice::WriteOnly); - const bool success = m_modelManager->replayEvents( - -1, -1, [this, &stream, &buffer, &bufferStream](const QmlEvent &event, - const QmlEventType &type) { - Q_UNUSED(type); - bufferStream << event; - // 32MB buffer should be plenty for efficient compression - if (buffer.data().length() > (1 << 25)) { - stream << qCompress(buffer.data()); - buffer.close(); - buffer.buffer().clear(); - if (isCanceled()) - return; - buffer.open(QIODevice::WriteOnly); - updateProgress(event.timestamp()); - } - }); - if (!success) { - emit error(tr("Could not re-read events from temporary trace file. Saving failed.")); - return; - } + addStageProgress(ProgressNotes); } if (isCanceled()) { emit canceled(); - } else { - stream << qCompress(buffer.data()); - buffer.close(); - buffer.buffer().clear(); - updateProgress(m_endTime); - emit success(); - } -} - -void QmlProfilerFileWriter::updateProgress(qint64 timestamp) -{ - if (!m_future) 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); } + + qint64 lastProgressTimestamp = traceStart(); + modelManager()->replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { + Q_UNUSED(type); + bufferStream << event; + // 32MB buffer should be plenty for efficient compression + if (buffer.data().length() > (1 << 25)) { + stream << qCompress(buffer.data()); + buffer.close(); + buffer.buffer().clear(); + buffer.open(QIODevice::WriteOnly); + if (isProgressUpdateNeeded()) { + addEventsProgress(event.timestamp() - lastProgressTimestamp); + lastProgressTimestamp = event.timestamp(); + } + } + }, [&]() { + buffer.open(QIODevice::WriteOnly); + }, [&]() { + if (isCanceled()) { + emit canceled(); + } else { + stream << qCompress(buffer.data()); + buffer.close(); + buffer.buffer().clear(); + addEventsProgress(traceEnd() - lastProgressTimestamp); + emit success(); + } + }, [this](const QString &message) { + emit error(tr("Could not re-read events from temporary trace file: %s\nSaving failed.") + .arg(message)); + }, future()); } -bool QmlProfilerFileWriter::isCanceled() const +QmlProfilerModelManager *QmlProfilerTraceFile::modelManager() { - return m_future && m_future->isCanceled(); + return static_cast(traceManager()); +} + +QmlProfilerNotesModel *QmlProfilerTraceFile::qmlNotes() +{ + return static_cast(notes()); } } // namespace Internal diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.h b/src/plugins/qmlprofiler/qmlprofilertracefile.h index 929858d0316..0d9321a5290 100644 --- a/src/plugins/qmlprofiler/qmlprofilertracefile.h +++ b/src/plugins/qmlprofiler/qmlprofilertracefile.h @@ -32,6 +32,8 @@ #include "qmlevent.h" #include "qmlprofilermodelmanager.h" +#include + #include #include #include @@ -44,81 +46,36 @@ QT_FORWARD_DECLARE_CLASS(QXmlStreamReader) namespace QmlProfiler { namespace Internal { -class QmlProfilerFileReader : public QObject +class QmlProfilerTraceFile : public Timeline::TimelineTraceFile { Q_OBJECT public: - explicit QmlProfilerFileReader(QObject *parent = 0); + explicit QmlProfilerTraceFile(QObject *parent = nullptr); - void setFuture(QFutureInterface *future); - - 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 &types); - void notesLoaded(const QVector ¬es); - void qmlEventsLoaded(const QVector &event); - void error(const QString &error); - void success(); - void canceled(); + void load(QIODevice *device) final; + void save(QIODevice *device) final; 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 loadEvents(QXmlStreamReader &reader); void loadNotes(QXmlStreamReader &reader); - void updateProgress(QIODevice *device); - bool isCanceled() const; - - qint64 m_traceStart, m_traceEnd; - QFutureInterface *m_future; - QVector m_eventTypes; - QVector 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 ¬es); - void setFuture(QFutureInterface *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 { - ProgressTypes = -128, - ProgressNotes = -32, - ProgressEvents = 1024, - ProgressTotal = ProgressEvents - ProgressTypes - ProgressNotes + ProgressTypes = 128, + ProgressNotes = 32, + ProgressEvents = MaximumProgress - ProgressTypes - ProgressNotes - MinimumProgress, }; - qint64 m_startTime, m_endTime, m_measuredTime; - QFutureInterface *m_future; - const QmlProfilerModelManager *m_modelManager; - QVector m_notes; + void addEventsProgress(qint64 timestamp); + void addStageProgress(ProgressValues stage); }; diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp index ea6e53406d3..aba480c5700 100644 --- a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp @@ -425,7 +425,7 @@ bool TraceViewFindSupport::findOne(const QString &txt, Core::FindFlags findFlags bool forwardSearch = !(findFlags & Core::FindBackward); int increment = forwardSearch ? +1 : -1; int current = forwardSearch ? start : start - 1; - QmlProfilerNotesModel *model = m_modelManager->notesModel(); + Timeline::TimelineNotesModel *model = m_modelManager->notesModel(); while (current >= 0 && current < model->count()) { QTextDocument doc(model->text(current)); // for automatic handling of WholeWords option if (!doc.find(regexp, 0, flags).isNull()) { diff --git a/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp b/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp index ab14fbae118..b85aa561352 100644 --- a/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp +++ b/src/plugins/qmlprofiler/scenegraphtimelinemodel.cpp @@ -219,6 +219,7 @@ void SceneGraphTimelineModel::finalize() { computeNesting(); flattenLoads(); + QmlProfilerTimelineModel::finalize(); } void SceneGraphTimelineModel::flattenLoads() diff --git a/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp b/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp index 71a68ded907..8948e5dfbfd 100644 --- a/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/debugmessagesmodel_test.cpp @@ -39,7 +39,7 @@ DebugMessagesModelTest::DebugMessagesModelTest(QObject *parent) : void DebugMessagesModelTest::initTestCase() { - manager.startAcquiring(); + manager.initialize(); QmlEvent event; event.setTypeIndex(-1); diff --git a/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp b/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp index c4abc490849..b3ecb940141 100644 --- a/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp @@ -45,7 +45,7 @@ int FlameGraphModelTest::generateData(QmlProfilerModelManager *manager, int rangeModelId = rangeModel->modelId(); manager->notesModel()->addTimelineModel(rangeModel); - manager->startAcquiring(); + manager->initialize(); QmlEvent event; event.setTypeIndex(-1); @@ -87,7 +87,8 @@ int FlameGraphModelTest::generateData(QmlProfilerModelManager *manager, manager->finalize(); - manager->notesModel()->setNotes(QVector({QmlNote(0, 2, 1, 20, "dings")})); + static_cast(manager->notesModel()) + ->setNotes(QVector({QmlNote(0, 2, 1, 20, "dings")})); manager->notesModel()->restore(); return rangeModelId; @@ -220,7 +221,7 @@ void FlameGraphModelTest::testNotes() void FlameGraphModelTest::cleanupTestCase() { - manager.clear(); + manager.clearAll(); QCOMPARE(model.rowCount(), 0); } diff --git a/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp b/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp index 732128ab5a5..afe31bd5763 100644 --- a/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp +++ b/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp @@ -160,7 +160,7 @@ void FlameGraphViewTest::testContextMenu() void FlameGraphViewTest::cleanupTestCase() { - manager.clear(); + manager.clearAll(); } } // namespace Internal diff --git a/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp b/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp index b92eca9f931..e61058eb55f 100644 --- a/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/inputeventsmodel_test.cpp @@ -48,7 +48,7 @@ InputEventsModelTest::InputEventsModelTest(QObject *parent) : void InputEventsModelTest::initTestCase() { - manager.startAcquiring(); + manager.initialize(); QmlEvent event; for (int i = 0; i < 10; ++i) { diff --git a/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp b/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp index 8e7db17b7c0..e83a62333fd 100644 --- a/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/memoryusagemodel_test.cpp @@ -36,7 +36,7 @@ MemoryUsageModelTest::MemoryUsageModelTest(QObject *parent) : QObject(parent), void MemoryUsageModelTest::initTestCase() { - manager.startAcquiring(); + manager.initialize(); qint64 timestamp = 0; @@ -233,7 +233,7 @@ void MemoryUsageModelTest::testAccepted() void MemoryUsageModelTest::cleanupTestCase() { - manager.clear(); + manager.clearAll(); QCOMPARE(model.count(), 0); } diff --git a/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp b/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp index 0f4a0079b75..2a962d38201 100644 --- a/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/pixmapcachemodel_test.cpp @@ -37,7 +37,7 @@ PixmapCacheModelTest::PixmapCacheModelTest(QObject *parent) : QObject(parent), void PixmapCacheModelTest::initTestCase() { - manager.startAcquiring(); + manager.initialize(); manager.decreaseTraceStart(1); manager.increaseTraceEnd(300); @@ -313,7 +313,7 @@ void PixmapCacheModelTest::testLabels() void PixmapCacheModelTest::cleanupTestCase() { - manager.clear(); + manager.clearAll(); QCOMPARE(model.count(), 0); } diff --git a/src/plugins/qmlprofiler/tests/qmlprofileranimationsmodel_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofileranimationsmodel_test.cpp index 98e3883c677..e7adcee4eff 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofileranimationsmodel_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofileranimationsmodel_test.cpp @@ -42,7 +42,7 @@ static int frameRate(int i) void QmlProfilerAnimationsModelTest::initTestCase() { - manager.startAcquiring(); + manager.initialize(); QmlEventType type(Event, MaximumRangeType, AnimationFrame); QmlEvent event; diff --git a/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp index 2fc27ae4f92..216432f89d3 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilertraceclient_test.cpp @@ -69,6 +69,7 @@ void QmlProfilerTraceClientTest::testMessageReceived() } traceClient.stateChanged(QmlDebug::QmlDebugClient::NotConnected); + QFutureInterface future; modelManager.replayEvents(-1, -1, [&](const QmlEvent &event, const QmlEventType &type) { qint64 timestamp; qint32 message; @@ -77,10 +78,19 @@ void QmlProfilerTraceClientTest::testMessageReceived() QCOMPARE(event.timestamp(), timestamp); QCOMPARE(type.message(), static_cast(message)); QCOMPARE(type.rangeType(), static_cast(rangeType)); - }); - - modelManager.clear(); - traceClient.clear(); + }, nullptr, [this]() { + modelManager.clearAll(); + 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()); diff --git a/src/plugins/qmlprofiler/tests/qmlprofilertraceview_test.cpp b/src/plugins/qmlprofiler/tests/qmlprofilertraceview_test.cpp index 172d969a521..9c101aca846 100644 --- a/src/plugins/qmlprofiler/tests/qmlprofilertraceview_test.cpp +++ b/src/plugins/qmlprofiler/tests/qmlprofilertraceview_test.cpp @@ -37,15 +37,15 @@ QmlProfilerTraceViewTest::QmlProfilerTraceViewTest(QObject *parent) : void QmlProfilerTraceViewTest::testStateChanges() { // Standard acquire-process-clear work flow - modelManager.startAcquiring(); + modelManager.initialize(); QVERIFY(traceView.isSuspended()); modelManager.finalize(); QVERIFY(!traceView.isSuspended()); - modelManager.clear(); + modelManager.clearAll(); QVERIFY(!traceView.isSuspended()); // Restrict to range - modelManager.startAcquiring(); + modelManager.initialize(); QVERIFY(traceView.isSuspended()); modelManager.finalize(); QVERIFY(!traceView.isSuspended()); @@ -53,13 +53,13 @@ void QmlProfilerTraceViewTest::testStateChanges() QVERIFY(!traceView.isSuspended()); modelManager.restrictToRange(-1, -1); QVERIFY(!traceView.isSuspended()); - modelManager.clear(); + modelManager.clearAll(); QVERIFY(!traceView.isSuspended()); // Abort Acquiring - modelManager.startAcquiring(); + modelManager.initialize(); QVERIFY(traceView.isSuspended()); - modelManager.clear(); + modelManager.clearAll(); QVERIFY(!traceView.isSuspended()); }