Tracing: Simplify saving and loading

We don't need the success and canceled signals from TimelineTraceFile,
as the future produced by runAsync tells us when a job is finished or
canceled. Also, we can just create the QFile inside the runnable to save
some code.

Change-Id: I7d91c60c1f798077573712cf624243e9f5969fd6
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Ulf Hermann
2018-05-09 14:44:40 +02:00
parent 1251eb87bb
commit 3ce528d6af
4 changed files with 61 additions and 111 deletions

View File

@@ -61,6 +61,12 @@ void TimelineTraceFile::setDeviceProgress(QIODevice *device)
* (MaximumProgress - MinimumProgress) / device->size()); * (MaximumProgress - MinimumProgress) / device->size());
} }
void TimelineTraceFile::fail(const QString &message)
{
emit error(message);
m_future.cancel();
}
bool TimelineTraceFile::isCanceled() const bool TimelineTraceFile::isCanceled() const
{ {
return m_future.isCanceled(); return m_future.isCanceled();

View File

@@ -64,6 +64,7 @@ public:
virtual void save(QIODevice *device) = 0; virtual void save(QIODevice *device) = 0;
virtual void load(QIODevice *device) = 0; virtual void load(QIODevice *device) = 0;
void fail(const QString &message);
void setTraceStart(qint64 traceStart) { m_traceStart = traceStart; } void setTraceStart(qint64 traceStart) { m_traceStart = traceStart; }
qint64 traceStart() const { return m_traceStart; } qint64 traceStart() const { return m_traceStart; }
@@ -79,8 +80,6 @@ public:
signals: signals:
void error(const QString &error); void error(const QString &error);
void success();
void canceled();
private: private:
qint64 m_traceStart = -1; qint64 m_traceStart = -1;

View File

@@ -230,100 +230,66 @@ void TimelineTraceManager::finalize()
QFuture<void> TimelineTraceManager::save(const QString &filename) QFuture<void> TimelineTraceManager::save(const QString &filename)
{ {
QFile *file = new QFile(filename);
if (!file->open(QIODevice::WriteOnly)) {
delete file;
return Utils::runAsync([this, filename](QFutureInterface<void> &future) {
future.setProgressRange(0, 1);
future.setProgressValue(1);
emit error(tr("Could not open %1 for writing.").arg(filename));
emit saveFinished();
});
}
TimelineTraceFile *writer = createTraceFile(); TimelineTraceFile *writer = createTraceFile();
writer->setTraceTime(traceStart(), traceEnd(), traceDuration()); writer->setTraceTime(traceStart(), traceEnd(), traceDuration());
writer->setTraceManager(this); writer->setTraceManager(this);
writer->setNotes(d->notesModel); writer->setNotes(d->notesModel);
connect(writer, &QObject::destroyed, this, &TimelineTraceManager::saveFinished, connect(writer, &QObject::destroyed, this, &TimelineTraceManager::saveFinished);
Qt::QueuedConnection); connect(writer, &TimelineTraceFile::error, this, &TimelineTraceManager::error);
connect(writer, &TimelineTraceFile::error, this, [this, file](const QString &message) { return Utils::runAsync([filename, writer] (QFutureInterface<void> &future) {
file->close();
file->remove();
delete file;
if (!message.isEmpty())
emit error(message);
}, Qt::QueuedConnection);
connect(writer, &TimelineTraceFile::success, this, [file]() {
file->close();
delete file;
}, Qt::QueuedConnection);
connect(writer, &TimelineTraceFile::canceled, this, [file]() {
file->close();
file->remove();
delete file;
}, Qt::QueuedConnection);
return Utils::runAsync([file, writer] (QFutureInterface<void> &future) {
writer->setFuture(future); writer->setFuture(future);
writer->save(file); QFile file(filename);
if (file.open(QIODevice::WriteOnly))
writer->save(&file);
else
writer->fail(tr("Could not open %1 for writing.").arg(filename));
if (future.isCanceled())
file.remove();
writer->deleteLater(); writer->deleteLater();
}); });
} }
QFuture<void> TimelineTraceManager::load(const QString &filename) QFuture<void> TimelineTraceManager::load(const QString &filename)
{ {
QFile *file = new QFile(filename, this);
if (!file->open(QIODevice::ReadOnly)) {
delete file;
return Utils::runAsync([this, filename] (QFutureInterface<void> &future) {
future.setProgressRange(0, 1);
future.setProgressValue(1);
emit error(tr("Could not open %1 for reading.").arg(filename));
emit loadFinished();
});
}
clearAll(); clearAll();
initialize(); initialize();
TimelineTraceFile *reader = createTraceFile(); TimelineTraceFile *reader = createTraceFile();
reader->setTraceManager(this); reader->setTraceManager(this);
reader->setNotes(d->notesModel); reader->setNotes(d->notesModel);
connect(reader, &QObject::destroyed, this, &TimelineTraceManager::loadFinished, connect(reader, &QObject::destroyed, this, &TimelineTraceManager::loadFinished);
Qt::QueuedConnection); connect(reader, &TimelineTraceFile::error, this, &TimelineTraceManager::error);
connect(reader, &TimelineTraceFile::success, this, [this, reader]() { QFuture<void> future = Utils::runAsync([filename, reader] (QFutureInterface<void> &future) {
reader->setFuture(future);
QFile file(filename);
if (file.open(QIODevice::ReadOnly))
reader->load(&file);
else
reader->fail(tr("Could not open %1 for reading.").arg(filename));
reader->deleteLater();
});
QFutureWatcher<void> *watcher = new QFutureWatcher<void>(reader);
watcher->setFuture(future);
connect(watcher, &QFutureWatcherBase::canceled, this, &TimelineTraceManager::clearAll);
connect(watcher, &QFutureWatcherBase::finished, this, [this, reader]() {
if (!reader->isCanceled()) {
if (reader->traceStart() >= 0) if (reader->traceStart() >= 0)
decreaseTraceStart(reader->traceStart()); decreaseTraceStart(reader->traceStart());
if (reader->traceEnd() >= 0) if (reader->traceEnd() >= 0)
increaseTraceEnd(reader->traceEnd()); increaseTraceEnd(reader->traceEnd());
finalize(); finalize();
delete reader; }
}, Qt::QueuedConnection);
connect(reader, &TimelineTraceFile::error, this, [this, reader](const QString &message) {
clearAll();
delete reader;
if (!message.isEmpty())
emit error(message);
}, Qt::QueuedConnection);
connect(reader, &TimelineTraceFile::canceled, this, [this, reader]() {
clearAll();
delete reader;
}, Qt::QueuedConnection);
return Utils::runAsync([file, reader] (QFutureInterface<void> &future) {
reader->setFuture(future);
reader->load(file);
file->close();
file->deleteLater();
}); });
return future;
} }
qint64 TimelineTraceManager::traceStart() const qint64 TimelineTraceManager::traceStart() const

View File

@@ -189,12 +189,8 @@ void QmlProfilerTraceFile::loadQtd(QIODevice *device)
} }
} }
if (isCanceled()) if (stream.hasError())
emit canceled(); fail(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
else if (stream.hasError())
emit error(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
else
emit success();
} }
void QmlProfilerTraceFile::loadQzt(QIODevice *device) void QmlProfilerTraceFile::loadQzt(QIODevice *device)
@@ -205,7 +201,7 @@ void QmlProfilerTraceFile::loadQzt(QIODevice *device)
QByteArray magic; QByteArray magic;
stream >> magic; stream >> magic;
if (magic != QByteArray("QMLPROFILER")) { if (magic != QByteArray("QMLPROFILER")) {
emit error(tr("Invalid magic: %1").arg(QLatin1String(magic))); fail(tr("Invalid magic: %1").arg(QLatin1String(magic)));
return; return;
} }
@@ -213,7 +209,7 @@ void QmlProfilerTraceFile::loadQzt(QIODevice *device)
stream >> dataStreamVersion; stream >> dataStreamVersion;
if (dataStreamVersion > QDataStream::Qt_DefaultCompiledVersion) { if (dataStreamVersion > QDataStream::Qt_DefaultCompiledVersion) {
emit error(tr("Unknown data stream version: %1").arg(dataStreamVersion)); fail(tr("Unknown data stream version: %1").arg(dataStreamVersion));
return; return;
} }
stream.setVersion(dataStreamVersion); stream.setVersion(dataStreamVersion);
@@ -237,7 +233,7 @@ void QmlProfilerTraceFile::loadQzt(QIODevice *device)
quint32 numEventTypes; quint32 numEventTypes;
bufferStream >> numEventTypes; bufferStream >> numEventTypes;
if (numEventTypes > quint32(std::numeric_limits<int>::max())) { if (numEventTypes > quint32(std::numeric_limits<int>::max())) {
emit error(tr("Excessive number of event types: %1").arg(numEventTypes)); fail(tr("Excessive number of event types: %1").arg(numEventTypes));
return; return;
} }
@@ -270,7 +266,7 @@ void QmlProfilerTraceFile::loadQzt(QIODevice *device)
bufferStream >> event; bufferStream >> event;
if (bufferStream.status() == QDataStream::Ok) { if (bufferStream.status() == QDataStream::Ok) {
if (event.typeIndex() >= traceManager()->numEventTypes()) { if (event.typeIndex() >= traceManager()->numEventTypes()) {
emit error(tr("Invalid type index %1").arg(event.typeIndex())); fail(tr("Invalid type index %1").arg(event.typeIndex()));
return; return;
} }
addFeature(manager->eventType(event.typeIndex()).feature()); addFeature(manager->eventType(event.typeIndex()).feature());
@@ -279,7 +275,7 @@ void QmlProfilerTraceFile::loadQzt(QIODevice *device)
} else if (bufferStream.status() == QDataStream::ReadPastEnd) { } else if (bufferStream.status() == QDataStream::ReadPastEnd) {
break; // Apparently EOF is a character so we end up here after the last event. break; // Apparently EOF is a character so we end up here after the last event.
} else if (bufferStream.status() == QDataStream::ReadCorruptData) { } else if (bufferStream.status() == QDataStream::ReadCorruptData) {
emit error(tr("Corrupt data before position %1.").arg(device->pos())); fail(tr("Corrupt data before position %1.").arg(device->pos()));
return; return;
} else { } else {
Q_UNREACHABLE(); Q_UNREACHABLE();
@@ -289,12 +285,6 @@ void QmlProfilerTraceFile::loadQzt(QIODevice *device)
buffer.close(); buffer.close();
setDeviceProgress(device); setDeviceProgress(device);
} }
if (isCanceled()) {
emit canceled();
} else {
emit success();
}
} }
void QmlProfilerTraceFile::addEventsProgress(qint64 timestamp) void QmlProfilerTraceFile::addEventsProgress(qint64 timestamp)
@@ -694,10 +684,8 @@ void QmlProfilerTraceFile::saveQtd(QIODevice *device)
addStageProgress(ProgressTypes); addStageProgress(ProgressTypes);
stream.writeEndElement(); // eventData stream.writeEndElement(); // eventData
if (isCanceled()) { if (isCanceled())
emit canceled();
return; return;
}
QStack<QmlEvent> stack; QStack<QmlEvent> stack;
qint64 lastProgressTimestamp = traceStart(); qint64 lastProgressTimestamp = traceStart();
@@ -794,14 +782,10 @@ void QmlProfilerTraceFile::saveQtd(QIODevice *device)
stream.writeEndElement(); // trace stream.writeEndElement(); // trace
stream.writeEndDocument(); stream.writeEndDocument();
if (isCanceled()) if (stream.hasError())
emit canceled(); fail(tr("Error writing trace file."));
else if (stream.hasError())
emit error(tr("Error writing trace file."));
else
emit success();
}, [this](const QString &message) { }, [this](const QString &message) {
emit error(tr("Could not re-read events from temporary trace file: %s\nSaving failed.") fail(tr("Could not re-read events from temporary trace file: %s\nSaving failed.")
.arg(message)); .arg(message));
}, future()); }, future());
} }
@@ -841,10 +825,8 @@ void QmlProfilerTraceFile::saveQzt(QIODevice *device)
addStageProgress(ProgressNotes); addStageProgress(ProgressNotes);
} }
if (isCanceled()) { if (isCanceled())
emit canceled();
return; return;
}
qint64 lastProgressTimestamp = traceStart(); qint64 lastProgressTimestamp = traceStart();
modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) { modelManager()->replayQmlEvents([&](const QmlEvent &event, const QmlEventType &type) {
@@ -864,17 +846,14 @@ void QmlProfilerTraceFile::saveQzt(QIODevice *device)
}, [&]() { }, [&]() {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
}, [&]() { }, [&]() {
if (isCanceled()) { if (!isCanceled()) {
emit canceled();
} else {
stream << qCompress(buffer.data()); stream << qCompress(buffer.data());
buffer.close(); buffer.close();
buffer.buffer().clear(); buffer.buffer().clear();
addEventsProgress(traceEnd() - lastProgressTimestamp); addEventsProgress(traceEnd() - lastProgressTimestamp);
emit success();
} }
}, [this](const QString &message) { }, [this](const QString &message) {
emit error(tr("Could not re-read events from temporary trace file: %s\nSaving failed.") fail(tr("Could not re-read events from temporary trace file: %s\nSaving failed.")
.arg(message)); .arg(message));
}, future()); }, future());
} }