QmlProfiler: save trace data in background thread

Trace data is saved in the background, progress is emitted, and
the save operation can be canceled. While data is being saved
the views are disabled and a semitransparent layer is put on top
of the trace view.

Task-number: QTCREATORBUG-11822
Change-Id: I94ec93147fb1788fc85939ddc591961d058050b5
Reviewed-by: Ulf Hermann <ulf.hermann@theqtcompany.com>
This commit is contained in:
Joerg Bornemann
2015-02-10 20:29:54 +01:00
parent 2404974176
commit c3e1ccf78e
12 changed files with 119 additions and 14 deletions

View File

@@ -36,6 +36,7 @@ namespace Constants {
const char ATTACH[] = "Menu.Analyzer.Attach";
const char TraceFileExtension[] = ".qtd";
const char TASK_SAVE[] = "QmlProfiler.TaskSave";
} // namespace Constants
} // namespace QmlProfiler

View File

@@ -29,11 +29,14 @@
****************************************************************************/
#include "qmlprofilermodelmanager.h"
#include "qmlprofilerconstants.h"
#include "qmlprofilerdatamodel.h"
#include "qv8profilerdatamodel.h"
#include "qmlprofilertracefile.h"
#include "qmlprofilernotesmodel.h"
#include <coreplugin/progressmanager/progressmanager.h>
#include <utils/runextensions.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -363,20 +366,31 @@ void QmlProfilerModelManager::modelProcessingDone()
void QmlProfilerModelManager::save(const QString &filename)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
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;
}
QmlProfilerFileWriter writer;
d->notesModel->saveData();
writer.setTraceTime(traceTime()->startTime(), traceTime()->endTime(), traceTime()->duration());
writer.setV8DataModel(d->v8Model);
writer.setQmlEvents(d->model->getEventTypes(), d->model->getEvents());
writer.setNotes(d->model->getEventNotes());
writer.save(&file);
QFuture<void> result = QtConcurrent::run<void>([this, file] (QFutureInterface<void> &future) {
QmlProfilerFileWriter writer;
writer.setTraceTime(traceTime()->startTime(), traceTime()->endTime(),
traceTime()->duration());
writer.setV8DataModel(d->v8Model);
writer.setQmlEvents(d->model->getEventTypes(), d->model->getEvents());
writer.setNotes(d->model->getEventNotes());
writer.setFuture(&future);
writer.save(file);
file->deleteLater();
QMetaObject::invokeMethod(this, "saveFinished", Qt::QueuedConnection);
});
Core::ProgressManager::addTask(result, tr("Saving Trace Data"), Constants::TASK_SAVE,
Core::ProgressManager::ShowInApplicationIcon);
}
void QmlProfilerModelManager::load(const QString &filename)

View File

@@ -137,6 +137,7 @@ signals:
void stateChanged();
void progressChanged();
void dataAvailable();
void saveFinished();
void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
void availableFeaturesChanged(quint64 features);

View File

@@ -150,6 +150,8 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
connect(d->m_profilerModelManager, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
connect(d->m_profilerModelManager, SIGNAL(availableFeaturesChanged(quint64)),
this, SLOT(setAvailableFeatures(quint64)));
connect(d->m_profilerModelManager, &QmlProfilerModelManager::saveFinished,
this, &QmlProfilerTool::onSaveFinished);
d->m_profilerConnections->setModelManager(d->m_profilerModelManager);
Command *command = 0;
@@ -590,10 +592,16 @@ void QmlProfilerTool::showSaveDialog()
if (!filename.isEmpty()) {
if (!filename.endsWith(QLatin1String(TraceFileExtension)))
filename += QLatin1String(TraceFileExtension);
AnalyzerManager::mainWindow()->setEnabled(false);
d->m_profilerModelManager->save(filename);
}
}
void QmlProfilerTool::onSaveFinished()
{
AnalyzerManager::mainWindow()->setEnabled(true);
}
void QmlProfilerTool::showLoadDialog()
{
if (!checkForUnsavedNotes())

View File

@@ -95,6 +95,7 @@ private slots:
void showSaveOption();
void showLoadOption();
void showSaveDialog();
void onSaveFinished();
void showLoadDialog();
void toggleRecordingFeature(QAction *action);

View File

@@ -425,7 +425,8 @@ QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
m_startTime(0),
m_endTime(0),
m_measuredTime(0),
m_v8Model(0)
m_v8Model(0),
m_future(0)
{
}
@@ -453,8 +454,21 @@ void QmlProfilerFileWriter::setNotes(const QVector<QmlProfilerDataModel::QmlEven
m_notes = notes;
}
void QmlProfilerFileWriter::setFuture(QFutureInterface<void> *future)
{
m_future = future;
}
void QmlProfilerFileWriter::save(QIODevice *device)
{
if (m_future) {
m_future->setProgressRange(0,
qMax(m_qmlEvents.size() + m_ranges.size() + m_notes.size()
+ m_v8Model->numberOfV8Events(), 1));
m_future->setProgressValue(0);
m_newProgressValue = 0;
}
QXmlStreamWriter stream(device);
stream.setAutoFormatting(true);
@@ -469,7 +483,10 @@ void QmlProfilerFileWriter::save(QIODevice *device)
stream.writeStartElement(_("eventData"));
stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime));
for (int typeIndex = 0; typeIndex < m_qmlEvents.size(); ++typeIndex) {
for (int typeIndex = 0; typeIndex < m_qmlEvents.size(); ++typeIndex) {
if (isCanceled())
return;
const QmlProfilerDataModel::QmlEventTypeData &event = m_qmlEvents[typeIndex];
stream.writeStartElement(_("event"));
@@ -496,12 +513,16 @@ void QmlProfilerFileWriter::save(QIODevice *device)
if (event.message == MemoryAllocation)
stream.writeTextElement(_("memoryEventType"), QString::number(event.detailType));
stream.writeEndElement();
incrementProgress();
}
stream.writeEndElement(); // eventData
stream.writeStartElement(_("profilerDataModel"));
for (int rangeIndex = 0; rangeIndex < m_ranges.size(); ++rangeIndex) {
if (isCanceled())
return;
const QmlProfilerDataModel::QmlEventData &range = m_ranges[rangeIndex];
stream.writeStartElement(_("range"));
@@ -550,11 +571,15 @@ void QmlProfilerFileWriter::save(QIODevice *device)
stream.writeAttribute(_("amount"), QString::number(range.numericData1));
stream.writeEndElement();
incrementProgress();
}
stream.writeEndElement(); // profilerDataModel
stream.writeStartElement(_("noteData"));
for (int noteIndex = 0; noteIndex < m_notes.size(); ++noteIndex) {
if (isCanceled())
return;
const QmlProfilerDataModel::QmlEventNoteData &notes = m_notes[noteIndex];
stream.writeStartElement(_("note"));
stream.writeAttribute(_("startTime"), QString::number(notes.startTime));
@@ -562,15 +587,35 @@ void QmlProfilerFileWriter::save(QIODevice *device)
stream.writeAttribute(_("eventIndex"), QString::number(notes.typeIndex));
stream.writeCharacters(notes.text);
stream.writeEndElement(); // note
incrementProgress();
}
stream.writeEndElement(); // noteData
m_v8Model->save(stream);
if (isCanceled())
return;
m_v8Model->save(stream, m_future);
stream.writeEndElement(); // trace
stream.writeEndDocument();
}
void QmlProfilerFileWriter::incrementProgress()
{
if (!m_future)
return;
m_newProgressValue++;
if (float(m_newProgressValue - m_future->progressValue())
/ float(m_future->progressMaximum() - m_future->progressMinimum()) >= 0.01) {
m_future->setProgressValue(m_newProgressValue);
}
}
bool QmlProfilerFileWriter::isCanceled() const
{
return m_future && m_future->isCanceled();
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -31,6 +31,7 @@
#ifndef QMLPROFILERTRACEFILE_H
#define QMLPROFILERTRACEFILE_H
#include <QFutureInterface>
#include <QObject>
#include <QVector>
#include <QString>
@@ -88,17 +89,22 @@ public:
void setQmlEvents(const QVector<QmlProfilerDataModel::QmlEventTypeData> &types,
const QVector<QmlProfilerDataModel::QmlEventData> &events);
void setNotes(const QVector<QmlProfilerDataModel::QmlEventNoteData> &notes);
void setFuture(QFutureInterface<void> *future);
void save(QIODevice *device);
private:
void calculateMeasuredTime();
void incrementProgress();
bool isCanceled() const;
qint64 m_startTime, m_endTime, m_measuredTime;
QV8ProfilerDataModel *m_v8Model;
QFutureInterface<void> *m_future;
QVector<QmlProfilerDataModel::QmlEventTypeData> m_qmlEvents;
QVector<QmlProfilerDataModel::QmlEventData> m_ranges;
QVector<QmlProfilerDataModel::QmlEventNoteData> m_notes;
int m_newProgressValue;
};

View File

@@ -326,5 +326,13 @@ void QmlProfilerTraceView::profilerDataModelStateChanged()
}
}
void QmlProfilerTraceView::changeEvent(QEvent *e)
{
if (e->type() == QEvent::EnabledChange) {
QQuickItem *rootObject = d->m_mainView->rootObject();
rootObject->setProperty("enabled", isEnabled());
}
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -71,6 +71,7 @@ private slots:
void profilerDataModelStateChanged();
protected:
void changeEvent(QEvent *e) Q_DECL_OVERRIDE;
virtual void contextMenuEvent(QContextMenuEvent *event);
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);

View File

@@ -164,6 +164,12 @@ QList<QV8ProfilerDataModel::QV8EventData *> QV8ProfilerDataModel::getV8Events()
return d->v8EventHash.values();
}
int QV8ProfilerDataModel::numberOfV8Events() const
{
Q_D(const QV8ProfilerDataModel);
return d->v8EventHash.size();
}
QString getHashStringForV8Event(const QString &displayName, const QString &function)
{
return QString::fromLatin1("%1:%2").arg(displayName, function);
@@ -323,7 +329,7 @@ void QV8ProfilerDataModel::clearV8RootEvent()
d->v8RootEvent.childrenHash.clear();
}
void QV8ProfilerDataModel::save(QXmlStreamWriter &stream)
void QV8ProfilerDataModel::save(QXmlStreamWriter &stream, QFutureInterface<void> *future)
{
Q_D(QV8ProfilerDataModel);
stream.writeStartElement(QLatin1String("v8profile")); // v8 profiler output
@@ -359,6 +365,9 @@ void QV8ProfilerDataModel::save(QXmlStreamWriter &stream)
stream.writeEndElement();
}
stream.writeEndElement();
if (future)
future->setProgressValue(future->progressValue() + 1);
}
stream.writeEndElement(); // v8 profiler output
}

View File

@@ -34,6 +34,7 @@
#include "qmlprofilerbasemodel.h"
#include <utils/fileinprojectfinder.h>
#include <QFutureInterface>
#include <QObject>
#include <QHash>
@@ -82,11 +83,12 @@ public:
void clear();
bool isEmpty() const;
QList<QV8EventData *> getV8Events() const;
int numberOfV8Events() const;
QV8EventData *v8EventDescription(int typeId) const;
qint64 v8MeasuredTime() const;
void save(QXmlStreamWriter &stream);
void save(QXmlStreamWriter &stream, QFutureInterface<void> *future = 0);
void load(QXmlStreamReader &stream);
void complete();