forked from qt-creator/qt-creator
QmlProfiler: Move parts of model manager and trace file to Timeline
This way we can use the trace file loading and saving mechanism for other profilers. Change-Id: I98ec1cdde6f7abcea152cabf72e64d4e696dfa59 Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -30,9 +30,9 @@
|
||||
#include "qmlprofilerdetailsrewriter.h"
|
||||
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
#include <timeline/tracestashfile.h>
|
||||
#include <utils/runextensions.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/temporaryfile.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
@@ -43,7 +43,6 @@
|
||||
#include <functional>
|
||||
|
||||
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<ProfileFeature, QVector<EventLoader> > eventLoaders;
|
||||
QVector<Finalizer> finalizers;
|
||||
Internal::QmlProfilerTextMarkModel *textMarkModel = nullptr;
|
||||
|
||||
QVector<QmlEventType> eventTypes;
|
||||
QmlProfilerDetailsRewriter *detailsRewriter = nullptr;
|
||||
Internal::QmlProfilerDetailsRewriter *detailsRewriter = nullptr;
|
||||
|
||||
Utils::TemporaryFile file;
|
||||
QDataStream eventStream;
|
||||
Timeline::TraceStashFile<QmlEvent> 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<const QmlEvent &>(event),
|
||||
static_cast<const QmlEventType &>(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<QmlEvent> &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<QmlEventType> &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<void> &future) const
|
||||
{
|
||||
replayEvents(rangeStart, rangeEnd, static_cast<QmlEventLoader>(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<void> &future) const
|
||||
{
|
||||
QStack<QmlEvent> stack;
|
||||
QmlEvent event;
|
||||
QFile file(d->file.fileName());
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
if (initializer)
|
||||
initializer();
|
||||
|
||||
QDataStream stream(&file);
|
||||
QStack<QmlEvent> 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<ProfileFeature>(type.feature()))) {
|
||||
loader(event, type);
|
||||
switch (result) {
|
||||
case Timeline::TraceStashFile<QmlEvent>::ReplaySuccess:
|
||||
if (finalizer)
|
||||
finalizer();
|
||||
break;
|
||||
case Timeline::TraceStashFile<QmlEvent>::ReplayOpenFailed:
|
||||
if (errorHandler)
|
||||
errorHandler(tr("Could not re-open temporary trace file"));
|
||||
break;
|
||||
case Timeline::TraceStashFile<QmlEvent>::ReplayLoadFailed:
|
||||
if (errorHandler)
|
||||
errorHandler(tr("Could not load events from temporary trace file"));
|
||||
break;
|
||||
case Timeline::TraceStashFile<QmlEvent>::ReplayReadPastEnd:
|
||||
if (errorHandler)
|
||||
errorHandler(tr("Read past end in temporary trace file"));
|
||||
break;
|
||||
}
|
||||
++numLoadedEvents;
|
||||
}
|
||||
|
||||
static QString getDisplayName(const QmlEventType &event)
|
||||
@@ -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<ProfileFeature>(feature)].append(eventLoader);
|
||||
}
|
||||
|
||||
d->finalizers.append(finalizer);
|
||||
}
|
||||
|
||||
quint64 QmlProfilerModelManager::availableFeatures() const
|
||||
{
|
||||
return d->availableFeatures;
|
||||
}
|
||||
|
||||
quint64 QmlProfilerModelManager::visibleFeatures() const
|
||||
{
|
||||
return d->visibleFeatures;
|
||||
}
|
||||
|
||||
void QmlProfilerModelManager::setVisibleFeatures(quint64 features)
|
||||
{
|
||||
if (d->visibleFeatures != features) {
|
||||
d->visibleFeatures = features;
|
||||
emit visibleFeaturesChanged(d->visibleFeatures);
|
||||
}
|
||||
}
|
||||
|
||||
quint64 QmlProfilerModelManager::recordedFeatures() const
|
||||
{
|
||||
return d->recordedFeatures;
|
||||
}
|
||||
|
||||
void QmlProfilerModelManager::setRecordedFeatures(quint64 features)
|
||||
{
|
||||
if (d->recordedFeatures != features) {
|
||||
d->recordedFeatures = features;
|
||||
emit recordedFeaturesChanged(d->recordedFeatures);
|
||||
}
|
||||
}
|
||||
|
||||
bool QmlProfilerModelManager::aggregateTraces() const
|
||||
{
|
||||
return d->aggregateTraces;
|
||||
}
|
||||
|
||||
void QmlProfilerModelManager::setAggregateTraces(bool aggregateTraces)
|
||||
{
|
||||
d->aggregateTraces = aggregateTraces;
|
||||
}
|
||||
|
||||
|
||||
const char *QmlProfilerModelManager::featureName(ProfileFeature feature)
|
||||
{
|
||||
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<void> result = Utils::runAsync([file, writer] (QFutureInterface<void> &future) {
|
||||
writer->setFuture(&future);
|
||||
if (file->fileName().endsWith(QLatin1String(Constants::QtdFileExtension)))
|
||||
writer->saveQtd(file);
|
||||
else
|
||||
writer->saveQzt(file);
|
||||
writer->deleteLater();
|
||||
});
|
||||
|
||||
Core::ProgressManager::addTask(result, tr("Saving Trace Data"), Constants::TASK_SAVE,
|
||||
Core::ProgressManager::ShowInApplicationIcon);
|
||||
}
|
||||
|
||||
void QmlProfilerModelManager::load(const QString &filename)
|
||||
{
|
||||
bool isQtd = filename.endsWith(QLatin1String(Constants::QtdFileExtension));
|
||||
QFile *file = new QFile(filename, this);
|
||||
if (!file->open(isQtd ? (QIODevice::ReadOnly | QIODevice::Text) : QIODevice::ReadOnly)) {
|
||||
emit error(tr("Could not open %1 for reading.").arg(filename));
|
||||
delete file;
|
||||
emit loadFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
clear();
|
||||
setState(AcquiringData);
|
||||
QmlProfilerFileReader *reader = new QmlProfilerFileReader(this);
|
||||
|
||||
connect(reader, &QObject::destroyed, this, &QmlProfilerModelManager::loadFinished,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
connect(reader, &QmlProfilerFileReader::typesLoaded,
|
||||
this, &QmlProfilerModelManager::addEventTypes);
|
||||
|
||||
connect(reader, &QmlProfilerFileReader::notesLoaded,
|
||||
d->notesModel, &QmlProfilerNotesModel::setNotes);
|
||||
|
||||
connect(reader, &QmlProfilerFileReader::qmlEventsLoaded,
|
||||
this, &QmlProfilerModelManager::addEvents);
|
||||
|
||||
connect(reader, &QmlProfilerFileReader::success, this, [this, reader]() {
|
||||
if (reader->traceStart() >= 0)
|
||||
decreaseTraceStart(reader->traceStart());
|
||||
if (reader->traceEnd() >= 0)
|
||||
increaseTraceEnd(reader->traceEnd());
|
||||
setRecordedFeatures(reader->loadedFeatures());
|
||||
delete reader;
|
||||
finalize();
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
connect(reader, &QmlProfilerFileReader::error, this, [this, reader](const QString &message) {
|
||||
clear();
|
||||
delete reader;
|
||||
emit error(message);
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
connect(reader, &QmlProfilerFileReader::canceled, this, [this, reader]() {
|
||||
clear();
|
||||
delete reader;
|
||||
}, Qt::QueuedConnection);
|
||||
|
||||
QFuture<void> result = Utils::runAsync([isQtd, file, reader] (QFutureInterface<void> &future) {
|
||||
reader->setFuture(&future);
|
||||
if (isQtd)
|
||||
reader->loadQtd(file);
|
||||
else
|
||||
reader->loadQzt(file);
|
||||
file->close();
|
||||
file->deleteLater();
|
||||
});
|
||||
|
||||
Core::ProgressManager::addTask(result, tr("Loading Trace Data"), Constants::TASK_LOAD);
|
||||
}
|
||||
|
||||
void QmlProfilerModelManager::setState(QmlProfilerModelManager::State state)
|
||||
{
|
||||
// It's not an error, we are continuously calling "AcquiringData" for example
|
||||
if (d->state == state)
|
||||
return;
|
||||
|
||||
switch (state) {
|
||||
case ClearingData:
|
||||
QTC_ASSERT(d->state == Done || d->state == Empty || d->state == AcquiringData, /**/);
|
||||
break;
|
||||
case Empty:
|
||||
// if it's not empty, complain but go on
|
||||
QTC_ASSERT(isEmpty(), /**/);
|
||||
break;
|
||||
case AcquiringData:
|
||||
break;
|
||||
case Done:
|
||||
QTC_ASSERT(d->state == AcquiringData || d->state == Empty, return);
|
||||
break;
|
||||
default:
|
||||
emit error(tr("Trying to set unknown state in events list."));
|
||||
break;
|
||||
}
|
||||
|
||||
d->state = state;
|
||||
emit stateChanged();
|
||||
}
|
||||
|
||||
void QmlProfilerModelManager::detailsChanged(int typeId, const QString &newString)
|
||||
{
|
||||
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<QmlNote> 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
|
||||
|
||||
Reference in New Issue
Block a user