QmlProfiler: Guard against the temporary trace file going away

If we cannot open a temporary file to cache a trace or retrieve a
previously cached trace, the QML profiler won't work correctly. Show
an error in this case.

Change-Id: I468d74d9c33033b9ad19501bccbd69a9fe164fed
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Ulf Hermann
2017-02-20 19:19:50 +01:00
parent 1ee8d0fb64
commit bf69eb9467
5 changed files with 56 additions and 24 deletions

View File

@@ -101,8 +101,10 @@ QmlProfilerDataModel::QmlProfilerDataModel(QObject *parent) :
this, &QmlProfilerDataModel::detailsChanged); this, &QmlProfilerDataModel::detailsChanged);
connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::eventDetailsChanged, connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::eventDetailsChanged,
this, &QmlProfilerDataModel::allTypesLoaded); this, &QmlProfilerDataModel::allTypesLoaded);
d->file.open(); if (!d->file.open())
d->eventStream.setDevice(&d->file); emit traceFileError();
else
d->eventStream.setDevice(&d->file);
} }
QmlProfilerDataModel::~QmlProfilerDataModel() QmlProfilerDataModel::~QmlProfilerDataModel()
@@ -170,8 +172,11 @@ void QmlProfilerDataModel::clear()
{ {
Q_D(QmlProfilerDataModel); Q_D(QmlProfilerDataModel);
d->file.remove(); d->file.remove();
d->file.open(); d->eventStream.unsetDevice();
d->eventStream.setDevice(&d->file); if (!d->file.open())
emit traceFileError();
else
d->eventStream.setDevice(&d->file);
d->eventTypes.clear(); d->eventTypes.clear();
d->detailsRewriter->clearRequests(); d->detailsRewriter->clearRequests();
} }
@@ -207,14 +212,16 @@ static bool isStateful(const QmlEventType &type)
return message == PixmapCacheEvent || message == MemoryAllocation; return message == PixmapCacheEvent || message == MemoryAllocation;
} }
void QmlProfilerDataModel::replayEvents(qint64 rangeStart, qint64 rangeEnd, bool QmlProfilerDataModel::replayEvents(qint64 rangeStart, qint64 rangeEnd,
QmlProfilerModelManager::EventLoader loader) const QmlProfilerModelManager::EventLoader loader) const
{ {
Q_D(const QmlProfilerDataModel); Q_D(const QmlProfilerDataModel);
QStack<QmlEvent> stack; QStack<QmlEvent> stack;
QmlEvent event; QmlEvent event;
QFile file(d->file.fileName()); QFile file(d->file.fileName());
file.open(QIODevice::ReadOnly); if (!file.open(QIODevice::ReadOnly))
return false;
QDataStream stream(&file); QDataStream stream(&file);
bool crossedRangeStart = false; bool crossedRangeStart = false;
while (!stream.atEnd()) { while (!stream.atEnd()) {
@@ -271,6 +278,7 @@ void QmlProfilerDataModel::replayEvents(qint64 rangeStart, qint64 rangeEnd,
loader(event, type); loader(event, type);
} }
return true;
} }
void QmlProfilerDataModel::finalize() void QmlProfilerDataModel::finalize()

View File

@@ -55,12 +55,13 @@ public:
bool isEmpty() const; bool isEmpty() const;
void addEvent(const QmlEvent &event); void addEvent(const QmlEvent &event);
void addEvents(const QVector<QmlEvent> &events); void addEvents(const QVector<QmlEvent> &events);
void replayEvents(qint64 startTime, qint64 endTime, bool replayEvents(qint64 startTime, qint64 endTime,
QmlProfilerModelManager::EventLoader loader) const; QmlProfilerModelManager::EventLoader loader) const;
void finalize(); void finalize();
signals: signals:
void allTypesLoaded(); void allTypesLoaded();
void traceFileError();
protected slots: protected slots:
void detailsChanged(int typeId, const QString &newString); void detailsChanged(int typeId, const QString &newString);

View File

@@ -176,6 +176,10 @@ QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) :
d->textMarkModel = new QmlProfilerTextMarkModel(this); d->textMarkModel = new QmlProfilerTextMarkModel(this);
connect(d->model, &QmlProfilerDataModel::allTypesLoaded, connect(d->model, &QmlProfilerDataModel::allTypesLoaded,
this, &QmlProfilerModelManager::processingDone); this, &QmlProfilerModelManager::processingDone);
connect(d->model, &QmlProfilerDataModel::traceFileError,
this, [this]() {
emit error(tr("Could not open a temporary file for storing QML traces."));
});
} }
QmlProfilerModelManager::~QmlProfilerModelManager() QmlProfilerModelManager::~QmlProfilerModelManager()
@@ -543,12 +547,17 @@ void QmlProfilerModelManager::restrictToRange(qint64 startTime, qint64 endTime)
setVisibleFeatures(0); setVisibleFeatures(0);
startAcquiring(); startAcquiring();
d->model->replayEvents(startTime, endTime, if (!d->model->replayEvents(startTime, endTime,
std::bind(&QmlProfilerModelManagerPrivate::dispatch, d, std::bind(&QmlProfilerModelManagerPrivate::dispatch, d,
std::placeholders::_1, std::placeholders::_2)); std::placeholders::_1, std::placeholders::_2))) {
d->notesModel->setNotes(notes); emit error(tr("Could not re-read events from temporary trace file. "
d->traceTime->restrictToRange(startTime, endTime); "The trace data is lost."));
acquiringDone(); clear();
} else {
d->notesModel->setNotes(notes);
d->traceTime->restrictToRange(startTime, endTime);
acquiringDone();
}
} }
bool QmlProfilerModelManager::isRestrictedToRange() const bool QmlProfilerModelManager::isRestrictedToRange() const

View File

@@ -120,13 +120,17 @@ void QmlProfilerStatisticsModel::restrictToFeatures(qint64 features)
return; return;
clear(); clear();
d->modelManager->qmlModel()->replayEvents(d->modelManager->traceTime()->startTime(), if (!d->modelManager->qmlModel()->replayEvents(d->modelManager->traceTime()->startTime(),
d->modelManager->traceTime()->endTime(), d->modelManager->traceTime()->endTime(),
std::bind(&QmlProfilerStatisticsModel::loadEvent, std::bind(&QmlProfilerStatisticsModel::loadEvent,
this, std::placeholders::_1, this, std::placeholders::_1,
std::placeholders::_2)); std::placeholders::_2))) {
finalize(); emit d->modelManager->error(tr("Could not re-read events from temporary trace file."));
notesChanged(-1); // Reload notes clear();
} else {
finalize();
notesChanged(-1); // Reload notes
}
} }
const QHash<int, QmlProfilerStatisticsModel::QmlEventStats> &QmlProfilerStatisticsModel::getData() const const QHash<int, QmlProfilerStatisticsModel::QmlEventStats> &QmlProfilerStatisticsModel::getData() const

View File

@@ -668,8 +668,9 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device)
stream.writeStartElement(_("profilerDataModel")); stream.writeStartElement(_("profilerDataModel"));
QStack<QmlEvent> stack; QStack<QmlEvent> stack;
m_model->replayEvents(-1, -1, [this, &stack, &stream](const QmlEvent &event, const bool success = m_model->replayEvents(
const QmlEventType &type) { -1, -1, [this, &stack, &stream](const QmlEvent &event,
const QmlEventType &type) {
if (isCanceled()) if (isCanceled())
return; return;
@@ -741,6 +742,10 @@ void QmlProfilerFileWriter::saveQtd(QIODevice *device)
if ((event.timestamp() & 0xfff) == 0) if ((event.timestamp() & 0xfff) == 0)
updateProgress(event.timestamp()); updateProgress(event.timestamp());
}); });
if (!success) {
emit error(tr("Could not re-read events from temporary trace file. Saving failed."));
return;
}
stream.writeEndElement(); // profilerDataModel stream.writeEndElement(); // profilerDataModel
} }
@@ -807,8 +812,9 @@ void QmlProfilerFileWriter::saveQzt(QFile *file)
if (!isCanceled()) { if (!isCanceled()) {
buffer.open(QIODevice::WriteOnly); buffer.open(QIODevice::WriteOnly);
m_model->replayEvents(-1, -1, [this, &stream, &buffer, &bufferStream]( const bool success = m_model->replayEvents(
const QmlEvent &event, const QmlEventType &type) { -1, -1, [this, &stream, &buffer, &bufferStream](const QmlEvent &event,
const QmlEventType &type) {
Q_UNUSED(type); Q_UNUSED(type);
bufferStream << event; bufferStream << event;
// 32MB buffer should be plenty for efficient compression // 32MB buffer should be plenty for efficient compression
@@ -822,6 +828,10 @@ void QmlProfilerFileWriter::saveQzt(QFile *file)
updateProgress(event.timestamp()); updateProgress(event.timestamp());
} }
}); });
if (!success) {
emit error(tr("Could not re-read events from temporary trace file. Saving failed."));
return;
}
} }
if (isCanceled()) { if (isCanceled()) {