summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristiaan Janssen <christiaan.janssen@digia.com>2013-08-08 13:28:08 +0200
committerChristiaan Janssen <christiaan.janssen@digia.com>2013-08-09 17:19:32 +0200
commit0a3b20f5f99bec75b590aa81ae26aac046efd794 (patch)
tree13cdc00fe9b5a3e5c91cba19950ff4d788ee42f8
parent7764f35107e901e74458847315ffd114199ce26c (diff)
downloadqt-creator-0a3b20f5f99bec75b590aa81ae26aac046efd794.tar.gz
QmlProfiler: reworked
Change-Id: I66a236a024d76e7bef6edfb91ae30b5dd098b76b Reviewed-by: Kai Koehne <kai.koehne@digia.com>
-rw-r--r--src/libs/qmldebug/qmldebug.qbs1
-rw-r--r--src/libs/qmldebug/qmlprofilereventtypes.h4
-rw-r--r--src/libs/qmldebug/qmlprofilertraceclient.cpp34
-rw-r--r--src/libs/qmldebug/qmlprofilertraceclient.h20
-rw-r--r--src/plugins/qmlprofiler/abstracttimelinemodel.cpp82
-rw-r--r--src/plugins/qmlprofiler/abstracttimelinemodel.h113
-rw-r--r--src/plugins/qmlprofiler/qml/Detail.qml2
-rw-r--r--src/plugins/qmlprofiler/qml/Label.qml58
-rw-r--r--src/plugins/qmlprofiler/qml/MainView.qml157
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.js111
-rw-r--r--src/plugins/qmlprofiler/qml/Overview.qml31
-rw-r--r--src/plugins/qmlprofiler/qml/RangeDetails.qml76
-rw-r--r--src/plugins/qmlprofiler/qml/SelectionRange.qml2
-rw-r--r--src/plugins/qmlprofiler/qml/TimeMarks.qml35
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.pro28
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.qbs24
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp66
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerclientmanager.h21
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp1685
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdatamodel.h201
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp17
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h10
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerengine.cpp2
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp479
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h172
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.cpp657
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.h96
-rw-r--r--src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp364
-rw-r--r--src/plugins/qmlprofiler/qmlprofilermodelmanager.h159
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp458
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h123
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerplugin.cpp14
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerplugin.h10
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp182
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerprocessedmodel.h62
-rw-r--r--src/plugins/qmlprofiler/qmlprofilersimplemodel.cpp114
-rw-r--r--src/plugins/qmlprofiler/qmlprofilersimplemodel.h88
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp100
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatemanager.h18
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp43
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerstatewidget.h7
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp721
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h146
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp73
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertracefile.cpp562
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertracefile.h134
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceview.cpp81
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertraceview.h9
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertreeview.cpp91
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertreeview.h72
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp1016
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerv8eventsview.h202
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp50
-rw-r--r--src/plugins/qmlprofiler/qmlprofilerviewmanager.h5
-rw-r--r--src/plugins/qmlprofiler/qv8profilerdatamodel.cpp62
-rw-r--r--src/plugins/qmlprofiler/qv8profilerdatamodel.h10
-rw-r--r--src/plugins/qmlprofiler/qv8profilereventview.cpp725
-rw-r--r--src/plugins/qmlprofiler/qv8profilereventview.h163
-rw-r--r--src/plugins/qmlprofiler/timelinemodelaggregator.cpp373
-rw-r--r--src/plugins/qmlprofiler/timelinemodelaggregator.h122
-rw-r--r--src/plugins/qmlprofiler/timelinerenderer.cpp446
-rw-r--r--src/plugins/qmlprofiler/timelinerenderer.h70
62 files changed, 7923 insertions, 3136 deletions
diff --git a/src/libs/qmldebug/qmldebug.qbs b/src/libs/qmldebug/qmldebug.qbs
index 601c0e4c5d..beba356937 100644
--- a/src/libs/qmldebug/qmldebug.qbs
+++ b/src/libs/qmldebug/qmldebug.qbs
@@ -6,6 +6,7 @@ QtcLibrary {
cpp.defines: base.concat("QMLDEBUG_LIB")
+ Depends { name: "cpp" }
Depends { name: "Qt"; submodules: ["gui", "network"] }
files: [
diff --git a/src/libs/qmldebug/qmlprofilereventtypes.h b/src/libs/qmldebug/qmlprofilereventtypes.h
index b0498ab755..a5ece2928c 100644
--- a/src/libs/qmldebug/qmlprofilereventtypes.h
+++ b/src/libs/qmldebug/qmlprofilereventtypes.h
@@ -38,6 +38,8 @@ enum QmlEventType {
Creating,
Binding,
HandlingSignal,
+ PixmapCacheEvent,
+ SceneGraphFrameEvent,
MaximumQmlEventType
};
@@ -46,6 +48,8 @@ enum BindingType {
QmlBinding,
V8Binding,
OptimizedBinding,
+ QPainterEvent,
+ AnimationFrame,
MaximumBindingType
};
diff --git a/src/libs/qmldebug/qmlprofilertraceclient.cpp b/src/libs/qmldebug/qmlprofilertraceclient.cpp
index 157c8ac4f6..b0824ddfdb 100644
--- a/src/libs/qmldebug/qmlprofilertraceclient.cpp
+++ b/src/libs/qmldebug/qmlprofilertraceclient.cpp
@@ -170,7 +170,8 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
} else if (event == AnimationFrame) {
int frameRate, animationCount;
stream >> frameRate >> animationCount;
- emit this->frame(time, frameRate, animationCount);
+ emit rangedEvent(QmlDebug::Painting, QmlDebug::AnimationFrame, time, 0,
+ QStringList(), QmlDebug::QmlEventLocation(), frameRate, animationCount, 0,0,0);
d->maximumTime = qMax(time, d->maximumTime);
} else if (event == StartTrace) {
emit this->traceStarted(time);
@@ -181,6 +182,32 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
}
} else if (messageType == Complete) {
emit complete();
+ } else if (messageType == SceneGraphFrame) {
+ int sgEventType;
+ int count = 0;
+ qint64 params[5];
+
+ stream >> sgEventType;
+ while (!stream.atEnd()) {
+ stream >> params[count++];
+ }
+ while (count<5)
+ params[count++] = 0;
+ emit rangedEvent(SceneGraphFrameEvent, sgEventType,time, 0, QStringList(),
+ QmlDebug::QmlEventLocation(), params[0], params[1], params[2], params[3], params[4]);
+ } else if (messageType == PixmapCacheEvent) {
+ int pixEvTy, width = 0, height = 0, refcount = 0;
+ QString pixUrl;
+ stream >> pixEvTy >> pixUrl;
+ if (pixEvTy == (int)PixmapReferenceCountChanged || pixEvTy == (int)PixmapCacheCountChanged) {
+ stream >> refcount;
+ } else if (pixEvTy == (int)PixmapSizeKnown) {
+ stream >> width >> height;
+ refcount = 1;
+ }
+ emit rangedEvent(QmlDebug::PixmapCacheEvent, pixEvTy, time, 0, QStringList(),
+ QmlDebug::QmlEventLocation(pixUrl,0,0), width, height, refcount, 0, 0);
+ d->maximumTime = qMax(time, d->maximumTime);
} else {
int range;
stream >> range;
@@ -240,7 +267,10 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
BindingType bindingType = QmlBinding;
if ((QmlEventType)range == Binding)
bindingType = d->bindingTypes.pop();
- emit this->range((QmlEventType)range, bindingType, startTime, time - startTime, data, location);
+ if ((QmlEventType)range == Painting)
+ bindingType = QPainterEvent;
+ emit rangedEvent((QmlEventType)range, bindingType, startTime, time - startTime, data,
+ location, 0, 0, 0, 0, 0);
if (d->rangeCount[range] == 0) {
int count = d->rangeDatas[range].count() +
d->rangeStartTimes[range].count() +
diff --git a/src/libs/qmldebug/qmlprofilertraceclient.h b/src/libs/qmldebug/qmlprofilertraceclient.h
index 6e0cf1470a..b8364c0ab0 100644
--- a/src/libs/qmldebug/qmlprofilertraceclient.h
+++ b/src/libs/qmldebug/qmlprofilertraceclient.h
@@ -71,10 +71,23 @@ public:
RangeLocation,
RangeEnd,
Complete,
+ PixmapCacheEvent,
+ SceneGraphFrame,
MaximumMessage
};
+ enum PixmapEventType {
+ PixmapSizeKnown,
+ PixmapReferenceCountChanged,
+ PixmapCacheCountChanged,
+ PixmapLoadingStarted,
+ PixmapLoadingFinished,
+ PixmapLoadingError,
+
+ MaximumPixmapEventType
+ };
+
bool isEnabled() const;
bool isRecording() const;
void setRecording(bool);
@@ -89,10 +102,9 @@ signals:
void event(int event, qint64 time);
void traceFinished( qint64 time );
void traceStarted( qint64 time );
- void range(int type, int bindingType, qint64 startTime, qint64 length,
- const QStringList &data, const QmlDebug::QmlEventLocation &location);
- void frame(qint64 time, int frameRate, int animationCount);
-
+ void rangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
+ const QStringList &data, const QmlDebug::QmlEventLocation &location,
+ qint64 param1, qint64 param2, qint64 param3, qint64 param4, qint64 param5);
void recordingChanged(bool arg);
void enabledChanged();
diff --git a/src/plugins/qmlprofiler/abstracttimelinemodel.cpp b/src/plugins/qmlprofiler/abstracttimelinemodel.cpp
new file mode 100644
index 0000000000..b45b3cc0c1
--- /dev/null
+++ b/src/plugins/qmlprofiler/abstracttimelinemodel.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "abstracttimelinemodel.h"
+
+namespace QmlProfiler {
+
+AbstractTimelineModel::AbstractTimelineModel(QObject *parent) : QObject(parent), m_modelManager(0)
+{}
+
+AbstractTimelineModel::~AbstractTimelineModel()
+{}
+
+void AbstractTimelineModel::setModelManager(QmlProfilerModelManager *modelManager)
+{
+ m_modelManager = modelManager;
+ connect(modelManager->simpleModel(),SIGNAL(changed()),this,SLOT(dataChanged()));
+ m_modelId = modelManager->registerModelProxy();
+}
+
+qint64 AbstractTimelineModel::traceStartTime() const
+{
+ return m_modelManager->traceTime()->startTime();
+}
+
+qint64 AbstractTimelineModel::traceEndTime() const
+{
+ return m_modelManager->traceTime()->endTime();
+}
+
+qint64 AbstractTimelineModel::traceDuration() const
+{
+ return m_modelManager->traceTime()->duration();
+}
+
+int AbstractTimelineModel::getState() const
+{
+ return (int)m_modelManager->state();
+}
+
+int AbstractTimelineModel::rowCount() const
+{
+ int count = 0;
+ for (int i=0; i<categoryCount(); i++)
+ count += categoryDepth(i);
+ return count;
+}
+
+int AbstractTimelineModel::getBindingLoopDest(int index) const
+{
+ Q_UNUSED(index);
+ return -1;
+}
+
+
+}
diff --git a/src/plugins/qmlprofiler/abstracttimelinemodel.h b/src/plugins/qmlprofiler/abstracttimelinemodel.h
new file mode 100644
index 0000000000..0efcd60631
--- /dev/null
+++ b/src/plugins/qmlprofiler/abstracttimelinemodel.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef ABSTRACTTIMELINEMODEL_H
+#define ABSTRACTTIMELINEMODEL_H
+
+
+#include "qmlprofiler_global.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+#include <QObject>
+#include <QVariant>
+#include <QColor>
+
+namespace QmlProfiler {
+
+class QMLPROFILER_EXPORT AbstractTimelineModel : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit AbstractTimelineModel(QObject *parent = 0);
+ ~AbstractTimelineModel();
+
+ void setModelManager(QmlProfilerModelManager *modelManager);
+
+ virtual int categories() const = 0;
+ virtual QStringList categoryTitles() const = 0;
+ virtual QString name() const = 0;
+ virtual int count() const = 0;
+
+ virtual bool isEmpty() const = 0;
+
+ virtual bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const = 0;
+
+ Q_INVOKABLE virtual qint64 lastTimeMark() const = 0;
+ Q_INVOKABLE qint64 traceStartTime() const;
+ Q_INVOKABLE qint64 traceEndTime() const;
+ Q_INVOKABLE qint64 traceDuration() const;
+ Q_INVOKABLE int getState() const;
+
+ Q_INVOKABLE virtual bool expanded(int category) const = 0;
+ Q_INVOKABLE virtual void setExpanded(int category, bool expanded) = 0;
+ Q_INVOKABLE virtual int categoryDepth(int categoryIndex) const = 0;
+ Q_INVOKABLE virtual int categoryCount() const = 0;
+ Q_INVOKABLE virtual int rowCount() const;
+ Q_INVOKABLE virtual const QString categoryLabel(int categoryIndex) const = 0;
+
+ virtual int findFirstIndex(qint64 startTime) const = 0;
+ virtual int findFirstIndexNoParents(qint64 startTime) const = 0;
+ virtual int findLastIndex(qint64 endTime) const = 0;
+
+ virtual int getEventType(int index) const = 0;
+ virtual int getEventCategory(int index) const = 0;
+ virtual int getEventRow(int index) const = 0;
+ Q_INVOKABLE virtual qint64 getDuration(int index) const = 0;
+ Q_INVOKABLE virtual qint64 getStartTime(int index) const = 0;
+ Q_INVOKABLE virtual qint64 getEndTime(int index) const = 0;
+ Q_INVOKABLE virtual int getEventId(int index) const = 0;
+ Q_INVOKABLE virtual int getBindingLoopDest(int index) const;
+ Q_INVOKABLE virtual QColor getColor(int index) const = 0;
+ Q_INVOKABLE virtual float getHeight(int index) const = 0;
+
+ Q_INVOKABLE virtual const QVariantList getLabelsForCategory(int category) const = 0;
+
+ Q_INVOKABLE virtual const QVariantList getEventDetails(int index) const = 0;
+
+ // returned map should contain "file", "line", "column" properties, or be empty
+ Q_INVOKABLE virtual const QVariantMap getEventLocation(int index) const = 0;
+ Q_INVOKABLE virtual int getEventIdForHash(const QString &eventHash) const = 0;
+ Q_INVOKABLE virtual int getEventIdForLocation(const QString &filename, int line, int column) const = 0;
+
+signals:
+ void countChanged();
+ void dataAvailable();
+ void stateChanged();
+ void emptyChanged();
+ void expandedChanged();
+
+protected:
+ QmlProfilerModelManager *m_modelManager;
+ int m_modelId;
+};
+
+}
+
+#endif // ABSTRACTTIMELINEMODEL_H
diff --git a/src/plugins/qmlprofiler/qml/Detail.qml b/src/plugins/qmlprofiler/qml/Detail.qml
index 567ea7f02a..7bc28178d9 100644
--- a/src/plugins/qmlprofiler/qml/Detail.qml
+++ b/src/plugins/qmlprofiler/qml/Detail.qml
@@ -34,7 +34,6 @@ Item {
id: detail
property string label
property string content
- signal linkActivated(string url)
height: childrenRect.height+2
width: childrenRect.width
@@ -55,7 +54,6 @@ Item {
font.pixelSize: 12
anchors.baseline: lbl.baseline
anchors.left: guideline.right
- onLinkActivated: detail.linkActivated(link)
textFormat: Text.PlainText
}
}
diff --git a/src/plugins/qmlprofiler/qml/Label.qml b/src/plugins/qmlprofiler/qml/Label.qml
index f7db9ab34c..015391a3f5 100644
--- a/src/plugins/qmlprofiler/qml/Label.qml
+++ b/src/plugins/qmlprofiler/qml/Label.qml
@@ -31,23 +31,24 @@ import QtQuick 1.0
Item {
id: labelContainer
- property alias text: txt.text
+ property string text: qmlProfilerModelProxy.categoryLabel(modelIndex, categoryIndex)
property bool expanded: false
- property int typeIndex: index
+ property int categoryIndex: qmlProfilerModelProxy.correctedCategoryIndexForModel(modelIndex, index)
+ property int modelIndex: qmlProfilerModelProxy.modelIndexForCategory(index);
property variant descriptions: []
property variant extdescriptions: []
property variant eventIds: []
+ visible: qmlProfilerModelProxy.categoryDepth(modelIndex, categoryIndex) > 0;
+
height: root.singleRowHeight
width: 150
onExpandedChanged: {
- var rE = labels.rowExpanded;
- rE[typeIndex] = expanded;
- labels.rowExpanded = rE;
+ qmlProfilerModelProxy.setExpanded(modelIndex, categoryIndex, expanded);
backgroundMarks.requestRedraw();
- view.setRowExpanded(typeIndex, expanded);
+ getDescriptions();
updateHeight();
}
@@ -56,20 +57,24 @@ Item {
}
function updateHeight() {
- height = root.singleRowHeight * (1 +
- (expanded ? qmlProfilerDataModel.uniqueEventsOfType(typeIndex) :
- qmlProfilerDataModel.maxNestingForType(typeIndex)));
+ if (expanded != qmlProfilerModelProxy.expanded(modelIndex, categoryIndex))
+ expanded = qmlProfilerModelProxy.expanded(modelIndex, categoryIndex);
+ height = root.singleRowHeight * qmlProfilerModelProxy.categoryDepth(modelIndex, categoryIndex);
}
function getDescriptions() {
+ visible = qmlProfilerModelProxy.categoryDepth(modelIndex, categoryIndex) > 0;
+ if (!visible)
+ return;
+
var desc=[];
var ids=[];
var extdesc=[];
- for (var i=0; i<qmlProfilerDataModel.uniqueEventsOfType(typeIndex); i++) {
- desc[i] = qmlProfilerDataModel.eventTextForType(typeIndex, i);
- ids[i] = qmlProfilerDataModel.eventIdForType(typeIndex, i);
- extdesc[i] = qmlProfilerDataModel.eventDisplayNameForType(typeIndex, i) +
- " : " + desc[i];
+ var labelList = qmlProfilerModelProxy.getLabelsForCategory(modelIndex, categoryIndex);
+ for (var i = 0; i < labelList.length; i++ ) {
+ desc[i] = labelList[i].description;
+ ids[i] = labelList[i].id;
+ extdesc[i] = labelList[i].displayName + ":" + labelList[i].description;
}
descriptions = desc;
eventIds = ids;
@@ -78,20 +83,13 @@ Item {
}
Connections {
- target: qmlProfilerDataModel
- onReloadDetailLabels: getDescriptions();
+ target: qmlProfilerModelProxy
+ onExpandedChanged: {
+ updateHeight();
+ }
+
onStateChanged: {
- // Empty
- if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
- descriptions = [];
- eventIds = [];
- extdescriptions = [];
- updateHeight();
- } else
- // Done
- if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
- getDescriptions();
- }
+ getDescriptions();
}
}
@@ -99,6 +97,7 @@ Item {
id: txt
x: 5
font.pixelSize: 12
+ text: labelContainer.text
color: "#232323"
height: root.singleRowHeight
width: 140
@@ -140,9 +139,9 @@ Item {
onExited: changeToolTip("");
onClicked: {
if (mouse.modifiers & Qt.ShiftModifier)
- view.selectPrevFromId(eventIds[index]);
+ view.selectPrevFromId(modelIndex,eventIds[index]);
else
- view.selectNextFromId(eventIds[index]);
+ view.selectNextFromId(modelIndex,eventIds[index]);
}
}
}
@@ -150,7 +149,6 @@ Item {
}
Image {
- visible: descriptions.length > 0
source: expanded ? "arrow_down.png" : "arrow_right.png"
x: parent.width - 12
y: root.singleRowHeight / 2 - height / 2
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index df8b8922a3..9e859258ee 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -51,14 +51,6 @@ Rectangle {
signal selectedEventChanged(int eventId)
property bool lockItemSelection : false
- property variant names: [ qsTr("Painting"),
- qsTr("Compiling"),
- qsTr("Creating"),
- qsTr("Binding"),
- qsTr("Handling Signal")]
- property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC",
- "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
-
property variant mainviewTimePerPixel : 0
signal updateCursorPosition
@@ -95,7 +87,7 @@ Rectangle {
backgroundMarks.updateMarks(startTime, endTime);
view.updateFlickRange(startTime, endTime);
if (duration > 0) {
- var candidateWidth = qmlProfilerDataModel.traceDuration() *
+ var candidateWidth = qmlProfilerModelProxy.traceDuration() *
flick.width / duration;
if (flick.contentWidth !== candidateWidth)
flick.contentWidth = candidateWidth;
@@ -104,22 +96,23 @@ Rectangle {
}
}
+
Connections {
- target: qmlProfilerDataModel
+ target: qmlProfilerModelProxy
onCountChanged: {
- eventCount = qmlProfilerDataModel.count();
+ eventCount = qmlProfilerModelProxy.count();
if (eventCount === 0)
root.clearAll();
if (eventCount > 1) {
root.progress = Math.min(1.0,
- (qmlProfilerDataModel.lastTimeMark() -
- qmlProfilerDataModel.traceStartTime()) / root.elapsedTime * 1e-9 );
+ (qmlProfilerModelProxy.lastTimeMark() -
+ qmlProfilerModelProxy.traceStartTime()) / root.elapsedTime * 1e-9 );
} else {
root.progress = 0;
}
}
onStateChanged: {
- switch (qmlProfilerDataModel.getCurrentStateFromQml()) {
+ switch (qmlProfilerModelProxy.getState()) {
case 0: {
root.clearAll();
break;
@@ -132,27 +125,30 @@ Rectangle {
root.progress = 0.9; // jump to 90%
break;
}
- case 3: {
- view.clearData();
- progress = 1.0;
- dataAvailable = true;
- view.visible = true;
- view.requestPaint();
- zoomControl.setRange(qmlProfilerDataModel.traceStartTime(),
- qmlProfilerDataModel.traceStartTime() +
- qmlProfilerDataModel.traceDuration()/10);
- break;
- }
}
}
+ onDataAvailable: {
+ view.clearData();
+ zoomControl.setRange(0,0);
+ progress = 1.0;
+ dataAvailable = true;
+ view.visible = true;
+ view.requestPaint();
+ zoomControl.setRange(qmlProfilerModelProxy.traceStartTime(),
+ qmlProfilerModelProxy.traceStartTime() +
+ qmlProfilerModelProxy.traceDuration()/10);
+ }
}
+
// ***** functions
function gotoSourceLocation(file,line,column) {
- root.fileName = file;
- root.lineNumber = line;
- root.columnNumber = column;
- root.updateCursorPosition();
+ if (file !== undefined) {
+ root.fileName = file;
+ root.lineNumber = line;
+ root.columnNumber = column;
+ root.updateCursorPosition();
+ }
}
function clearData() {
@@ -186,10 +182,10 @@ Rectangle {
function updateWindowLength(absoluteFactor) {
var windowLength = view.endTime - view.startTime;
- if (qmlProfilerDataModel.traceEndTime() <= qmlProfilerDataModel.traceStartTime() ||
+ if (qmlProfilerModelProxy.traceEndTime() <= qmlProfilerModelProxy.traceStartTime() ||
windowLength <= 0)
return;
- var currentFactor = windowLength / qmlProfilerDataModel.traceDuration();
+ var currentFactor = windowLength / qmlProfilerModelProxy.traceDuration();
updateZoom(absoluteFactor / currentFactor);
}
@@ -200,8 +196,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
- if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
- newWindowLength = qmlProfilerDataModel.traceDuration();
+ if (newWindowLength > qmlProfilerModelProxy.traceDuration()) {
+ newWindowLength = qmlProfilerModelProxy.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
@@ -210,13 +206,15 @@ Rectangle {
}
var fixedPoint = (view.startTime + view.endTime) / 2;
+
if (view.selectedItem !== -1) {
// center on selected item if it's inside the current screen
- var newFixedPoint = qmlProfilerDataModel.getStartTime(view.selectedItem);
+ var newFixedPoint = qmlProfilerModelProxy.getStartTime(view.selectedModel, view.selectedItem);
if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
fixedPoint = newFixedPoint;
}
+
var startTime = fixedPoint - relativeFactor*(fixedPoint - view.startTime);
zoomControl.setRange(startTime, startTime + newWindowLength);
}
@@ -229,8 +227,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
- if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
- newWindowLength = qmlProfilerDataModel.traceDuration();
+ if (newWindowLength > qmlProfilerModelProxy.traceDuration()) {
+ newWindowLength = qmlProfilerModelProxy.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
@@ -248,26 +246,27 @@ Rectangle {
var newStart = Math.floor(centerPoint - windowLength/2);
if (newStart < 0)
newStart = 0;
- if (newStart + windowLength > qmlProfilerDataModel.traceEndTime())
- newStart = qmlProfilerDataModel.traceEndTime() - windowLength;
+ if (newStart + windowLength > qmlProfilerModelProxy.traceEndTime())
+ newStart = qmlProfilerModelProxy.traceEndTime() - windowLength;
zoomControl.setRange(newStart, newStart + windowLength);
}
- function recenterOnItem( itemIndex )
+ function recenterOnItem( modelIndex, itemIndex )
{
if (itemIndex === -1)
return;
// if item is outside of the view, jump back to its position
- if (qmlProfilerDataModel.getEndTime(itemIndex) < view.startTime ||
- qmlProfilerDataModel.getStartTime(itemIndex) > view.endTime) {
- recenter((qmlProfilerDataModel.getStartTime(itemIndex) +
- qmlProfilerDataModel.getEndTime(itemIndex)) / 2);
+ if (qmlProfilerModelProxy.getEndTime(modelIndex, itemIndex) < view.startTime ||
+ qmlProfilerModelProxy.getStartTime(modelIndex, itemIndex) > view.endTime) {
+ recenter((qmlProfilerModelProxy.getStartTime(modelIndex, itemIndex) +
+ qmlProfilerModelProxy.getEndTime(modelIndex, itemIndex)) / 2);
}
+
}
function wheelZoom(wheelCenter, wheelDelta) {
- if (qmlProfilerDataModel.traceEndTime() > qmlProfilerDataModel.traceStartTime() &&
+ if (qmlProfilerModelProxy.traceEndTime() > qmlProfilerModelProxy.traceStartTime() &&
wheelDelta !== 0) {
if (wheelDelta>0)
updateZoomCentered(wheelCenter, 1/1.2);
@@ -280,24 +279,35 @@ Rectangle {
rangeDetails.visible = false;
rangeDetails.duration = "";
rangeDetails.label = "";
- rangeDetails.type = "";
+ //rangeDetails.type = "";
rangeDetails.file = "";
rangeDetails.line = -1;
rangeDetails.column = 0;
rangeDetails.isBindingLoop = false;
}
- function selectNextWithId( eventId )
+ function selectNextByHash(hash) {
+ var eventId = qmlProfilerModelProxy.getEventIdForHash(hash);
+ if (eventId !== -1) {
+ selectNextById(eventId);
+ }
+ }
+
+ function selectNextById(eventId)
{
+ // this is a slot responding to events from the other pane
+ // which tracks only events from the basic model
if (!lockItemSelection) {
lockItemSelection = true;
- var itemIndex = view.nextItemFromId( eventId );
+ var modelIndex = qmlProfilerModelProxy.basicModelIndex();
+ var itemIndex = view.nextItemFromId( modelIndex, eventId );
// select an item, lock to it, and recenter if necessary
- if (view.selectedItem != itemIndex) {
+ if (view.selectedItem != itemIndex || view.selectedModel != modelIndex) {
+ view.selectedModel = modelIndex;
view.selectedItem = itemIndex;
if (itemIndex !== -1) {
view.selectionLocked = true;
- recenterOnItem(itemIndex);
+ recenterOnItem(modelIndex, itemIndex);
}
}
lockItemSelection = false;
@@ -317,7 +327,9 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem != -1 && !lockItemSelection) {
lockItemSelection = true;
- selectedEventChanged( qmlProfilerDataModel.getEventId(selectedItem) );
+ // update in other views
+ var eventLocation = qmlProfilerModelProxy.getEventLocation(view.selectedModel, view.selectedItem);
+ gotoSourceLocation(eventLocation.file, eventLocation.line, eventLocation.column);
lockItemSelection = false;
}
}
@@ -392,7 +404,7 @@ Rectangle {
TimelineRenderer {
id: view
- profilerDataModel: qmlProfilerDataModel
+ profilerModelProxy: qmlProfilerModelProxy
x: flick.contentX
width: flick.width
@@ -401,12 +413,12 @@ Rectangle {
property variant startX: 0
onStartXChanged: {
var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) +
- qmlProfilerDataModel.traceStartTime();
+ qmlProfilerModelProxy.traceStartTime();
if (Math.abs(newStartTime - startTime) > 1) {
var newEndTime = Math.round((startX+flick.width) *
(endTime - startTime) /
flick.width) +
- qmlProfilerDataModel.traceStartTime();
+ qmlProfilerModelProxy.traceStartTime();
zoomControl.setRange(newStartTime, newEndTime);
}
@@ -418,7 +430,7 @@ Rectangle {
if (start !== startTime || end !== endTime) {
startTime = start;
endTime = end;
- var newStartX = (startTime - qmlProfilerDataModel.traceStartTime()) *
+ var newStartX = (startTime - qmlProfilerModelProxy.traceStartTime()) *
flick.width / (endTime-startTime);
if (Math.abs(newStartX - startX) >= 1)
startX = newStartX;
@@ -428,32 +440,26 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem !== -1) {
// display details
- rangeDetails.duration = qmlProfilerDataModel.getDuration(selectedItem)/1000.0;
- rangeDetails.label = qmlProfilerDataModel.getDetails(selectedItem);
- rangeDetails.file = qmlProfilerDataModel.getFilename(selectedItem);
- rangeDetails.line = qmlProfilerDataModel.getLine(selectedItem);
- rangeDetails.column = qmlProfilerDataModel.getColumn(selectedItem);
- rangeDetails.type = root.names[qmlProfilerDataModel.getType(selectedItem)];
- rangeDetails.isBindingLoop = qmlProfilerDataModel.getBindingLoopDest(selectedItem)!==-1;
-
- rangeDetails.visible = true;
+ rangeDetails.showInfo(qmlProfilerModelProxy.getEventDetails(selectedModel, selectedItem));
+ rangeDetails.setLocation(qmlProfilerModelProxy.getEventLocation(selectedModel, selectedItem));
// center view (horizontally)
var windowLength = view.endTime - view.startTime;
- var eventStartTime = qmlProfilerDataModel.getStartTime(selectedItem);
+ var eventStartTime = qmlProfilerModelProxy.getStartTime(selectedModel, selectedItem);
var eventEndTime = eventStartTime +
- qmlProfilerDataModel.getDuration(selectedItem);
+ qmlProfilerModelProxy.getDuration(selectedModel, selectedItem);
if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
var center = (eventStartTime + eventEndTime)/2;
- var from = Math.min(qmlProfilerDataModel.traceEndTime()-windowLength,
+ var from = Math.min(qmlProfilerModelProxy.traceEndTime()-windowLength,
Math.max(0, Math.floor(center - windowLength/2)));
zoomControl.setRange(from, from + windowLength);
+
}
// center view (vertically)
- var itemY = view.getYPosition(selectedItem);
+ var itemY = view.getYPosition(selectedModel, selectedItem);
if (itemY < root.scrollY) {
root.updateVerticalScroll(itemY);
} else
@@ -462,17 +468,16 @@ Rectangle {
root.updateVerticalScroll(itemY + root.singleRowHeight -
root.candidateHeight);
}
+
} else {
root.hideRangeDetails();
}
}
onItemPressed: {
- if (pressedItem !== -1) {
- root.gotoSourceLocation(qmlProfilerDataModel.getFilename(pressedItem),
- qmlProfilerDataModel.getLine(pressedItem),
- qmlProfilerDataModel.getColumn(pressedItem));
- }
+ var location = qmlProfilerModelProxy.getEventLocation(modelIndex, pressedItem);
+ if (location.hasOwnProperty("file")) // not empty
+ root.gotoSourceLocation(location.file, location.line, location.column);
}
// hack to pass mouse events to the other mousearea if enabled
@@ -522,17 +527,13 @@ Rectangle {
color: "#dcdcdc"
height: col.height
- property int rowCount: 5
- property variant rowExpanded: [false,false,false,false,false];
+ property int rowCount: qmlProfilerModelProxy.categories();
Column {
id: col
Repeater {
model: labels.rowCount
- delegate: Label {
- text: root.names[index]
- height: labels.height/labels.rowCount
- }
+ delegate: Label { }
}
}
}
diff --git a/src/plugins/qmlprofiler/qml/Overview.js b/src/plugins/qmlprofiler/qml/Overview.js
index 06b4d7c91d..4ef2187b43 100644
--- a/src/plugins/qmlprofiler/qml/Overview.js
+++ b/src/plugins/qmlprofiler/qml/Overview.js
@@ -29,7 +29,7 @@
.pragma library
-var qmlProfilerDataModel = 0;
+var qmlProfilerModelProxy = 0;
//draw background of the graph
function drawGraph(canvas, ctxt, region)
@@ -41,89 +41,82 @@ function drawGraph(canvas, ctxt, region)
//draw the actual data to be graphed
function drawData(canvas, ctxt, region)
{
- if ((!qmlProfilerDataModel) || qmlProfilerDataModel.count() == 0)
+ if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.count() == 0)
return;
- var typeCount = 5;
var width = canvas.width;
var bump = 10;
var height = canvas.height - bump;
+
+ var typeCount = qmlProfilerModelProxy.visibleCategories();
var blockHeight = height / typeCount;
- var spacing = width / qmlProfilerDataModel.traceDuration();
-
- var highest = [0,0,0,0,0]; // note: change if typeCount changes
-
- for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) {
-
- var xx = (qmlProfilerDataModel.getStartTime(ii) -
- qmlProfilerDataModel.traceStartTime()) * spacing;
- if (xx > region.x + region.width)
- continue;
-
- var eventWidth = qmlProfilerDataModel.getDuration(ii) * spacing;
- if (xx + eventWidth < region.x)
- continue;
-
- if (eventWidth < 1)
- eventWidth = 1;
-
- xx = Math.round(xx);
- var ty = qmlProfilerDataModel.getType(ii);
- if (xx + eventWidth > highest[ty]) {
- // special: animations
- if (ty === 0 && qmlProfilerDataModel.getAnimationCount(ii) >= 0) {
- var vertScale = qmlProfilerDataModel.getMaximumAnimationCount() -
- qmlProfilerDataModel.getMinimumAnimationCount();
- if (vertScale < 1)
- vertScale = 1;
- var fraction = (qmlProfilerDataModel.getAnimationCount(ii) -
- qmlProfilerDataModel.getMinimumAnimationCount()) / vertScale;
- var eventHeight = blockHeight * (fraction * 0.85 + 0.15);
- var yy = bump + ty*blockHeight + blockHeight - eventHeight;
-
- var fpsFraction = qmlProfilerDataModel.getFramerate(ii) / 60.0;
- if (fpsFraction > 1.0)
- fpsFraction = 1.0;
- ctxt.fillStyle = "hsl("+(fpsFraction*0.27+0.028)+",0.3,0.65)";
- ctxt.fillRect(xx, yy, eventWidth, eventHeight);
- } else {
- var hue = ( qmlProfilerDataModel.getEventId(ii) * 25 ) % 360;
- ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)";
- ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight);
- }
- highest[ty] = xx+eventWidth;
+ var spacing = width / qmlProfilerModelProxy.traceDuration();
+
+ var modelRowStart = 0;
+ for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); modelIndex++) {
+ for (var ii = 0; ii < qmlProfilerModelProxy.count(modelIndex); ++ii) {
+
+ var xx = (qmlProfilerModelProxy.getStartTime(modelIndex,ii) -
+ qmlProfilerModelProxy.traceStartTime()) * spacing;
+ if (xx > region.x + region.width)
+ continue;
+
+ var eventWidth = qmlProfilerModelProxy.getDuration(modelIndex,ii) * spacing;
+ if (xx + eventWidth < region.x)
+ continue;
+
+ if (eventWidth < 1)
+ eventWidth = 1;
+
+ xx = Math.round(xx);
+
+ var rowNumber = modelRowStart + qmlProfilerModelProxy.getEventCategoryInModel(modelIndex, ii);
+
+ var itemHeight = qmlProfilerModelProxy.getHeight(modelIndex,ii) * blockHeight;
+ var yy = (rowNumber + 1) * blockHeight - itemHeight ;
+
+ var itemColor = qmlProfilerModelProxy.getColorRGB(modelIndex, ii);
+ ctxt.fillStyle = "rgb("+itemColor[0]+","+itemColor[1]+","+itemColor[2]+")";
+ ctxt.fillRect(xx, bump + yy, eventWidth, itemHeight);
}
+ modelRowStart += qmlProfilerModelProxy.categoryCount(modelIndex);
}
// binding loops
ctxt.strokeStyle = "orange";
ctxt.lineWidth = 2;
var radius = 1;
- for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) {
- if (qmlProfilerDataModel.getBindingLoopDest(ii) >= 0) {
- var xcenter = Math.round(qmlProfilerDataModel.getStartTime(ii) +
- qmlProfilerDataModel.getDuration(ii) -
- qmlProfilerDataModel.traceStartTime()) * spacing;
- var ycenter = Math.round(bump + qmlProfilerDataModel.getType(ii) *
- blockHeight + blockHeight/2);
- ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
- ctxt.stroke();
+ modelRowStart = 0;
+ for (modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); modelIndex++) {
+ for (ii = 0; ii < qmlProfilerModelProxy.count(modelIndex); ++ii) {
+ if (qmlProfilerModelProxy.getBindingLoopDest(modelIndex,ii) >= 0) {
+ var xcenter = Math.round(qmlProfilerModelProxy.getStartTime(modelIndex,ii) +
+ qmlProfilerModelProxy.getDuration(modelIndex,ii) -
+ qmlProfilerModelProxy.traceStartTime()) * spacing;
+ var ycenter = Math.round(bump + (modelRowStart +
+ qmlProfilerModelProxy.getEventCategoryInModel(modelIndex, ii)) *
+ blockHeight + blockHeight/2);
+ ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
+ ctxt.stroke();
+ }
}
+ modelRowStart += qmlProfilerModelProxy.categoryCount(modelIndex);
}
+
}
function drawTimeBar(canvas, ctxt, region)
{
- if (!qmlProfilerDataModel)
+ if (!qmlProfilerModelProxy)
return;
var width = canvas.width;
var height = 10;
- var startTime = qmlProfilerDataModel.traceStartTime();
- var endTime = qmlProfilerDataModel.traceEndTime();
+ var startTime = qmlProfilerModelProxy.traceStartTime();
+ var endTime = qmlProfilerModelProxy.traceEndTime();
- var totalTime = qmlProfilerDataModel.traceDuration();
+ var totalTime = qmlProfilerModelProxy.traceDuration();
var spacing = width / totalTime;
var initialBlockLength = 120;
diff --git a/src/plugins/qmlprofiler/qml/Overview.qml b/src/plugins/qmlprofiler/qml/Overview.qml
index 65686be9de..d90e1e6c7d 100644
--- a/src/plugins/qmlprofiler/qml/Overview.qml
+++ b/src/plugins/qmlprofiler/qml/Overview.qml
@@ -36,36 +36,37 @@ Canvas2D {
// ***** properties
height: 50
- property bool dataAvailable: false
+ property bool dataReady: false
property variant startTime : 0
property variant endTime : 0
// ***** functions
function clearDisplay()
{
- dataAvailable = false;
+ dataReady = false;
requestRedraw();
}
function updateRange() {
- var newStartTime = Math.round(rangeMover.x * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime();
- var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime();
+ var newStartTime = Math.round(rangeMover.x * qmlProfilerModelProxy.traceDuration() / width) + qmlProfilerModelProxy.traceStartTime();
+ var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerModelProxy.traceDuration() / width) + qmlProfilerModelProxy.traceStartTime();
if (startTime !== newStartTime || endTime !== newEndTime) {
zoomControl.setRange(newStartTime, newEndTime);
}
+
}
// ***** connections to external objects
Connections {
target: zoomControl
onRangeChanged: {
- if (qmlProfilerDataModel) {
+ if (qmlProfilerModelProxy) {
startTime = zoomControl.startTime();
endTime = zoomControl.endTime();
- var newRangeX = (startTime - qmlProfilerDataModel.traceStartTime()) * width / qmlProfilerDataModel.traceDuration();
+ var newRangeX = (startTime - qmlProfilerModelProxy.traceStartTime()) * width / qmlProfilerModelProxy.traceDuration();
if (rangeMover.x !== newRangeX)
rangeMover.x = newRangeX;
- var newWidth = (endTime-startTime) * width / qmlProfilerDataModel.traceDuration();
+ var newWidth = (endTime-startTime) * width / qmlProfilerModelProxy.traceDuration();
if (rangeMover.width !== newWidth)
rangeMover.width = newWidth;
}
@@ -73,20 +74,18 @@ Canvas2D {
}
Connections {
- target: qmlProfilerDataModel
- onStateChanged: {
- // State is "done"
- if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
- dataAvailable = true;
+ target: qmlProfilerModelProxy
+ onDataAvailable: {
+ dataReady = true;
requestRedraw();
- }
}
}
+
// ***** slots
onDrawRegion: {
- Plotter.qmlProfilerDataModel = qmlProfilerDataModel;
- if (dataAvailable) {
+ Plotter.qmlProfilerModelProxy = qmlProfilerModelProxy;
+ if (dataReady) {
Plotter.plot(canvas, ctxt, region);
} else {
Plotter.drawGraph(canvas, ctxt, region) //just draw the background
@@ -116,7 +115,7 @@ Canvas2D {
RangeMover {
id: rangeMover
- visible: dataAvailable
+ visible: dataReady
}
Rectangle {
diff --git a/src/plugins/qmlprofiler/qml/RangeDetails.qml b/src/plugins/qmlprofiler/qml/RangeDetails.qml
index 965458c690..a0fb3e8d77 100644
--- a/src/plugins/qmlprofiler/qml/RangeDetails.qml
+++ b/src/plugins/qmlprofiler/qml/RangeDetails.qml
@@ -35,7 +35,7 @@ Item {
property string duration
property string label
- property string type
+ property string dialogTitle
property string file
property int line
property int column
@@ -65,6 +65,36 @@ Item {
onCandidateHeightChanged: fitInView();
}
+ //property variant eventInfo
+
+ ListModel {
+ id: eventInfo
+ }
+
+ function showInfo(eventData) {
+ eventInfo.clear();
+ rangeDetails.dialogTitle = eventData[0]["title"];
+ for (var i = 1; i < eventData.length; i++) {
+ for (var k in eventData[i]) {
+ eventInfo.append({"key": k, "value":eventData[i][k]});
+ }
+ }
+ rangeDetails.visible = true;
+ }
+
+ function setLocation(location) {
+ if (location.hasOwnProperty("file")) { // not empty
+ file = location.file;
+ line = location.line;
+ column = location.column;
+ } else {
+ // reset to default values
+ file = "";
+ line = 0;
+ column = -1;
+ }
+ }
+
function fitInView() {
// don't reposition if it does not fit
if (root.width < width || root.candidateHeight < height)
@@ -122,7 +152,7 @@ Item {
//title
Text {
id: typeTitle
- text: " "+rangeDetails.type
+ text: " "+rangeDetails.dialogTitle
font.bold: true
height: 18
y: 2
@@ -145,42 +175,14 @@ Item {
id: col
x: 10
y: 5
- Detail {
- label: qsTr("Duration:")
- content: rangeDetails.duration < 1000 ? rangeDetails.duration + "μs" : Math.floor(rangeDetails.duration/10)/100 + "ms"
- }
- Detail {
- opacity: content.length !== 0 ? 1 : 0
- label: qsTr("Details:")
- content: {
- var inputString = rangeDetails.label;
- if (inputString.length > 7 && inputString.substring(0,7) == "file://") {
- var pos = inputString.lastIndexOf("/");
- return inputString.substr(pos+1);
- }
- var maxLen = 40;
- if (inputString.length > maxLen)
- inputString = inputString.substring(0,maxLen)+"...";
-
- return inputString;
- }
- }
- Detail {
- opacity: content.length !== 0 ? 1 : 0
- label: qsTr("Location:")
- content: {
- var file = rangeDetails.file;
- var pos = file.lastIndexOf("/");
- if (pos != -1)
- file = file.substr(pos+1);
- return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
+
+ Repeater {
+ model: eventInfo
+ Detail {
+ label: key
+ content: value
}
}
- Detail {
- visible: isBindingLoop
- opacity: content.length != 0 ? 1 : 0
- label: qsTr("Binding loop detected")
- }
}
}
@@ -194,7 +196,7 @@ Item {
drag.maximumY: root.candidateHeight - parent.height + yoffset
onClicked: {
root.gotoSourceLocation(file, line, column);
- root.recenterOnItem(view.selectedItem);
+ root.recenterOnItem(view.selectedModel, view.selectedItem);
}
}
diff --git a/src/plugins/qmlprofiler/qml/SelectionRange.qml b/src/plugins/qmlprofiler/qml/SelectionRange.qml
index cd8c986ddd..b302de614a 100644
--- a/src/plugins/qmlprofiler/qml/SelectionRange.qml
+++ b/src/plugins/qmlprofiler/qml/SelectionRange.qml
@@ -47,7 +47,7 @@ Rectangle {
property string endTimeString: detailedPrintTime(startTime+duration)
property string durationString: detailedPrintTime(duration)
- property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerDataModel.traceStartTime()
+ property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerModelProxy.traceStartTime()
property variant duration: width * selectionRange.viewTimePerPixel
property variant viewTimePerPixel: 1
property variant creationState : 0
diff --git a/src/plugins/qmlprofiler/qml/TimeMarks.qml b/src/plugins/qmlprofiler/qml/TimeMarks.qml
index bb0c07e400..f2ba65f66e 100644
--- a/src/plugins/qmlprofiler/qml/TimeMarks.qml
+++ b/src/plugins/qmlprofiler/qml/TimeMarks.qml
@@ -48,6 +48,11 @@ Canvas2D {
requestRedraw();
}
+ Connections {
+ target: labels
+ onHeightChanged: { requestRedraw(); }
+ }
+
onDrawRegion: {
drawBackgroundBars( ctxt, region );
@@ -86,19 +91,21 @@ Canvas2D {
}
}
+
// gray off out-of-bounds areas
var rectWidth;
- if (startTime < qmlProfilerDataModel.traceStartTime()) {
+ if (startTime < qmlProfilerModelProxy.traceStartTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)";
- rectWidth = (qmlProfilerDataModel.traceStartTime() - startTime) * spacing;
+ rectWidth = (qmlProfilerModelProxy.traceStartTime() - startTime) * spacing;
ctxt.fillRect(0, 0, rectWidth, height);
}
- if (endTime > qmlProfilerDataModel.traceEndTime()) {
+ if (endTime > qmlProfilerModelProxy.traceEndTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)";
- var rectX = (qmlProfilerDataModel.traceEndTime() - startTime) * spacing;
- rectWidth = (endTime - qmlProfilerDataModel.traceEndTime()) * spacing;
+ var rectX = (qmlProfilerModelProxy.traceEndTime() - startTime) * spacing;
+ rectWidth = (endTime - qmlProfilerModelProxy.traceEndTime()) * spacing;
ctxt.fillRect(rectX, 0, rectWidth, height);
}
+
}
function updateMarks(start, end) {
@@ -121,16 +128,16 @@ Canvas2D {
// separators
var cumulatedHeight = 0;
- for (var i=0; i<labels.rowCount; i++) {
- cumulatedHeight += root.singleRowHeight + (labels.rowExpanded[i] ?
- qmlProfilerDataModel.uniqueEventsOfType(i) * root.singleRowHeight :
- qmlProfilerDataModel.maxNestingForType(i) * root.singleRowHeight);
+ for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); modelIndex++) {
+ for (var i=0; i<qmlProfilerModelProxy.categoryCount(modelIndex); i++) {
+ cumulatedHeight += root.singleRowHeight * qmlProfilerModelProxy.categoryDepth(modelIndex, i);
- ctxt.strokeStyle = "#B0B0B0";
- ctxt.beginPath();
- ctxt.moveTo(0, cumulatedHeight);
- ctxt.lineTo(width, cumulatedHeight);
- ctxt.stroke();
+ ctxt.strokeStyle = "#B0B0B0";
+ ctxt.beginPath();
+ ctxt.moveTo(0, cumulatedHeight);
+ ctxt.lineTo(width, cumulatedHeight);
+ ctxt.stroke();
+ }
}
// bottom
diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro
index d644e47527..c9f7ba7ed7 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.pro
+++ b/src/plugins/qmlprofiler/qmlprofiler.pro
@@ -12,16 +12,26 @@ SOURCES += \
qmlprofilerattachdialog.cpp \
localqmlprofilerrunner.cpp \
qmlprofilereventview.cpp \
+ qv8profilereventview.cpp \
qmlprofilerdetailsrewriter.cpp \
qmlprofilertraceview.cpp \
timelinerenderer.cpp \
qmlprofilerstatemanager.cpp \
qv8profilerdatamodel.cpp \
- qmlprofilerdatamodel.cpp \
qmlprofilerclientmanager.cpp \
qmlprofilerviewmanager.cpp \
qmlprofilerstatewidget.cpp \
- qmlprofilerruncontrolfactory.cpp
+ qmlprofilerruncontrolfactory.cpp \
+ qmlprofilermodelmanager.cpp \
+ qmlprofilersimplemodel.cpp \
+ qmlprofilerprocessedmodel.cpp \
+ qmlprofilereventsmodelproxy.cpp \
+ qmlprofilertimelinemodelproxy.cpp \
+ qmlprofilertreeview.cpp \
+ qmlprofilertracefile.cpp \
+ abstracttimelinemodel.cpp \
+ timelinemodelaggregator.cpp \
+ qmlprofilerpainteventsmodelproxy.cpp
HEADERS += \
qmlprofilerconstants.h \
@@ -33,16 +43,26 @@ HEADERS += \
abstractqmlprofilerrunner.h \
localqmlprofilerrunner.h \
qmlprofilereventview.h \
+ qv8profilereventview.h \
qmlprofilerdetailsrewriter.h \
qmlprofilertraceview.h \
timelinerenderer.h \
qmlprofilerstatemanager.h \
qv8profilerdatamodel.h \
- qmlprofilerdatamodel.h \
qmlprofilerclientmanager.h \
qmlprofilerviewmanager.h \
qmlprofilerstatewidget.h \
- qmlprofilerruncontrolfactory.h
+ qmlprofilerruncontrolfactory.h \
+ qmlprofilermodelmanager.h \
+ qmlprofilersimplemodel.h \
+ qmlprofilerprocessedmodel.h \
+ qmlprofilereventsmodelproxy.h \
+ qmlprofilertimelinemodelproxy.h \
+ qmlprofilertreeview.h \
+ qmlprofilertracefile.h \
+ abstracttimelinemodel.h \
+ timelinemodelaggregator.h \
+ qmlprofilerpainteventsmodelproxy.h
RESOURCES += \
qml/qmlprofiler.qrc
diff --git a/src/plugins/qmlprofiler/qmlprofiler.qbs b/src/plugins/qmlprofiler/qmlprofiler.qbs
index 047b4f78c5..ca9a4a09dc 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.qbs
+++ b/src/plugins/qmlprofiler/qmlprofiler.qbs
@@ -22,6 +22,8 @@ QtcPlugin {
cpp.includePaths: base.concat("canvas")
files: [
+ "abstracttimelinemodel.h",
+ "abstracttimelinemodel.cpp",
"abstractqmlprofilerrunner.h",
"localqmlprofilerrunner.cpp",
"localqmlprofilerrunner.h",
@@ -31,32 +33,50 @@ QtcPlugin {
"qmlprofilerclientmanager.cpp",
"qmlprofilerclientmanager.h",
"qmlprofilerconstants.h",
- "qmlprofilerdatamodel.cpp",
- "qmlprofilerdatamodel.h",
"qmlprofilerdetailsrewriter.cpp",
"qmlprofilerdetailsrewriter.h",
"qmlprofilerengine.cpp",
"qmlprofilerengine.h",
+ "qmlprofilereventsmodelproxy.cpp",
+ "qmlprofilereventsmodelproxy.h",
"qmlprofilereventview.cpp",
"qmlprofilereventview.h",
+ "qmlprofilermodelmanager.cpp",
+ "qmlprofilermodelmanager.h",
"qmlprofilerplugin.cpp",
"qmlprofilerplugin.h",
"qmlprofilerruncontrolfactory.cpp",
"qmlprofilerruncontrolfactory.h",
+ "qmlprofilerprocessedmodel.cpp",
+ "qmlprofilerprocessedmodel.h",
+ "qmlprofilersimplemodel.cpp",
+ "qmlprofilersimplemodel.h",
"qmlprofilerstatemanager.cpp",
"qmlprofilerstatemanager.h",
"qmlprofilerstatewidget.cpp",
"qmlprofilerstatewidget.h",
+ "qmlprofilertimelinemodelproxy.cpp",
+ "qmlprofilertimelinemodelproxy.h",
"qmlprofilertool.cpp",
"qmlprofilertool.h",
+ "qmlprofilertreeview.cpp",
+ "qmlprofilertreeview.h",
+ "qmlprofilertracefile.cpp",
+ "qmlprofilertracefile.h",
"qmlprofilertraceview.cpp",
"qmlprofilertraceview.h",
"qmlprofilerviewmanager.cpp",
"qmlprofilerviewmanager.h",
+ "qv8profilereventview.h",
+ "qv8profilereventview.cpp",
"qv8profilerdatamodel.cpp",
"qv8profilerdatamodel.h",
+ "timelinemodelaggregator.cpp",
+ "timelinemodelaggregator.h",
"timelinerenderer.cpp",
"timelinerenderer.h",
+ "qmlprofilerpainteventsmodelproxy.h",
+ "qmlprofilerpainteventsmodelproxy.cpp",
"canvas/qdeclarativecanvas.cpp",
"canvas/qdeclarativecanvas_p.h",
"canvas/qdeclarativecanvastimer.cpp",
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
index 5d517e503c..926a382ce7 100644
--- a/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.cpp
@@ -40,16 +40,19 @@
#include <QTimer>
#include <QMessageBox>
+#include "qmlprofilermodelmanager.h"
+
using namespace QmlDebug;
using namespace Core;
namespace QmlProfiler {
namespace Internal {
-class QmlProfilerClientManager::QmlProfilerClientManagerPrivate
-{
+class QmlProfilerClientManager::QmlProfilerClientManagerPrivate {
public:
- QmlProfilerStateManager *profilerState;
+ QmlProfilerClientManagerPrivate(QmlProfilerClientManager *qq) { Q_UNUSED(qq); }
+
+ QmlProfilerStateManager* profilerState;
QmlDebugConnection *connection;
QPointer<QmlProfilerTraceClient> qmlclientplugin;
@@ -58,7 +61,9 @@ public:
QTimer connectionTimer;
int connectionAttempts;
- enum ConnectMode { TcpConnection, OstConnection };
+ enum ConnectMode {
+ TcpConnection, OstConnection
+ };
ConnectMode connectMode;
QString tcpHost;
quint64 tcpPort;
@@ -67,10 +72,12 @@ public:
bool v8DataReady;
bool qmlDataReady;
+
+ QmlProfilerModelManager *modelManager;
};
QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
- QObject(parent), d(new QmlProfilerClientManagerPrivate)
+ QObject(parent), d(new QmlProfilerClientManagerPrivate(this))
{
setObjectName(QLatin1String("QML Profiler Connections"));
@@ -81,6 +88,8 @@ QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
d->v8DataReady = false;
d->qmlDataReady = false;
+ d->modelManager = 0;
+
d->connectionTimer.setInterval(200);
connect(&d->connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
}
@@ -95,6 +104,17 @@ QmlProfilerClientManager::~QmlProfilerClientManager()
delete d;
}
+void QmlProfilerClientManager::setModelManager(QmlProfilerModelManager *m)
+{
+ if (d->modelManager) {
+ disconnect(this,SIGNAL(dataReadyForProcessing()), d->modelManager, SLOT(complete()));
+ }
+ d->modelManager = m;
+ if (d->modelManager) {
+ connect(this,SIGNAL(dataReadyForProcessing()), d->modelManager, SLOT(complete()));
+ }
+}
+
////////////////////////////////////////////////////////////////
// Interface
void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port)
@@ -159,15 +179,15 @@ void QmlProfilerClientManager::connectClientSignals()
connect(d->qmlclientplugin.data(), SIGNAL(complete()),
this, SLOT(qmlComplete()));
connect(d->qmlclientplugin.data(),
- SIGNAL(range(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
- this,
- SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
+ SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
+ qint64,qint64,qint64,qint64,qint64)),
+ d->modelManager,
+ SLOT(addQmlEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
+ qint64,qint64,qint64,qint64,qint64)));
connect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
- this, SIGNAL(traceFinished(qint64)));
+ d->modelManager->traceTime(), SLOT(setEndTime(qint64)));
connect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
- this, SIGNAL(traceStarted(qint64)));
- connect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
- this, SIGNAL(addFrameEvent(qint64,int,int)));
+ d->modelManager->traceTime(), SLOT(setStartTime(qint64)));
connect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
// fixme: this should be unified for both clients
@@ -178,8 +198,8 @@ void QmlProfilerClientManager::connectClientSignals()
connect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
connect(d->v8clientplugin.data(),
SIGNAL(v8range(int,QString,QString,int,double,double)),
- this,
- SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ d->modelManager,
+ SLOT(addV8Event(int,QString,QString,int,double,double)));
connect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
}
@@ -191,15 +211,17 @@ void QmlProfilerClientManager::disconnectClientSignals()
disconnect(d->qmlclientplugin.data(), SIGNAL(complete()),
this, SLOT(qmlComplete()));
disconnect(d->qmlclientplugin.data(),
- SIGNAL(range(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
- this,
- SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
+ SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
+ qint64,qint64,qint64,qint64,qint64)),
+ d->modelManager,
+ SLOT(addQmlEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
+ qint64,qint64,qint64,qint64,qint64)));
disconnect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
- this, SIGNAL(traceFinished(qint64)));
+ d->modelManager->traceTime(), SLOT(setEndTime(qint64)));
disconnect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
- this, SIGNAL(traceStarted(qint64)));
+ d->modelManager->traceTime(), SLOT(setStartTime(qint64)));
disconnect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
- this, SIGNAL(addFrameEvent(qint64,int,int)));
+ d->modelManager, SLOT(addFrameEvent(qint64,int,int)));
disconnect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
// fixme: this should be unified for both clients
@@ -210,8 +232,8 @@ void QmlProfilerClientManager::disconnectClientSignals()
disconnect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
disconnect(d->v8clientplugin.data(),
SIGNAL(v8range(int,QString,QString,int,double,double)),
- this,
- SIGNAL(addV8Event(int,QString,QString,int,double,double)));
+ d->modelManager,
+ SLOT(addV8Event(int,QString,QString,int,double,double)));
disconnect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
}
diff --git a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
index 518445d5f8..c1e2f22214 100644
--- a/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
+++ b/src/plugins/qmlprofiler/qmlprofilerclientmanager.h
@@ -37,12 +37,13 @@
#include <QStringList>
namespace QmlProfiler {
+class QmlProfilerModelManager;
+
namespace Internal {
class QmlProfilerClientManager : public QObject
{
Q_OBJECT
-
public:
explicit QmlProfilerClientManager(QObject *parent = 0);
~QmlProfilerClientManager();
@@ -56,16 +57,10 @@ public:
void discardPendingData();
bool isConnected() const;
+ void setModelManager(QmlProfilerModelManager *m);
signals:
void connectionFailed();
void connectionClosed();
-
- // data
- void addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation);
- void addV8Event(int,QString,QString,int,double,double);
- void addFrameEvent(qint64,int,int);
- void traceStarted(qint64);
- void traceFinished(qint64);
void dataReadyForProcessing();
public slots:
@@ -85,6 +80,9 @@ private slots:
void serverRecordingChanged();
private:
+ class QmlProfilerClientManagerPrivate;
+ QmlProfilerClientManagerPrivate *d;
+
void connectToClient();
void enableServices();
@@ -92,12 +90,9 @@ private:
void disconnectClientSignals();
void stopClientsRecording();
-
- class QmlProfilerClientManagerPrivate;
- QmlProfilerClientManagerPrivate *d;
};
-} // namespace Internal
-} // namespace QmlProfiler
+}
+}
#endif // QMLPROFILERCLIENTMANAGER_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
deleted file mode 100644
index 640b206d2b..0000000000
--- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
+++ /dev/null
@@ -1,1685 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#include "qmlprofilerdatamodel.h"
-
-#include <QUrl>
-#include <QHash>
-#include <QtAlgorithms>
-#include <QString>
-#include <QStringList>
-
-#include <QFile>
-#include <QXmlStreamReader>
-#include <QXmlStreamWriter>
-
-#include <QTimer>
-#include <utils/qtcassert.h>
-
-using namespace QmlDebug;
-
-namespace QmlProfiler {
-namespace Internal {
-
-typedef QHash <QString, QmlRangeEventRelative *> EventHash;
-
-static EventHash cloneEventHash(const EventHash &src)
-{
- EventHash result;
- const EventHash::ConstIterator cend = src.constEnd();
- for (EventHash::ConstIterator it = src.constBegin(); it != cend; ++it)
- result.insert(it.key(), new QmlRangeEventRelative(it.value()));
- return result;
-}
-
-///////////////////////////////////////////////////////////
-QmlRangeEventData::QmlRangeEventData()
-{
- eventType = MaximumQmlEventType;
- eventId = -1;
- duration = 0;
- calls = 0;
- minTime = 0;
- maxTime = 0;
- timePerCall = 0;
- percentOfTime = 0;
- medianTime = 0;
- isBindingLoop = false;
-}
-
-QmlRangeEventData::~QmlRangeEventData()
-{
- qDeleteAll(parentHash);
- parentHash.clear();
- qDeleteAll(childrenHash);
- childrenHash.clear();
-}
-
-QmlRangeEventData &QmlRangeEventData::operator=(const QmlRangeEventData &ref)
-{
- if (this == &ref)
- return *this;
-
- displayName = ref.displayName;
- location = ref.location;
- eventHashStr = ref.eventHashStr;
- details = ref.details;
- eventType = ref.eventType;
- duration = ref.duration;
- calls = ref.calls;
- minTime = ref.minTime;
- maxTime = ref.maxTime;
- timePerCall = ref.timePerCall;
- percentOfTime = ref.percentOfTime;
- medianTime = ref.medianTime;
- eventId = ref.eventId;
- isBindingLoop = ref.isBindingLoop;
-
- qDeleteAll(parentHash);
- parentHash = cloneEventHash(ref.parentHash);
-
- qDeleteAll(childrenHash);
- childrenHash = cloneEventHash(ref.childrenHash);
-
- return *this;
-}
-
-///////////////////////////////////////////////////////////
-
-// endtimedata
-struct QmlRangeEventEndInstance {
- qint64 endTime;
- int startTimeIndex;
- QmlRangeEventData *description;
-};
-
-// starttimedata
-struct QmlRangeEventStartInstance {
- qint64 startTime;
- qint64 duration;
- qint64 level;
- int endTimeIndex;
- qint64 nestingLevel;
- qint64 nestingDepth;
- QmlRangeEventData *statsInfo;
-
- int baseEventIndex;
-
- // animation-related data
- int frameRate;
- int animationCount;
-
- int bindingLoopHead;
-};
-
-struct QmlRangeEventTypeCount {
- QVector<int> eventIds;
- int nestingCount;
-};
-
-// used by quicksort
-bool compareEndTimes(const QmlRangeEventEndInstance &t1, const QmlRangeEventEndInstance &t2)
-{
- return t1.endTime < t2.endTime;
-}
-
-bool compareStartTimes(const QmlRangeEventStartInstance &t1, const QmlRangeEventStartInstance &t2)
-{
- return t1.startTime < t2.startTime;
-}
-
-bool compareStartIndexes(const QmlRangeEventEndInstance &t1, const QmlRangeEventEndInstance &t2)
-{
- return t1.startTimeIndex < t2.startTimeIndex;
-}
-
-//////////////////////////////////////////////////////////////
-class QmlProfilerDataModel::QmlProfilerDataModelPrivate
-{
-public:
- QmlProfilerDataModelPrivate(QmlProfilerDataModel *qq) : q(qq) {}
-
- QmlProfilerDataModel *q;
-
- // convenience functions
- void clearQmlRootEvent();
- void insertQmlRootEvent();
- void postProcess();
- void sortEndTimes();
- void findAnimationLimits();
- void sortStartTimes();
- void computeNestingLevels();
- void computeNestingDepth();
- void prepareForDisplay();
- void linkStartsToEnds();
- void linkEndsToStarts();
-
-
- // stats
- void clearStatistics();
- void redoTree(qint64 fromTime, qint64 toTime);
- void computeMedianTime(qint64 fromTime, qint64 toTime);
- void findBindingLoops(qint64 fromTime, qint64 toTime);
-
- QmlProfilerDataModel::State listState;
-
- // Stored data
- QHash<QString, QmlRangeEventData *> rangeEventDictionary;
- QVector<QmlRangeEventEndInstance> endInstanceList;
- QVector<QmlRangeEventStartInstance> startInstanceList;
-
- QmlRangeEventData qmlRootEvent;
-
- QV8ProfilerDataModel *v8DataModel;
-
- QHash<int, QmlRangeEventTypeCount *> typeCounts;
-
- qint64 traceEndTime;
- qint64 traceStartTime;
- qint64 qmlMeasuredTime;
-
- int lastFrameEventIndex;
- qint64 maxAnimationCount;
- qint64 minAnimationCount;
-
- // file to load
- QString fileName;
-};
-
-
-////////////////////////////////////////////////////////////////////////////////////
-
-
-QmlProfilerDataModel::QmlProfilerDataModel(QObject *parent) :
- QObject(parent), d(new QmlProfilerDataModelPrivate(this))
-{
- setObjectName(QLatin1String("QmlProfilerDataModel"));
-
- d->listState = Empty;
-
- d->traceEndTime = 0;
- d->traceStartTime = -1;
- d->qmlMeasuredTime = 0;
- d->clearQmlRootEvent();
- d->lastFrameEventIndex = -1;
- d->maxAnimationCount = 0;
- d->minAnimationCount = 0;
- d->v8DataModel = new QV8ProfilerDataModel(this, this);
-}
-
-QmlProfilerDataModel::~QmlProfilerDataModel()
-{
- clear();
- delete d;
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-QList<QmlRangeEventData *> QmlProfilerDataModel::getEventDescriptions() const
-{
- return d->rangeEventDictionary.values();
-}
-
-QmlRangeEventData *QmlProfilerDataModel::eventDescription(int eventId) const
-{
- foreach (QmlRangeEventData *event, d->rangeEventDictionary.values()) {
- if (event->eventId == eventId)
- return event;
- }
- return 0;
-}
-
-QList<QV8EventData *> QmlProfilerDataModel::getV8Events() const
-{
- return d->v8DataModel->getV8Events();
-}
-
-QV8EventData *QmlProfilerDataModel::v8EventDescription(int eventId) const
-{
- return d->v8DataModel->v8EventDescription(eventId);
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-void QmlProfilerDataModel::clear()
-{
- qDeleteAll(d->rangeEventDictionary);
- d->rangeEventDictionary.clear();
-
- d->endInstanceList.clear();
- d->startInstanceList.clear();
-
- d->clearQmlRootEvent();
-
- foreach (QmlRangeEventTypeCount *typeCount, d->typeCounts.values())
- delete typeCount;
- d->typeCounts.clear();
-
- d->traceEndTime = 0;
- d->traceStartTime = -1;
- d->qmlMeasuredTime = 0;
-
- d->lastFrameEventIndex = -1;
- d->maxAnimationCount = 0;
- d->minAnimationCount = 0;
-
- d->v8DataModel->clear();
-
- emit countChanged();
- setState(Empty);
-}
-
-void QmlProfilerDataModel::prepareForWriting()
-{
- setState(AcquiringData);
-}
-
-void QmlProfilerDataModel::addRangedEvent(int type, int bindingType, qint64 startTime,
- qint64 length, const QStringList &data,
- const QmlDebug::QmlEventLocation &location)
-{
- const QChar colon = QLatin1Char(':');
- QString displayName, eventHashStr, details;
- QmlDebug::QmlEventLocation eventLocation = location;
-
- setState(AcquiringData);
-
- // generate details string
- if (data.isEmpty()) {
- details = tr("Source code not available.");
- } else {
- details = data.join(QLatin1String(" ")).replace(QLatin1Char('\n'),QLatin1Char(' ')).simplified();
- QRegExp rewrite(QLatin1String("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
- bool match = rewrite.exactMatch(details);
- if (match)
- details = rewrite.cap(1) + QLatin1String(": ") + rewrite.cap(3);
- if (details.startsWith(QLatin1String("file://")))
- details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
- }
-
- // backwards compatibility: "compiling" events don't have a proper location in older
- // version of the protocol, but the filename is passed in the details string
- if ((type == QmlDebug::Creating || type == QmlDebug::Compiling) && eventLocation.filename.isEmpty()) {
- eventLocation.filename = details;
- eventLocation.line = 1;
- eventLocation.column = 1;
- }
-
- // generate hash
- if (eventLocation.filename.isEmpty()) {
- displayName = tr("<bytecode>");
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- } else {
- const QString filePath = QUrl(eventLocation.filename).path();
- displayName = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + colon +
- QString::number(eventLocation.line);
- eventHashStr = getHashStringForQmlEvent(eventLocation, type);
- }
-
- QmlRangeEventData *newEvent;
- if (d->rangeEventDictionary.contains(eventHashStr)) {
- newEvent = d->rangeEventDictionary[eventHashStr];
- } else {
- newEvent = new QmlRangeEventData;
- newEvent->displayName = displayName;
- newEvent->location = eventLocation;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = (QmlDebug::QmlEventType)type;
- newEvent->details = details;
- newEvent->bindingType = bindingType;
- d->rangeEventDictionary.insert(eventHashStr, newEvent);
- }
-
- QmlRangeEventEndInstance endTimeData;
- endTimeData.endTime = startTime + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->startInstanceList.count();
-
- QmlRangeEventStartInstance startTimeData;
- startTimeData.startTime = startTime;
- startTimeData.duration = length;
- startTimeData.statsInfo = newEvent;
- startTimeData.endTimeIndex = d->endInstanceList.count();
- startTimeData.animationCount = -1;
- startTimeData.frameRate = 1e9/length;
- startTimeData.baseEventIndex = d->startInstanceList.count(); // point to itself by default
-
- d->endInstanceList << endTimeData;
- d->startInstanceList << startTimeData;
-
- emit countChanged();
-}
-
-void QmlProfilerDataModel::addV8Event(int depth, const QString &function,
- const QString &filename, int lineNumber,
- double totalTime, double selfTime)
-{
- d->v8DataModel->addV8Event(depth, function, filename, lineNumber, totalTime, selfTime);
-}
-
-void QmlProfilerDataModel::addFrameEvent(qint64 time, int framerate, int animationcount)
-{
- QString displayName, eventHashStr, details;
-
- setState(AcquiringData);
-
- details = tr("Animation Timer Update");
- displayName = tr("<Animation Update>");
- eventHashStr = displayName;
-
- QmlRangeEventData *newEvent;
- if (d->rangeEventDictionary.contains(eventHashStr)) {
- newEvent = d->rangeEventDictionary[eventHashStr];
- } else {
- newEvent = new QmlRangeEventData;
- newEvent->displayName = displayName;
- newEvent->eventHashStr = eventHashStr;
- newEvent->eventType = QmlDebug::Painting;
- newEvent->details = details;
- d->rangeEventDictionary.insert(eventHashStr, newEvent);
- }
-
- qint64 length = 1e9/framerate;
- // avoid overlap
- QmlRangeEventStartInstance *lastFrameEvent = 0;
- if (d->lastFrameEventIndex > -1) {
- lastFrameEvent = &d->startInstanceList[d->lastFrameEventIndex];
- if (lastFrameEvent->startTime + lastFrameEvent->duration >= time) {
- lastFrameEvent->duration = time - 1 - lastFrameEvent->startTime;
- d->endInstanceList[lastFrameEvent->endTimeIndex].endTime =
- lastFrameEvent->startTime + lastFrameEvent->duration;
- }
- }
-
- QmlRangeEventEndInstance endTimeData;
- endTimeData.endTime = time + length;
- endTimeData.description = newEvent;
- endTimeData.startTimeIndex = d->startInstanceList.count();
-
- QmlRangeEventStartInstance startTimeData;
- startTimeData.startTime = time;
- startTimeData.duration = length;
- startTimeData.statsInfo = newEvent;
- startTimeData.endTimeIndex = d->endInstanceList.count();
- startTimeData.animationCount = animationcount;
- startTimeData.frameRate = framerate;
- startTimeData.baseEventIndex = d->startInstanceList.count(); // point to itself by default
-
- d->endInstanceList << endTimeData;
- d->startInstanceList << startTimeData;
-
- d->lastFrameEventIndex = d->startInstanceList.count() - 1;
-
- emit countChanged();
-}
-
-void QmlProfilerDataModel::setTraceEndTime(qint64 time)
-{
- d->traceEndTime = time;
-}
-
-void QmlProfilerDataModel::setTraceStartTime(qint64 time)
-{
- d->traceStartTime = time;
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-QString QmlProfilerDataModel::getHashStringForQmlEvent(
- const QmlDebug::QmlEventLocation &location, int eventType)
-{
- return QString::fromLatin1("%1:%2:%3:%4").arg(location.filename,
- QString::number(location.line),
- QString::number(location.column),
- QString::number(eventType));
-}
-
-QString QmlProfilerDataModel::getHashStringForV8Event(const QString &displayName,
- const QString &function)
-{
- return QString::fromLatin1("%1:%2").arg(displayName, function);
-}
-
-QString QmlProfilerDataModel::rootEventName()
-{
- return tr("<program>");
-}
-
-QString QmlProfilerDataModel::rootEventDescription()
-{
- return tr("Main Program");
-}
-
-QString QmlProfilerDataModel::qmlEventTypeAsString(QmlEventType typeEnum)
-{
- switch (typeEnum) {
- case Painting:
- return QLatin1String(Constants::TYPE_PAINTING_STR);
- break;
- case Compiling:
- return QLatin1String(Constants::TYPE_COMPILING_STR);
- break;
- case Creating:
- return QLatin1String(Constants::TYPE_CREATING_STR);
- break;
- case Binding:
- return QLatin1String(Constants::TYPE_BINDING_STR);
- break;
- case HandlingSignal:
- return QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR);
- break;
- default:
- return QString::number((int)typeEnum);
- }
-}
-
-QmlEventType QmlProfilerDataModel::qmlEventTypeAsEnum(const QString &typeString)
-{
- if (typeString == QLatin1String(Constants::TYPE_PAINTING_STR)) {
- return Painting;
- } else if (typeString == QLatin1String(Constants::TYPE_COMPILING_STR)) {
- return Compiling;
- } else if (typeString == QLatin1String(Constants::TYPE_CREATING_STR)) {
- return Creating;
- } else if (typeString == QLatin1String(Constants::TYPE_BINDING_STR)) {
- return Binding;
- } else if (typeString == QLatin1String(Constants::TYPE_HANDLINGSIGNAL_STR)) {
- return HandlingSignal;
- } else {
- bool isNumber = false;
- int type = typeString.toUInt(&isNumber);
- if (isNumber)
- return (QmlEventType)type;
- else
- return MaximumQmlEventType;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-int QmlProfilerDataModel::findFirstIndex(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->endInstanceList.isEmpty())
- return 0; // -1
- if (d->endInstanceList.count() == 1 || d->endInstanceList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->endInstanceList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1)
- {
- int fromIndex = 0;
- int toIndex = d->endInstanceList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->endInstanceList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int eventIndex = d->endInstanceList[candidate].startTimeIndex;
- return d->startInstanceList[eventIndex].baseEventIndex;
-}
-
-int QmlProfilerDataModel::findFirstIndexNoParents(qint64 startTime) const
-{
- int candidate = -1;
- // in the "endtime" list, find the first event that ends after startTime
- if (d->endInstanceList.isEmpty())
- return 0; // -1
- if (d->endInstanceList.count() == 1 || d->endInstanceList.first().endTime >= startTime)
- candidate = 0;
- else
- if (d->endInstanceList.last().endTime <= startTime)
- return 0; // -1
-
- if (candidate == -1) {
- int fromIndex = 0;
- int toIndex = d->endInstanceList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->endInstanceList[midIndex].endTime < startTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- candidate = toIndex;
- }
-
- int ndx = d->endInstanceList[candidate].startTimeIndex;
-
- return ndx;
-}
-
-int QmlProfilerDataModel::findLastIndex(qint64 endTime) const
-{
- // in the "starttime" list, find the last event that starts before endtime
- if (d->startInstanceList.isEmpty())
- return 0; // -1
- if (d->startInstanceList.first().startTime >= endTime)
- return 0; // -1
- if (d->startInstanceList.count() == 1)
- return 0;
- if (d->startInstanceList.last().startTime <= endTime)
- return d->startInstanceList.count()-1;
-
- int fromIndex = 0;
- int toIndex = d->startInstanceList.count()-1;
- while (toIndex - fromIndex > 1) {
- int midIndex = (fromIndex + toIndex)/2;
- if (d->startInstanceList[midIndex].startTime < endTime)
- fromIndex = midIndex;
- else
- toIndex = midIndex;
- }
-
- return fromIndex;
-}
-
-qint64 QmlProfilerDataModel::firstTimeMark() const
-{
- if (d->startInstanceList.isEmpty())
- return 0;
- return d->startInstanceList[0].startTime;
-}
-
-qint64 QmlProfilerDataModel::lastTimeMark() const
-{
- if (d->endInstanceList.isEmpty())
- return 0;
- return d->endInstanceList.last().endTime;
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-int QmlProfilerDataModel::count() const
-{
- return d->startInstanceList.count();
-}
-
-bool QmlProfilerDataModel::isEmpty() const
-{
- return d->startInstanceList.isEmpty() && d->v8DataModel->isEmpty();
-}
-
-qint64 QmlProfilerDataModel::getStartTime(int index) const
-{
- return d->startInstanceList[index].startTime;
-}
-
-qint64 QmlProfilerDataModel::getEndTime(int index) const
-{
- return d->startInstanceList[index].startTime + d->startInstanceList[index].duration;
-}
-
-qint64 QmlProfilerDataModel::getDuration(int index) const
-{
- return d->startInstanceList[index].duration;
-}
-
-int QmlProfilerDataModel::getType(int index) const
-{
- return d->startInstanceList[index].statsInfo->eventType;
-}
-
-int QmlProfilerDataModel::getNestingLevel(int index) const
-{
- return d->startInstanceList[index].nestingLevel;
-}
-
-int QmlProfilerDataModel::getNestingDepth(int index) const
-{
- return d->startInstanceList[index].nestingDepth;
-}
-
-QString QmlProfilerDataModel::getFilename(int index) const
-{
- return d->startInstanceList[index].statsInfo->location.filename;
-}
-
-int QmlProfilerDataModel::getLine(int index) const
-{
- return d->startInstanceList[index].statsInfo->location.line;
-}
-
-int QmlProfilerDataModel::getColumn(int index) const
-{
- return d->startInstanceList[index].statsInfo->location.column;
-}
-
-QString QmlProfilerDataModel::getDetails(int index) const
-{
- // special: animations
- if (d->startInstanceList[index].statsInfo->eventType == QmlDebug::Painting &&
- d->startInstanceList[index].animationCount >= 0)
- return tr("%1 animations at %2 FPS.").arg(
- QString::number(d->startInstanceList[index].animationCount),
- QString::number(d->startInstanceList[index].frameRate));
- return d->startInstanceList[index].statsInfo->details;
-}
-
-int QmlProfilerDataModel::getEventId(int index) const
-{
- return d->startInstanceList[index].statsInfo->eventId;
-}
-
-int QmlProfilerDataModel::getBindingLoopDest(int index) const
-{
- return d->startInstanceList[index].bindingLoopHead;
-}
-
-int QmlProfilerDataModel::getFramerate(int index) const
-{
- return d->startInstanceList[index].frameRate;
-}
-
-int QmlProfilerDataModel::getAnimationCount(int index) const
-{
- return d->startInstanceList[index].animationCount;
-}
-
-int QmlProfilerDataModel::getMaximumAnimationCount() const
-{
- return d->maxAnimationCount;
-}
-
-int QmlProfilerDataModel::getMinimumAnimationCount() const
-{
- return d->minAnimationCount;
-}
-
-/////////////////////////////////////////
-
-int QmlProfilerDataModel::uniqueEventsOfType(int type) const
-{
- if (!d->typeCounts.contains(type))
- return 0;
- return d->typeCounts[type]->eventIds.count();
-}
-
-int QmlProfilerDataModel::maxNestingForType(int type) const
-{
- if (!d->typeCounts.contains(type))
- return 0;
- return d->typeCounts[type]->nestingCount;
-}
-
-QString QmlProfilerDataModel::eventTextForType(int type, int index) const
-{
- if (!d->typeCounts.contains(type))
- return QString();
- return d->rangeEventDictionary.values().at(d->typeCounts[type]->eventIds[index])->details;
-}
-
-QString QmlProfilerDataModel::eventDisplayNameForType(int type, int index) const
-{
- if (!d->typeCounts.contains(type))
- return QString();
- return d->rangeEventDictionary.values().at(d->typeCounts[type]->eventIds[index])->displayName;
-}
-
-int QmlProfilerDataModel::eventIdForType(int type, int index) const
-{
- if (!d->typeCounts.contains(type))
- return -1;
- return d->typeCounts[type]->eventIds[index];
-}
-
-int QmlProfilerDataModel::eventPosInType(int index) const
-{
- int eventType = d->startInstanceList[index].statsInfo->eventType;
- return d->typeCounts[eventType]->eventIds.indexOf(d->startInstanceList[index].statsInfo->eventId);
-}
-
-/////////////////////////////////////////
-
-qint64 QmlProfilerDataModel::traceStartTime() const
-{
- return d->traceStartTime != -1? d->traceStartTime : firstTimeMark();
-}
-
-qint64 QmlProfilerDataModel::traceEndTime() const
-{
- return d->traceEndTime ? d->traceEndTime : lastTimeMark();
-}
-
-qint64 QmlProfilerDataModel::traceDuration() const
-{
- return traceEndTime() - traceStartTime();
-}
-
-qint64 QmlProfilerDataModel::qmlMeasuredTime() const
-{
- return d->qmlMeasuredTime;
-}
-qint64 QmlProfilerDataModel::v8MeasuredTime() const
-{
- return d->v8DataModel->v8MeasuredTime();
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-void QmlProfilerDataModel::complete()
-{
- if (currentState() == AcquiringData) {
- setState(ProcessingData);
- d->v8DataModel->collectV8Statistics();
- d->postProcess();
- } else
- if (currentState() == Empty) {
- d->v8DataModel->collectV8Statistics();
- compileStatistics(traceStartTime(), traceEndTime());
- setState(Done);
- } else
- if (currentState() == Done) {
- // ignore duplicated complete signals
- } else {
- emit error(tr("Unexpected complete signal in data model."));
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::postProcess()
-{
- if (q->count() != 0) {
- sortStartTimes();
- sortEndTimes();
- findAnimationLimits();
- computeNestingLevels();
- computeNestingDepth();
- linkEndsToStarts();
- insertQmlRootEvent();
- q->reloadDetails();
- prepareForDisplay();
- q->compileStatistics(q->traceStartTime(), q->traceEndTime());
- }
- q->setState(Done);
-}
-
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::prepareForDisplay()
-{
- // generate numeric ids
- int ndx = 0;
- foreach (QmlRangeEventData *binding, rangeEventDictionary.values()) {
- binding->eventId = ndx++;
- }
-
- // collect type counts
- foreach (const QmlRangeEventStartInstance &eventStartData, startInstanceList) {
- int typeNumber = eventStartData.statsInfo->eventType;
- if (!typeCounts.contains(typeNumber)) {
- typeCounts[typeNumber] = new QmlRangeEventTypeCount;
- typeCounts[typeNumber]->nestingCount = 0;
- }
- if (eventStartData.nestingLevel > typeCounts[typeNumber]->nestingCount)
- typeCounts[typeNumber]->nestingCount = eventStartData.nestingLevel;
- if (!typeCounts[typeNumber]->eventIds.contains(eventStartData.statsInfo->eventId))
- typeCounts[typeNumber]->eventIds << eventStartData.statsInfo->eventId;
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::sortStartTimes()
-{
- if (startInstanceList.count() < 2)
- return;
-
- // assuming startTimes is partially sorted
- // identify blocks of events and sort them with quicksort
- QVector<QmlRangeEventStartInstance>::iterator itFrom = startInstanceList.end() - 2;
- QVector<QmlRangeEventStartInstance>::iterator itTo = startInstanceList.end() - 1;
-
- while (itFrom != startInstanceList.begin() && itTo != startInstanceList.begin()) {
- // find block to sort
- while (itFrom != startInstanceList.begin()
- && itTo->startTime > itFrom->startTime) {
- itTo--;
- itFrom = itTo - 1;
- }
-
- // if we're at the end of the list
- if (itFrom == startInstanceList.begin())
- break;
-
- // find block length
- while (itFrom != startInstanceList.begin()
- && itTo->startTime <= itFrom->startTime)
- itFrom--;
-
- if (itTo->startTime <= itFrom->startTime)
- qSort(itFrom, itTo + 1, compareStartTimes);
- else
- qSort(itFrom + 1, itTo + 1, compareStartTimes);
-
- // move to next block
- itTo = itFrom;
- itFrom = itTo - 1;
- }
-
- // link back the endTimes
- linkEndsToStarts();
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::sortEndTimes()
-{
- // assuming endTimes is partially sorted
- // identify blocks of events and sort them with quicksort
-
- if (endInstanceList.count() < 2)
- return;
-
- QVector<QmlRangeEventEndInstance>::iterator itFrom = endInstanceList.begin();
- QVector<QmlRangeEventEndInstance>::iterator itTo = endInstanceList.begin() + 1;
-
- while (itTo != endInstanceList.end() && itFrom != endInstanceList.end()) {
- // find block to sort
- while (itTo != endInstanceList.end()
- && startInstanceList[itTo->startTimeIndex].startTime >
- startInstanceList[itFrom->startTimeIndex].startTime +
- startInstanceList[itFrom->startTimeIndex].duration) {
- itFrom++;
- itTo = itFrom+1;
- }
-
- // if we're at the end of the list
- if (itTo == endInstanceList.end())
- break;
-
- // find block length
- while (itTo != endInstanceList.end()
- && startInstanceList[itTo->startTimeIndex].startTime <=
- startInstanceList[itFrom->startTimeIndex].startTime +
- startInstanceList[itFrom->startTimeIndex].duration)
- itTo++;
-
- // sort block
- qSort(itFrom, itTo, compareEndTimes);
-
- // move to next block
- itFrom = itTo;
- itTo = itFrom+1;
-
- }
-
- // link back the startTimes
- linkStartsToEnds();
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::linkStartsToEnds()
-{
- for (int i = 0; i < endInstanceList.count(); i++)
- startInstanceList[endInstanceList[i].startTimeIndex].endTimeIndex = i;
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::findAnimationLimits()
-{
- maxAnimationCount = 0;
- minAnimationCount = 0;
- lastFrameEventIndex = -1;
-
- for (int i = 0; i < startInstanceList.count(); i++) {
- if (startInstanceList[i].statsInfo->eventType == QmlDebug::Painting &&
- startInstanceList[i].animationCount >= 0) {
- int animationcount = startInstanceList[i].animationCount;
- if (lastFrameEventIndex > -1) {
- if (animationcount > maxAnimationCount)
- maxAnimationCount = animationcount;
- if (animationcount < minAnimationCount)
- minAnimationCount = animationcount;
- } else {
- maxAnimationCount = animationcount;
- minAnimationCount = animationcount;
- }
- lastFrameEventIndex = i;
- }
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeNestingLevels()
-{
- // compute levels
- QHash<int, qint64> endtimesPerLevel;
- QList<int> nestingLevels;
- QList< QHash<int, qint64> > endtimesPerNestingLevel;
- int level = Constants::QML_MIN_LEVEL;
- endtimesPerLevel[Constants::QML_MIN_LEVEL] = 0;
- int lastBaseEventIndex = 0;
- qint64 lastBaseEventEndTime = traceStartTime;
-
- for (int i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
- nestingLevels << Constants::QML_MIN_LEVEL;
- QHash<int, qint64> dummyHash;
- dummyHash[Constants::QML_MIN_LEVEL] = 0;
- endtimesPerNestingLevel << dummyHash;
- }
-
- for (int i=0; i<startInstanceList.count(); i++) {
- qint64 st = startInstanceList[i].startTime;
- int type = startInstanceList[i].statsInfo->eventType;
-
- if (type == QmlDebug::Painting) {
- // animation/paint events have level 0 by definition (same as "mainprogram"),
- // but are not considered parents of other events for statistical purposes
- startInstanceList[i].level = Constants::QML_MIN_LEVEL - 1;
- startInstanceList[i].nestingLevel = Constants::QML_MIN_LEVEL;
- if (lastBaseEventEndTime < startInstanceList[i].startTime) {
- lastBaseEventIndex = i;
- lastBaseEventEndTime = startInstanceList[i].startTime + startInstanceList[i].duration;
- }
- startInstanceList[i].baseEventIndex = lastBaseEventIndex;
- continue;
- }
-
- // general level
- if (endtimesPerLevel[level] > st) {
- level++;
- } else {
- while (level > Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st)
- level--;
- }
- endtimesPerLevel[level] = st + startInstanceList[i].duration;
-
- // per type
- if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
- nestingLevels[type]++;
- } else {
- while (nestingLevels[type] > Constants::QML_MIN_LEVEL &&
- endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
- nestingLevels[type]--;
- }
- endtimesPerNestingLevel[type][nestingLevels[type]] =
- st + startInstanceList[i].duration;
-
- startInstanceList[i].level = level;
- startInstanceList[i].nestingLevel = nestingLevels[type];
-
- if (level == Constants::QML_MIN_LEVEL) {
- qmlMeasuredTime += startInstanceList[i].duration;
- if (lastBaseEventEndTime < startInstanceList[i].startTime) {
- lastBaseEventIndex = i;
- lastBaseEventEndTime = startInstanceList[i].startTime + startInstanceList[i].duration;
- }
- }
- startInstanceList[i].baseEventIndex = lastBaseEventIndex;
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeNestingDepth()
-{
- QHash<int, int> nestingDepth;
- for (int i = 0; i < endInstanceList.count(); i++) {
- int type = endInstanceList[i].description->eventType;
- int nestingInType = startInstanceList[endInstanceList[i].startTimeIndex].nestingLevel;
- if (!nestingDepth.contains(type)) {
- nestingDepth[type] = nestingInType;
- } else {
- int nd = nestingDepth[type];
- nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
- }
-
- startInstanceList[endInstanceList[i].startTimeIndex].nestingDepth = nestingDepth[type];
- if (nestingInType == Constants::QML_MIN_LEVEL)
- nestingDepth[type] = Constants::QML_MIN_LEVEL;
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::linkEndsToStarts()
-{
- for (int i = 0; i < startInstanceList.count(); i++)
- endInstanceList[startInstanceList[i].endTimeIndex].startTimeIndex = i;
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-void QmlProfilerDataModel::compileStatistics(qint64 startTime, qint64 endTime)
-{
- d->clearStatistics();
- if (traceDuration() > 0) {
- if (count() > 0) {
- d->redoTree(startTime, endTime);
- d->computeMedianTime(startTime, endTime);
- d->findBindingLoops(startTime, endTime);
- } else {
- d->insertQmlRootEvent();
- QmlRangeEventData *listedRootEvent = d->rangeEventDictionary.value(rootEventName());
- listedRootEvent->calls = 1;
- listedRootEvent->percentOfTime = 100;
- }
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::clearStatistics()
-{
- // clear existing statistics
- foreach (QmlRangeEventData *eventDescription, rangeEventDictionary.values()) {
- eventDescription->calls = 0;
- // maximum possible value
- eventDescription->minTime = traceEndTime;
- eventDescription->maxTime = 0;
- eventDescription->medianTime = 0;
- eventDescription->duration = 0;
- qDeleteAll(eventDescription->parentHash);
- qDeleteAll(eventDescription->childrenHash);
- eventDescription->parentHash.clear();
- eventDescription->childrenHash.clear();
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::redoTree(qint64 fromTime,
- qint64 toTime)
-{
- double totalTime = 0;
- int fromIndex = q->findFirstIndex(fromTime);
- int toIndex = q->findLastIndex(toTime);
-
- QmlRangeEventData *listedRootEvent = rangeEventDictionary.value(rootEventName());
- QTC_ASSERT(listedRootEvent, /**/);
-
- // compute parent-child relationship and call count
- QHash<int, QmlRangeEventData*> lastParent;
- for (int index = fromIndex; index <= toIndex; index++) {
- QmlRangeEventData *eventDescription = startInstanceList[index].statsInfo;
-
- if (startInstanceList[index].startTime > toTime ||
- startInstanceList[index].startTime+startInstanceList[index].duration < fromTime) {
- continue;
- }
-
- if (eventDescription->eventType == QmlDebug::Painting) {
- // skip animation/paint events
- continue;
- }
-
- eventDescription->calls++;
- qint64 duration = startInstanceList[index].duration;
- eventDescription->duration += duration;
- if (eventDescription->maxTime < duration)
- eventDescription->maxTime = duration;
- if (eventDescription->minTime > duration)
- eventDescription->minTime = duration;
-
- int level = startInstanceList[index].level;
-
- QmlRangeEventData *parentEvent = listedRootEvent;
- if (level > Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
- parentEvent = lastParent[level-1];
-
- if (!eventDescription->parentHash.contains(parentEvent->eventHashStr)) {
- QmlRangeEventRelative *newParentEvent = new QmlRangeEventRelative(parentEvent);
- newParentEvent->calls = 1;
- newParentEvent->duration = duration;
-
- eventDescription->parentHash.insert(parentEvent->eventHashStr, newParentEvent);
- } else {
- QmlRangeEventRelative *newParentEvent =
- eventDescription->parentHash.value(parentEvent->eventHashStr);
- newParentEvent->duration += duration;
- newParentEvent->calls++;
- }
-
- if (!parentEvent->childrenHash.contains(eventDescription->eventHashStr)) {
- QmlRangeEventRelative *newChildEvent = new QmlRangeEventRelative(eventDescription);
- newChildEvent->calls = 1;
- newChildEvent->duration = duration;
-
- parentEvent->childrenHash.insert(eventDescription->eventHashStr, newChildEvent);
- } else {
- QmlRangeEventRelative *newChildEvent =
- parentEvent->childrenHash.value(eventDescription->eventHashStr);
- newChildEvent->duration += duration;
- newChildEvent->calls++;
- }
-
- lastParent[level] = eventDescription;
-
- if (level == Constants::QML_MIN_LEVEL)
- totalTime += duration;
- }
-
- // fake rootEvent statistics
- // the +1 nanosecond is to force it to be on top of the sorted list
- listedRootEvent->duration = totalTime+1;
- listedRootEvent->minTime = totalTime+1;
- listedRootEvent->maxTime = totalTime+1;
- listedRootEvent->medianTime = totalTime+1;
- if (totalTime > 0)
- listedRootEvent->calls = 1;
-
- // copy to the global root reference
- qmlRootEvent = *listedRootEvent;
-
- // compute percentages
- foreach (QmlRangeEventData *binding, rangeEventDictionary.values()) {
- binding->percentOfTime = binding->duration * 100.0 / totalTime;
- binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::computeMedianTime(qint64 fromTime,
- qint64 toTime)
-{
- int fromIndex = q->findFirstIndex(fromTime);
- int toIndex = q->findLastIndex(toTime);
-
- // compute median time
- QHash< QmlRangeEventData* , QList<qint64> > durationLists;
- for (int index = fromIndex; index <= toIndex; index++) {
- QmlRangeEventData *desc = startInstanceList[index].statsInfo;
- qint64 len = startInstanceList[index].duration;
- durationLists[desc].append(len);
- }
- QMutableHashIterator < QmlRangeEventData* , QList<qint64> > iter(durationLists);
- while (iter.hasNext()) {
- iter.next();
- if (!iter.value().isEmpty()) {
- qSort(iter.value());
- iter.key()->medianTime = iter.value().at(iter.value().count()/2);
- }
- }
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::findBindingLoops(qint64 fromTime,
- qint64 toTime)
-{
- int fromIndex = q->findFirstIndex(fromTime);
- int toIndex = q->findLastIndex(toTime);
-
- // first clear existing data
- foreach (QmlRangeEventData *event, rangeEventDictionary.values()) {
- event->isBindingLoop = false;
- foreach (QmlRangeEventRelative *parentEvent, event->parentHash.values())
- parentEvent->inLoopPath = false;
- foreach (QmlRangeEventRelative *childEvent, event->childrenHash.values())
- childEvent->inLoopPath = false;
- }
-
- QList<QmlRangeEventData *> stackRefs;
- QList<QmlRangeEventStartInstance *> stack;
-
- for (int i = 0; i < startInstanceList.count(); i++) {
- QmlRangeEventData *currentEvent = startInstanceList[i].statsInfo;
- QmlRangeEventStartInstance *inTimeEvent = &startInstanceList[i];
- inTimeEvent->bindingLoopHead = -1;
-
- // special: skip animation events (but not old paint events)
- if (inTimeEvent->statsInfo->eventType == Painting && inTimeEvent->animationCount >= 0)
- continue;
-
- // managing call stack
- for (int j = stack.count() - 1; j >= 0; j--) {
- if (stack[j]->startTime + stack[j]->duration <= inTimeEvent->startTime) {
- stack.removeAt(j);
- stackRefs.removeAt(j);
- }
- }
-
- bool loopDetected = stackRefs.contains(currentEvent);
- stack << inTimeEvent;
- stackRefs << currentEvent;
-
- // skip loops if bindings are anonymous
- if (loopDetected && !currentEvent->location.filename.isEmpty()) {
- if (i >= fromIndex && i <= toIndex) {
- // for the statistics
- currentEvent->isBindingLoop = true;
- for (int j = stackRefs.indexOf(currentEvent); j < stackRefs.count()-1; j++) {
- QmlRangeEventRelative *nextEventSub =
- stackRefs[j]->childrenHash.value(stackRefs[j+1]->eventHashStr);
- nextEventSub->inLoopPath = true;
- QmlRangeEventRelative *prevEventSub =
- stackRefs[j+1]->parentHash.value(stackRefs[j]->eventHashStr);
- prevEventSub->inLoopPath = true;
- }
- }
-
- // use crossed references to find index in starttimesortedlist
- QmlRangeEventStartInstance *head = stack[stackRefs.indexOf(currentEvent)];
- inTimeEvent->bindingLoopHead = endInstanceList[head->endTimeIndex].startTimeIndex;
- startInstanceList[inTimeEvent->bindingLoopHead].bindingLoopHead = i;
- }
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::clearQmlRootEvent()
-{
- qmlRootEvent.displayName = rootEventName();
- qmlRootEvent.location = QmlEventLocation();
- qmlRootEvent.eventHashStr = rootEventName();
- qmlRootEvent.details = rootEventDescription();
- qmlRootEvent.eventType = QmlDebug::Binding;
- qmlRootEvent.duration = 0;
- qmlRootEvent.calls = 0;
- qmlRootEvent.minTime = 0;
- qmlRootEvent.maxTime = 0;
- qmlRootEvent.timePerCall = 0;
- qmlRootEvent.percentOfTime = 0;
- qmlRootEvent.medianTime = 0;
- qmlRootEvent.eventId = -1;
-
- qDeleteAll(qmlRootEvent.parentHash);
- qDeleteAll(qmlRootEvent.childrenHash);
- qmlRootEvent.parentHash.clear();
- qmlRootEvent.childrenHash.clear();
-}
-
-void QmlProfilerDataModel::QmlProfilerDataModelPrivate::insertQmlRootEvent()
-{
- // create root event for statistics & insert into list
- clearQmlRootEvent();
- QmlRangeEventData *listedRootEvent = rangeEventDictionary.value(rootEventName());
- if (!listedRootEvent) {
- listedRootEvent = new QmlRangeEventData;
- rangeEventDictionary.insert(rootEventName(), listedRootEvent);
- }
- *listedRootEvent = qmlRootEvent;
-}
-
-void QmlProfilerDataModel::reloadDetails()
-{
- // request binding/signal details from the AST
- foreach (QmlRangeEventData *event, d->rangeEventDictionary.values()) {
- if (event->eventType != Binding && event->eventType != HandlingSignal)
- continue;
-
- // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
- if (event->location.filename.isEmpty())
- continue;
-
- // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
- if (event->location.column == -1)
- continue;
-
- emit requestDetailsForLocation(event->eventType, event->location);
- }
- emit reloadDocumentsForDetails();
-}
-
-void QmlProfilerDataModel::rewriteDetailsString(int eventType,
- const QmlDebug::QmlEventLocation &location,
- const QString &newString)
-{
- QString eventHashStr = getHashStringForQmlEvent(location, eventType);
- QTC_ASSERT(d->rangeEventDictionary.contains(eventHashStr), return);
- d->rangeEventDictionary.value(eventHashStr)->details = newString;
- emit detailsChanged(d->rangeEventDictionary.value(eventHashStr)->eventId, newString);
-}
-
-void QmlProfilerDataModel::finishedRewritingDetails()
-{
- emit reloadDetailLabels();
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-bool QmlProfilerDataModel::save(const QString &filename)
-{
- if (isEmpty()) {
- emit error(tr("No data to save."));
- return false;
- }
-
- QFile file(filename);
- if (!file.open(QIODevice::WriteOnly)) {
- emit error(tr("Could not open %1 for writing.").arg(filename));
- return false;
- }
-
- QXmlStreamWriter stream(&file);
- stream.setAutoFormatting(true);
- stream.writeStartDocument();
-
- stream.writeStartElement(QLatin1String("trace"));
- stream.writeAttribute(QLatin1String("version"), QLatin1String(Constants::PROFILER_FILE_VERSION));
-
- stream.writeAttribute(QLatin1String("traceStart"), QString::number(traceStartTime()));
- stream.writeAttribute(QLatin1String("traceEnd"), QString::number(traceEndTime()));
-
- stream.writeStartElement(QLatin1String("eventData"));
- stream.writeAttribute(QLatin1String("totalTime"), QString::number(d->qmlMeasuredTime));
-
- foreach (const QmlRangeEventData *eventData, d->rangeEventDictionary.values()) {
- stream.writeStartElement(QLatin1String("event"));
- stream.writeAttribute(QLatin1String("index"), QString::number(d->rangeEventDictionary.keys().indexOf(eventData->eventHashStr)));
- stream.writeTextElement(QLatin1String("displayname"), eventData->displayName);
- stream.writeTextElement(QLatin1String("type"), qmlEventTypeAsString(eventData->eventType));
- if (!eventData->location.filename.isEmpty()) {
- stream.writeTextElement(QLatin1String("filename"), eventData->location.filename);
- stream.writeTextElement(QLatin1String("line"), QString::number(eventData->location.line));
- stream.writeTextElement(QLatin1String("column"), QString::number(eventData->location.column));
- }
- stream.writeTextElement(QLatin1String("details"), eventData->details);
- if (eventData->eventType == Binding)
- stream.writeTextElement(QLatin1String("bindingType"), QString::number((int)eventData->bindingType));
- stream.writeEndElement();
- }
- stream.writeEndElement(); // eventData
-
- stream.writeStartElement(QLatin1String("profilerDataModel"));
- foreach (const QmlRangeEventStartInstance &rangedEvent, d->startInstanceList) {
- stream.writeStartElement(QLatin1String("range"));
- stream.writeAttribute(QLatin1String("startTime"), QString::number(rangedEvent.startTime));
- stream.writeAttribute(QLatin1String("duration"), QString::number(rangedEvent.duration));
- stream.writeAttribute(QLatin1String("eventIndex"), QString::number(d->rangeEventDictionary.keys().indexOf(rangedEvent.statsInfo->eventHashStr)));
- if (rangedEvent.statsInfo->eventType == QmlDebug::Painting && rangedEvent.animationCount >= 0) {
- // animation frame
- stream.writeAttribute(QLatin1String("framerate"), QString::number(rangedEvent.frameRate));
- stream.writeAttribute(QLatin1String("animationcount"), QString::number(rangedEvent.animationCount));
- }
- stream.writeEndElement();
- }
- stream.writeEndElement(); // profilerDataModel
-
- d->v8DataModel->save(stream);
-
- stream.writeEndElement(); // trace
- stream.writeEndDocument();
-
- file.close();
- return true;
-}
-
-void QmlProfilerDataModel::setFilename(const QString &filename)
-{
- d->fileName = filename;
-}
-
-void QmlProfilerDataModel::load(const QString &filename)
-{
- setFilename(filename);
- load();
-}
-
-// "be strict in your output but tolerant in your inputs"
-void QmlProfilerDataModel::load()
-{
- QString filename = d->fileName;
-
- QFile file(filename);
-
- if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
- emit error(tr("Could not open %1 for reading.").arg(filename));
- return;
- }
-
- // erase current
- clear();
-
- setState(AcquiringData);
-
- bool readingQmlEvents = false;
- QHash<int, QmlRangeEventData *> descriptionBuffer;
- QmlRangeEventData *currentEvent = 0;
- bool startTimesAreSorted = true;
- bool validVersion = true;
-
- // time computation
- d->qmlMeasuredTime = 0;
-
- QXmlStreamReader stream(&file);
-
- while (validVersion && !stream.atEnd() && !stream.hasError()) {
- QXmlStreamReader::TokenType token = stream.readNext();
- QString elementName = stream.name().toString();
- switch (token) {
- case QXmlStreamReader::StartDocument : continue;
- case QXmlStreamReader::StartElement : {
- if (elementName == QLatin1String("trace")) {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute(QLatin1String("version")))
- validVersion = attributes.value(QLatin1String("version")).toString() == QLatin1String(Constants::PROFILER_FILE_VERSION);
- else
- validVersion = false;
- if (attributes.hasAttribute(QLatin1String("traceStart")))
- setTraceStartTime(attributes.value(QLatin1String("traceStart")).toString().toLongLong());
- if (attributes.hasAttribute(QLatin1String("traceEnd")))
- setTraceEndTime(attributes.value(QLatin1String("traceEnd")).toString().toLongLong());
- }
- if (elementName == QLatin1String("eventData")) {
- readingQmlEvents = true;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute(QLatin1String("totalTime")))
- d->qmlMeasuredTime = attributes.value(QLatin1String("totalTime")).toString().toDouble();
- break;
- }
- if (elementName == QLatin1String("v8profile") && !readingQmlEvents) {
- d->v8DataModel->load(stream);
- break;
- }
-
- if (elementName == QLatin1String("trace")) {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute(QLatin1String("traceStart")))
- setTraceStartTime(attributes.value(QLatin1String("traceStart")).toString().toLongLong());
- if (attributes.hasAttribute(QLatin1String("traceEnd")))
- setTraceEndTime(attributes.value(QLatin1String("traceEnd")).toString().toLongLong());
- }
-
- if (elementName == QLatin1String("range")) {
- QmlRangeEventStartInstance rangedEvent;
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute(QLatin1String("startTime")))
- rangedEvent.startTime = attributes.value(QLatin1String("startTime")).toString().toLongLong();
- if (attributes.hasAttribute(QLatin1String("duration")))
- rangedEvent.duration = attributes.value(QLatin1String("duration")).toString().toLongLong();
- if (attributes.hasAttribute(QLatin1String("framerate")))
- rangedEvent.frameRate = attributes.value(QLatin1String("framerate")).toString().toInt();
- if (attributes.hasAttribute(QLatin1String("animationcount")))
- rangedEvent.animationCount = attributes.value(QLatin1String("animationcount")).toString().toInt();
- else
- rangedEvent.animationCount = -1;
- if (attributes.hasAttribute(QLatin1String("eventIndex"))) {
- int ndx = attributes.value(QLatin1String("eventIndex")).toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlRangeEventData;
- rangedEvent.statsInfo = descriptionBuffer.value(ndx);
- }
- rangedEvent.endTimeIndex = d->endInstanceList.count();
-
- if (!d->startInstanceList.isEmpty()
- && rangedEvent.startTime < d->startInstanceList.last().startTime)
- startTimesAreSorted = false;
- d->startInstanceList << rangedEvent;
-
- QmlRangeEventEndInstance endTimeEvent;
- endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.duration;
- endTimeEvent.startTimeIndex = d->startInstanceList.count()-1;
- endTimeEvent.description = rangedEvent.statsInfo;
- d->endInstanceList << endTimeEvent;
- break;
- }
-
- if (readingQmlEvents) {
- if (elementName == QLatin1String("event")) {
- QXmlStreamAttributes attributes = stream.attributes();
- if (attributes.hasAttribute(QLatin1String("index"))) {
- int ndx = attributes.value(QLatin1String("index")).toString().toInt();
- if (!descriptionBuffer.value(ndx))
- descriptionBuffer[ndx] = new QmlRangeEventData;
- currentEvent = descriptionBuffer[ndx];
- // backwards compatibility: default bindingType
- currentEvent->bindingType = QmlBinding;
- } else {
- currentEvent = 0;
- }
- break;
- }
-
- // the remaining are eventdata or v8eventdata elements
- if (!currentEvent)
- break;
-
- stream.readNext();
- if (stream.tokenType() != QXmlStreamReader::Characters)
- break;
- QString readData = stream.text().toString();
-
- if (elementName == QLatin1String("displayname")) {
- currentEvent->displayName = readData;
- break;
- }
- if (elementName == QLatin1String("type")) {
- currentEvent->eventType = qmlEventTypeAsEnum(readData);
- break;
- }
- if (elementName == QLatin1String("filename")) {
- currentEvent->location.filename = readData;
- break;
- }
- if (elementName == QLatin1String("line")) {
- currentEvent->location.line = readData.toInt();
- break;
- }
- if (elementName == QLatin1String("column"))
- currentEvent->location.column = readData.toInt();
- if (elementName == QLatin1String("details")) {
- currentEvent->details = readData;
- break;
- }
- if (elementName == QLatin1String("bindingType")) {
- currentEvent->bindingType = readData.toInt();
- break;
- }
- }
- break;
- }
- case QXmlStreamReader::EndElement : {
- if (elementName == QLatin1String("event")) {
- currentEvent = 0;
- break;
- }
- if (elementName == QLatin1String("eventData")) {
- readingQmlEvents = false;
- break;
- }
- }
- default: break;
- }
- }
-
- file.close();
-
- if (stream.hasError()) {
- emit error(tr("Error while parsing %1.").arg(filename));
- clear();
- return;
- }
-
- stream.clear();
-
- if (!validVersion) {
- clear();
- emit error(tr("Invalid version of QML Trace file."));
- return;
- }
-
- // move the buffered data to the details cache
- foreach (QmlRangeEventData *desc, descriptionBuffer.values()) {
- desc->eventHashStr = getHashStringForQmlEvent(desc->location, desc->eventType);;
- d->rangeEventDictionary[desc->eventHashStr] = desc;
- }
-
- // sort startTimeSortedList
- if (!startTimesAreSorted) {
- qSort(d->startInstanceList.begin(), d->startInstanceList.end(), compareStartTimes);
- for (int i = 0; i< d->startInstanceList.count(); i++) {
- QmlRangeEventStartInstance startTimeData = d->startInstanceList[i];
- d->endInstanceList[startTimeData.endTimeIndex].startTimeIndex = i;
- }
- qSort(d->endInstanceList.begin(), d->endInstanceList.end(), compareStartIndexes);
- }
-
- emit countChanged();
-
- descriptionBuffer.clear();
-
- setState(ProcessingData);
- d->postProcess();
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-QmlProfilerDataModel::State QmlProfilerDataModel::currentState() const
-{
- return d->listState;
-}
-
-int QmlProfilerDataModel::getCurrentStateFromQml() const
-{
- return (int)d->listState;
-}
-
-void QmlProfilerDataModel::setState(QmlProfilerDataModel::State state)
-{
- // It's not an error, we are continuously calling "AcquiringData" for example
- if (d->listState == state)
- return;
-
- switch (state) {
- case Empty:
- // if it's not empty, complain but go on
- QTC_ASSERT(isEmpty(), /**/);
- break;
- case AcquiringData:
- // we're not supposed to receive new data while processing older data
- QTC_ASSERT(d->listState != ProcessingData, return);
- break;
- case ProcessingData:
- QTC_ASSERT(d->listState == AcquiringData, return);
- break;
- case Done:
- QTC_ASSERT(d->listState == ProcessingData || d->listState == Empty, return);
- break;
- default:
- emit error(tr("Trying to set unknown state in events list."));
- break;
- }
-
- d->listState = state;
- emit stateChanged();
-
- return;
-}
-
-} // namespace Internal
-} // namespace QmlProfiler
-
-QT_BEGIN_NAMESPACE
-Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventData, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventStartInstance, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventEndInstance, Q_MOVABLE_TYPE);
-Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QmlRangeEventRelative, Q_MOVABLE_TYPE);
-QT_END_NAMESPACE
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
deleted file mode 100644
index 0eb31b43f2..0000000000
--- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-****************************************************************************/
-
-#ifndef QMLPROFILERDATAMODEL_H
-#define QMLPROFILERDATAMODEL_H
-
-#include <qmldebug/qmlprofilereventtypes.h>
-#include <qmldebug/qmlprofilereventlocation.h>
-#include "qv8profilerdatamodel.h"
-
-#include <QHash>
-#include <QObject>
-
-namespace QmlProfiler {
-namespace Internal {
-
-// used for parents and children
-struct QmlRangeEventRelative;
-
-struct QmlRangeEventData
-{
- QmlRangeEventData();
- ~QmlRangeEventData();
-
- int eventId;
- int bindingType;
- QString displayName;
- QString eventHashStr;
- QString details;
- QmlDebug::QmlEventLocation location;
- QmlDebug::QmlEventType eventType;
-
- bool isBindingLoop;
-
- QHash <QString, QmlRangeEventRelative *> parentHash;
- QHash <QString, QmlRangeEventRelative *> childrenHash;
-
- qint64 duration;
- qint64 calls;
- qint64 minTime;
- qint64 maxTime;
- double timePerCall;
- double percentOfTime;
- qint64 medianTime;
-
- QmlRangeEventData &operator=(const QmlRangeEventData &ref);
-};
-
-struct QmlRangeEventRelative {
- QmlRangeEventRelative(QmlRangeEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
- QmlRangeEventRelative(QmlRangeEventRelative *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
- QmlRangeEventData *reference;
- qint64 duration;
- qint64 calls;
- bool inLoopPath;
-};
-
-class QmlProfilerDataModel : public QObject
-{
- Q_OBJECT
-public:
- enum State {
- Empty,
- AcquiringData,
- ProcessingData,
- Done
- };
-
- explicit QmlProfilerDataModel(QObject *parent = 0);
- ~QmlProfilerDataModel();
-
- QList<QmlRangeEventData *> getEventDescriptions() const;
- QmlRangeEventData *eventDescription(int eventId) const;
- QList<QV8EventData *> getV8Events() const;
- QV8EventData *v8EventDescription(int eventId) const;
-
- static QString getHashStringForQmlEvent(const QmlDebug::QmlEventLocation &location, int eventType);
- static QString getHashStringForV8Event(const QString &displayName, const QString &function);
- static QString rootEventName();
- static QString rootEventDescription();
- static QString qmlEventTypeAsString(QmlDebug::QmlEventType typeEnum);
- static QmlDebug::QmlEventType qmlEventTypeAsEnum(const QString &typeString);
-
- int findFirstIndex(qint64 startTime) const;
- int findFirstIndexNoParents(qint64 startTime) const;
- int findLastIndex(qint64 endTime) const;
- Q_INVOKABLE qint64 firstTimeMark() const;
- Q_INVOKABLE qint64 lastTimeMark() const;
-
- // data access
- Q_INVOKABLE int count() const;
- Q_INVOKABLE bool isEmpty() const;
- Q_INVOKABLE qint64 getStartTime(int index) const;
- Q_INVOKABLE qint64 getEndTime(int index) const;
- Q_INVOKABLE qint64 getDuration(int index) const;
- Q_INVOKABLE int getType(int index) const;
- Q_INVOKABLE int getNestingLevel(int index) const;
- Q_INVOKABLE int getNestingDepth(int index) const;
- Q_INVOKABLE QString getFilename(int index) const;
- Q_INVOKABLE int getLine(int index) const;
- Q_INVOKABLE int getColumn(int index) const;
- Q_INVOKABLE QString getDetails(int index) const;
- Q_INVOKABLE int getEventId(int index) const;
- Q_INVOKABLE int getBindingLoopDest(int index) const;
- Q_INVOKABLE int getFramerate(int index) const;
- Q_INVOKABLE int getAnimationCount(int index) const;
- Q_INVOKABLE int getMaximumAnimationCount() const;
- Q_INVOKABLE int getMinimumAnimationCount() const;
-
-
- // per-type data
- Q_INVOKABLE int uniqueEventsOfType(int type) const;
- Q_INVOKABLE int maxNestingForType(int type) const;
- Q_INVOKABLE QString eventTextForType(int type, int index) const;
- Q_INVOKABLE QString eventDisplayNameForType(int type, int index) const;
- Q_INVOKABLE int eventIdForType(int type, int index) const;
- Q_INVOKABLE int eventPosInType(int index) const;
-
- Q_INVOKABLE qint64 traceStartTime() const;
- Q_INVOKABLE qint64 traceEndTime() const;
- Q_INVOKABLE qint64 traceDuration() const;
- Q_INVOKABLE qint64 qmlMeasuredTime() const;
- Q_INVOKABLE qint64 v8MeasuredTime() const;
-
- void compileStatistics(qint64 startTime, qint64 endTime);
- State currentState() const;
- Q_INVOKABLE int getCurrentStateFromQml() const;
-
-signals:
- void stateChanged();
- void countChanged();
- void error(const QString &error);
-
- void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
- void detailsChanged(int eventId, const QString &newString);
- void reloadDetailLabels();
- void reloadDocumentsForDetails();
-
-public slots:
- void clear();
-
- void prepareForWriting();
- void addRangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
- const QStringList &data, const QmlDebug::QmlEventLocation &location);
- void addV8Event(int depth,const QString &function,const QString &filename, int lineNumber, double totalTime, double selfTime);
- void addFrameEvent(qint64 time, int framerate, int animationcount);
- void setTraceStartTime(qint64 time);
- void setTraceEndTime(qint64 time);
-
- void complete();
-
- bool save(const QString &filename);
- void load(const QString &filename);
- void setFilename(const QString &filename);
- void load();
-
- void rewriteDetailsString(int eventType, const QmlDebug::QmlEventLocation &location, const QString &newString);
- void finishedRewritingDetails();
-
-private:
- void setState(State state);
- void reloadDetails();
-
-private:
- class QmlProfilerDataModelPrivate;
- QmlProfilerDataModelPrivate *d;
-
- friend class QV8ProfilerDataModel;
-};
-
-
-} // namespace Internal
-} // namespace QmlProfiler
-
-#endif // QMLPROFILERDATAMODEL_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
index 2385e42c1c..0b078f9c86 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
@@ -41,7 +41,7 @@ namespace Internal {
struct PendingEvent {
QmlDebug::QmlEventLocation location;
QString localFile;
- int eventType;
+ int requestId;
};
class PropertyVisitor: protected QmlJS::AST::Visitor
@@ -122,7 +122,7 @@ QmlProfilerDetailsRewriter::~QmlProfilerDetailsRewriter()
delete d;
}
-void QmlProfilerDetailsRewriter::requestDetailsForLocation(int type,
+void QmlProfilerDetailsRewriter::requestDetailsForLocation(int requestId,
const QmlDebug::QmlEventLocation &location)
{
const QString localFile = d->m_projectFinder->findFile(location.filename);
@@ -132,7 +132,7 @@ void QmlProfilerDetailsRewriter::requestDetailsForLocation(int type,
if (!QmlJS::Document::isQmlLikeLanguage(QmlJSTools::languageOfFile(localFile)))
return;
- PendingEvent ev = {location, localFile, type};
+ PendingEvent ev = {location, localFile, requestId};
d->m_pendingEvents << ev;
if (!d->m_pendingDocs.contains(localFile)) {
if (d->m_pendingDocs.isEmpty())
@@ -154,7 +154,7 @@ void QmlProfilerDetailsRewriter::reloadDocuments()
}
void QmlProfilerDetailsRewriter::rewriteDetailsForLocation(QTextStream &textDoc,
- QmlJS::Document::Ptr doc, int type, const QmlDebug::QmlEventLocation &location)
+ QmlJS::Document::Ptr doc, int requestId, const QmlDebug::QmlEventLocation &location)
{
PropertyVisitor propertyVisitor;
QmlJS::AST::Node *node = propertyVisitor(doc->ast(), location.line, location.column);
@@ -168,7 +168,12 @@ void QmlProfilerDetailsRewriter::rewriteDetailsForLocation(QTextStream &textDoc,
textDoc.seek(startPos);
QString details = textDoc.read(len).replace(QLatin1Char('\n'), QLatin1Char(' ')).simplified();
- emit rewriteDetailsString(type, location, details);
+ emit rewriteDetailsString(requestId, details);
+}
+
+void QmlProfilerDetailsRewriter::clearRequests()
+{
+ d->m_pendingDocs.clear();
}
void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc)
@@ -186,7 +191,7 @@ void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc)
PendingEvent ev = d->m_pendingEvents[i];
if (ev.localFile == doc->fileName()) {
d->m_pendingEvents.removeAt(i);
- rewriteDetailsForLocation(st, doc, ev.eventType, ev.location);
+ rewriteDetailsForLocation(st, doc, ev.requestId, ev.location);
}
}
}
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
index 2ec65606d2..21e94a1bf3 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
@@ -46,16 +46,18 @@ public:
explicit QmlProfilerDetailsRewriter(QObject *parent, Utils::FileInProjectFinder *fileFinder);
~QmlProfilerDetailsRewriter();
+ void clearRequests();
+
private:
- void rewriteDetailsForLocation(QTextStream &textDoc,QmlJS::Document::Ptr doc, int type,
+ void rewriteDetailsForLocation(QTextStream &textDoc, QmlJS::Document::Ptr doc, int requestId,
const QmlDebug::QmlEventLocation &location);
+
public slots:
- void requestDetailsForLocation(int type, const QmlDebug::QmlEventLocation &location);
+ void requestDetailsForLocation(int requestId, const QmlDebug::QmlEventLocation &location);
void reloadDocuments();
void documentReady(QmlJS::Document::Ptr doc);
signals:
- void rewriteDetailsString(int type, const QmlDebug::QmlEventLocation &location,
- const QString &details);
+ void rewriteDetailsString(int requestId, const QString &details);
void eventDetailsChanged();
private:
class QmlProfilerDetailsRewriterPrivate;
diff --git a/src/plugins/qmlprofiler/qmlprofilerengine.cpp b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
index 392588d5a9..0dff45f4d5 100644
--- a/src/plugins/qmlprofiler/qmlprofilerengine.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerengine.cpp
@@ -82,7 +82,7 @@ QmlProfilerRunControl::QmlProfilerRunControl(const Analyzer::AnalyzerStartParame
{
d->m_profilerState = 0;
- // Only wait 4 seconds for the 'Waiting for connection' on application ouput, then just try to connect
+ // Only wait 4 seconds for the 'Waiting for connection' on application output, then just try to connect
// (application output might be redirected / blocked)
d->m_noDebugOutputTimer.setSingleShot(true);
d->m_noDebugOutputTimer.setInterval(4000);
diff --git a/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp b/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp
new file mode 100644
index 0000000000..d1be7cfdb3
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.cpp
@@ -0,0 +1,479 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilereventsmodelproxy.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+
+#include <utils/qtcassert.h>
+
+#include <QVector>
+#include <QHash>
+#include <QUrl>
+#include <QString>
+#include <QStack>
+#include <QElapsedTimer>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerEventsModelProxy::QmlProfilerEventsModelProxyPrivate
+{
+public:
+ QmlProfilerEventsModelProxyPrivate(QmlProfilerEventsModelProxy *qq) : q(qq) {}
+ ~QmlProfilerEventsModelProxyPrivate() {}
+
+ QHash<QString, QmlProfilerEventsModelProxy::QmlEventStats> data;
+
+ QmlProfilerModelManager *modelManager;
+ QmlProfilerEventsModelProxy *q;
+
+ int modelId;
+
+ QVector<int> acceptedTypes;
+ QSet<QString> eventsInBindingLoop;
+};
+
+QmlProfilerEventsModelProxy::QmlProfilerEventsModelProxy(QmlProfilerModelManager *modelManager, QObject *parent)
+ : QObject(parent), d(new QmlProfilerEventsModelProxyPrivate(this))
+{
+ d->modelManager = modelManager;
+ connect(modelManager->simpleModel(), SIGNAL(changed()), this, SLOT(dataChanged()));
+ d->modelId = modelManager->registerModelProxy();
+
+ d->acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+QmlProfilerEventsModelProxy::~QmlProfilerEventsModelProxy()
+{
+ delete d;
+}
+
+const QList<QmlProfilerEventsModelProxy::QmlEventStats> QmlProfilerEventsModelProxy::getData() const
+{
+ return d->data.values();
+}
+
+void QmlProfilerEventsModelProxy::clear()
+{
+ d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1);
+ d->data.clear();
+ d->eventsInBindingLoop.clear();
+}
+
+void QmlProfilerEventsModelProxy::limitToRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ loadData(rangeStart, rangeEnd);
+}
+
+void QmlProfilerEventsModelProxy::dataChanged()
+{
+ if (d->modelManager->state() == QmlProfilerDataState::ProcessingData)
+ loadData();
+
+ if (d->modelManager->state() == QmlProfilerDataState::Empty)
+ clear();
+}
+
+QSet<QString> QmlProfilerEventsModelProxy::eventsInBindingLoop() const
+{
+ return d->eventsInBindingLoop;
+}
+
+void QmlProfilerEventsModelProxy::loadData(qint64 rangeStart, qint64 rangeEnd)
+{
+ clear();
+
+ qint64 qmlTime = 0;
+ qint64 lastEndTime = 0;
+ QHash <QString, QVector<qint64> > durations;
+
+ const bool checkRanges = (rangeStart != -1) && (rangeEnd != -1);
+
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList
+ = d->modelManager->simpleModel()->getEvents();
+
+ // used by binding loop detection
+ typedef QPair<QString, const QmlProfilerSimpleModel::QmlEventData*> CallStackEntry;
+ QStack<CallStackEntry> callStack;
+ callStack.push(CallStackEntry(QString(), 0)); // artificial root
+
+ for (int i = 0; i < eventList.size(); ++i) {
+ const QmlProfilerSimpleModel::QmlEventData *event = &eventList[i];
+
+ if (!d->acceptedTypes.contains(event->eventType))
+ continue;
+
+ if (checkRanges) {
+ if ((event->startTime + event->duration < rangeStart)
+ || (event->startTime > rangeEnd))
+ continue;
+ }
+
+ // put event in hash
+ QString hash = QmlProfilerSimpleModel::getHashString(*event);
+ if (!d->data.contains(hash)) {
+ QmlEventStats stats = {
+ event->displayName,
+ hash,
+ event->data.join(QLatin1String(" ")),
+ event->location,
+ event->eventType,
+ event->bindingType,
+ event->duration,
+ 1, //calls
+ event->duration, //minTime
+ event->duration, // maxTime
+ 0, //timePerCall
+ 0, //percentOfTime
+ 0, //medianTime
+ false //isBindingLoop
+ };
+
+ d->data.insert(hash, stats);
+
+ // for median computing
+ durations.insert(hash, QVector<qint64>());
+ durations[hash].append(event->duration);
+ } else {
+ // update stats
+ QmlEventStats *stats = &d->data[hash];
+
+ stats->duration += event->duration;
+ if (event->duration < stats->minTime)
+ stats->minTime = event->duration;
+ if (event->duration > stats->maxTime)
+ stats->maxTime = event->duration;
+ stats->calls++;
+
+ // for median computing
+ durations[hash].append(event->duration);
+ }
+
+ // qml time computation
+ if (event->startTime > lastEndTime) { // assume parent event if starts before last end
+ qmlTime += event->duration;
+ lastEndTime = event->startTime + event->duration;
+ }
+
+
+ //
+ // binding loop detection
+ //
+ const QmlProfilerSimpleModel::QmlEventData *potentialParent = callStack.top().second;
+ while (potentialParent
+ && !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
+ callStack.pop();
+ potentialParent = callStack.top().second;
+ }
+
+ // check whether event is already in stack
+ bool inLoop = false;
+ for (int ii = 1; ii < callStack.size(); ++ii) {
+ if (callStack.at(ii).first == hash)
+ inLoop = true;
+ if (inLoop)
+ d->eventsInBindingLoop.insert(hash);
+ }
+
+
+ CallStackEntry newEntry(hash, event);
+ callStack.push(newEntry);
+
+ d->modelManager->modelProxyCountUpdated(d->modelId, i, eventList.count()*2);
+ }
+
+ // post-process: calc mean time, median time, percentoftime
+ foreach (const QString &hash, d->data.keys()) {
+ QmlEventStats* stats = &d->data[hash];
+ if (stats->calls > 0)
+ stats->timePerCall = stats->duration / (double)stats->calls;
+
+ QVector<qint64> eventDurations = durations.value(hash);
+ if (!eventDurations.isEmpty()) {
+ qSort(eventDurations);
+ stats->medianTime = eventDurations.at(eventDurations.count()/2);
+ }
+
+ stats->percentOfTime = stats->duration * 100.0 / qmlTime;
+ }
+
+ // set binding loop flag
+ foreach (const QString &eventHash, d->eventsInBindingLoop)
+ d->data[eventHash].isBindingLoop = true;
+
+ QString rootEventName = tr("<program>");
+ QmlDebug::QmlEventLocation rootEventLocation(rootEventName, 1, 1);
+
+ // insert root event
+ QmlEventStats rootEvent = {
+ rootEventName, //event.displayName,
+ rootEventName, // hash
+ tr("Main Program"), //event.details,
+ rootEventLocation, // location
+ (int)QmlDebug::Binding, // event type
+ 0, // binding type
+ qmlTime + 1,
+ 1, //calls
+ qmlTime + 1, //minTime
+ qmlTime + 1, // maxTime
+ qmlTime + 1, //timePerCall
+ 100.0, //percentOfTime
+ qmlTime + 1, //medianTime;
+ false
+ };
+
+ d->data.insert(rootEventName, rootEvent);
+
+ d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1);
+ emit dataAvailable();
+}
+
+int QmlProfilerEventsModelProxy::count() const
+{
+ return d->data.count();
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+QmlProfilerEventRelativesModelProxy::QmlProfilerEventRelativesModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent)
+ : QObject(parent)
+{
+ QTC_CHECK(modelManager);
+ m_modelManager = modelManager;
+ connect(modelManager->simpleModel(), SIGNAL(changed()), this, SLOT(dataChanged()));
+
+ QTC_CHECK(eventsModel);
+ m_eventsModel = eventsModel;
+
+ m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+QmlProfilerEventRelativesModelProxy::~QmlProfilerEventRelativesModelProxy()
+{
+}
+
+const QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap QmlProfilerEventRelativesModelProxy::getData(const QString &hash) const
+{
+ if (m_data.contains(hash))
+ return m_data[hash];
+ return QmlEventRelativesMap();
+}
+
+int QmlProfilerEventRelativesModelProxy::count() const
+{
+ return m_data.count();
+}
+
+void QmlProfilerEventRelativesModelProxy::clear()
+{
+ m_data.clear();
+}
+
+void QmlProfilerEventRelativesModelProxy::dataChanged()
+{
+ loadData();
+
+ emit dataAvailable();
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////
+QmlProfilerEventParentsModelProxy::QmlProfilerEventParentsModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent)
+ : QmlProfilerEventRelativesModelProxy(modelManager, eventsModel, parent)
+{}
+
+QmlProfilerEventParentsModelProxy::~QmlProfilerEventParentsModelProxy()
+{}
+
+void QmlProfilerEventParentsModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ QHash<QString, QmlProfilerSimpleModel::QmlEventData> cachedEvents;
+ QString rootEventName = tr("<program>");
+ QmlProfilerSimpleModel::QmlEventData rootEvent = {
+ rootEventName,
+ QmlDebug::Binding,
+ 0,
+ 0,
+ 0,
+ QStringList() << tr("Main Program"),
+ QmlDebug::QmlEventLocation(rootEventName, 0, 0),
+ 0,0,0,0,0 // numericData fields
+ };
+ cachedEvents.insert(rootEventName, rootEvent);
+
+ // for level computation
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ const QSet<QString> eventsInBindingLoop = m_eventsModel->eventsInBindingLoop();
+
+ // compute parent-child relationship and call count
+ QHash<int, QString> lastParent;
+ //for (int index = fromIndex; index <= toIndex; index++) {
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+
+
+ QString parentHash = rootEventName;
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+
+ // save in cache
+ if (!cachedEvents.contains(eventHash))
+ cachedEvents.insert(eventHash, event);
+
+ if (level > QmlDebug::Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
+ parentHash = lastParent[level-1];
+
+ QmlProfilerSimpleModel::QmlEventData *parentEvent = &(cachedEvents[parentHash]);
+
+ // generate placeholder if needed
+ if (!m_data.contains(eventHash))
+ m_data.insert(eventHash, QmlEventRelativesMap());
+
+ if (m_data[eventHash].contains(parentHash)) {
+ QmlEventRelativesData *parent = &(m_data[eventHash][parentHash]);
+ parent->calls++;
+ parent->duration += event.duration;
+ } else {
+ m_data[eventHash].insert(parentHash, QmlEventRelativesData());
+ QmlEventRelativesData *parent = &(m_data[eventHash][parentHash]);
+ parent->displayName = parentEvent->displayName;
+ parent->eventType = parentEvent->eventType;
+ parent->duration = event.duration;
+ parent->calls = 1;
+ parent->details = parentEvent->data.join(QLatin1String(""));
+ parent->isBindingLoop = eventsInBindingLoop.contains(parentHash);
+ }
+
+ // now lastparent is a string with the hash
+ lastParent[level] = eventHash;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+QmlProfilerEventChildrenModelProxy::QmlProfilerEventChildrenModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent)
+ : QmlProfilerEventRelativesModelProxy(modelManager, eventsModel, parent)
+{}
+
+QmlProfilerEventChildrenModelProxy::~QmlProfilerEventChildrenModelProxy()
+{}
+
+void QmlProfilerEventChildrenModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ QString rootEventName = tr("<program>");
+
+ // for level computation
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ const QSet<QString> eventsInBindingLoop = m_eventsModel->eventsInBindingLoop();
+
+ // compute parent-child relationship and call count
+ QHash<int, QString> lastParent;
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+
+ QString parentHash = rootEventName;
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+
+ if (level > QmlDebug::Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
+ parentHash = lastParent[level-1];
+
+ // generate placeholder if needed
+ if (!m_data.contains(parentHash))
+ m_data.insert(parentHash, QmlEventRelativesMap());
+
+ if (m_data[parentHash].contains(eventHash)) {
+ QmlEventRelativesData *child = &(m_data[parentHash][eventHash]);
+ child->calls++;
+ child->duration += event.duration;
+ } else {
+ m_data[parentHash].insert(eventHash, QmlEventRelativesData());
+ QmlEventRelativesData *child = &(m_data[parentHash][eventHash]);
+ child->displayName = event.displayName;
+ child->eventType = event.eventType;
+ child->duration = event.duration;
+ child->calls = 1;
+ child->details = event.data.join(QLatin1String(""));
+ child->isBindingLoop = eventsInBindingLoop.contains(parentHash);
+ }
+
+ // now lastparent is a string with the hash
+ lastParent[level] = eventHash;
+ }
+}
+
+
+
+}
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h b/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h
new file mode 100644
index 0000000000..b10dd7e49a
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilereventsmodelproxy.h
@@ -0,0 +1,172 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef QMLPROFILEREVENTSMODELPROXY_H
+#define QMLPROFILEREVENTSMODELPROXY_H
+
+#include "qmlprofilersimplemodel.h"
+#include <QObject>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <qmldebug/qmlprofilereventlocation.h>
+#include <QHash>
+#include <QVector>
+
+
+namespace QmlProfiler {
+class QmlProfilerModelManager;
+
+namespace Internal {
+
+class QmlProfilerEventsModelProxy : public QObject
+{
+ Q_OBJECT
+public:
+ struct QmlEventStats {
+ QString displayName;
+ QString eventHashStr;
+ QString details;
+ QmlDebug::QmlEventLocation location;
+ int eventType;
+ int bindingType;
+
+ qint64 duration;
+ qint64 calls;
+ qint64 minTime;
+ qint64 maxTime;
+ qint64 timePerCall;
+ double percentOfTime;
+ qint64 medianTime;
+
+ bool isBindingLoop;
+ };
+
+ QmlProfilerEventsModelProxy(QmlProfilerModelManager *modelManager, QObject *parent = 0);
+ ~QmlProfilerEventsModelProxy();
+
+ const QList<QmlEventStats> getData() const;
+ int count() const;
+ void clear();
+
+ void limitToRange(qint64 rangeStart, qint64 rangeEnd);
+
+signals:
+ void dataAvailable();
+
+private:
+ void loadData(qint64 rangeStart = -1, qint64 rangeEnd = -1);
+
+ QSet<QString> eventsInBindingLoop() const;
+
+private slots:
+ void dataChanged();
+
+private:
+ class QmlProfilerEventsModelProxyPrivate;
+ QmlProfilerEventsModelProxyPrivate *d;
+
+ friend class QmlProfilerEventParentsModelProxy;
+ friend class QmlProfilerEventChildrenModelProxy;
+};
+
+class QmlProfilerEventRelativesModelProxy : public QObject
+{
+ Q_OBJECT
+public:
+ struct QmlEventRelativesData {
+ QString displayName;
+ int eventType;
+ qint64 duration;
+ qint64 calls;
+ QString details;
+ bool isBindingLoop;
+ };
+ typedef QHash <QString, QmlEventRelativesData> QmlEventRelativesMap;
+
+ QmlProfilerEventRelativesModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent = 0);
+ ~QmlProfilerEventRelativesModelProxy();
+
+
+ int count() const;
+ void clear();
+
+ const QmlEventRelativesMap getData(const QString &hash) const;
+
+protected:
+ virtual void loadData() = 0;
+
+signals:
+ void dataAvailable();
+
+protected slots:
+ void dataChanged();
+
+protected:
+ QHash <QString, QmlEventRelativesMap> m_data;
+ QmlProfilerModelManager *m_modelManager;
+ QmlProfilerEventsModelProxy *m_eventsModel;
+ QVector <int> m_acceptedTypes;
+};
+
+class QmlProfilerEventParentsModelProxy : public QmlProfilerEventRelativesModelProxy
+{
+ Q_OBJECT
+public:
+ QmlProfilerEventParentsModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent = 0);
+ ~QmlProfilerEventParentsModelProxy();
+
+protected:
+ virtual void loadData();
+signals:
+ void dataAvailable();
+};
+
+class QmlProfilerEventChildrenModelProxy : public QmlProfilerEventRelativesModelProxy
+{
+ Q_OBJECT
+public:
+ QmlProfilerEventChildrenModelProxy(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventsModelProxy *eventsModel,
+ QObject *parent = 0);
+ ~QmlProfilerEventChildrenModelProxy();
+
+protected:
+ virtual void loadData();
+signals:
+ void dataAvailable();
+};
+
+}
+}
+
+#endif
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index 5f4f16b24d..01d8504510 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -68,6 +68,9 @@ Q_GLOBAL_STATIC(Colors, colors)
////////////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////////////
+
class EventsViewItem : public QStandardItem
{
public:
@@ -104,46 +107,48 @@ public:
QmlProfilerViewManager *m_viewContainer;
QmlProfilerEventsMainView *m_eventTree;
- QmlProfilerEventsParentsAndChildrenView *m_eventChildren;
- QmlProfilerEventsParentsAndChildrenView *m_eventParents;
- QmlProfilerDataModel *m_profilerDataModel;
+ QmlProfilerEventRelativesView *m_eventChildren;
+ QmlProfilerEventRelativesView *m_eventParents;
- bool m_globalStatsEnabled;
+ QmlProfilerEventsModelProxy *modelProxy;
+ bool globalStats;
};
QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
QmlProfilerTool *profilerTool,
QmlProfilerViewManager *container,
- QmlProfilerDataModel *profilerDataModel )
+ QmlProfilerModelManager *profilerModelManager )
+
: QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
{
setObjectName(QLatin1String("QmlProfilerEventsView"));
- d->m_profilerDataModel = profilerDataModel;
- connect(d->m_profilerDataModel, SIGNAL(stateChanged()),
+ d->modelProxy = new QmlProfilerEventsModelProxy(profilerModelManager, this);
+ connect(profilerModelManager, SIGNAL(stateChanged()),
this, SLOT(profilerDataModelStateChanged()));
- d->m_eventTree = new QmlProfilerEventsMainView(QmlProfilerEventsMainView::EventsView, this, d->m_profilerDataModel);
+ d->m_eventTree = new QmlProfilerEventsMainView(this, d->modelProxy);
connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
- connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
-
- d->m_eventChildren = new QmlProfilerEventsParentsAndChildrenView(
- QmlProfilerEventsParentsAndChildrenView::ChildrenView,
- this,
- d->m_profilerDataModel);
- d->m_eventParents = new QmlProfilerEventsParentsAndChildrenView(
- QmlProfilerEventsParentsAndChildrenView::ParentsView,
- this,
- d->m_profilerDataModel);
- connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
- connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
- connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
- connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), this, SIGNAL(eventSelectedByHash(QString)));
+
+ d->m_eventChildren = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventChildrenModelProxy(profilerModelManager, d->modelProxy, this),
+ this);
+ d->m_eventParents = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventParentsModelProxy(profilerModelManager, d->modelProxy, this),
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventChildren, SLOT(displayEvent(QString)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventParents, SLOT(displayEvent(QString)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
// widget arrangement
QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->setContentsMargins(0,0,0,0);
groupLayout->setSpacing(0);
+
Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
splitterVertical->addWidget(d->m_eventTree);
Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
@@ -159,30 +164,17 @@ QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
d->m_profilerTool = profilerTool;
d->m_viewContainer = container;
- d->m_globalStatsEnabled = true;
+ d->globalStats = true;
}
QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
{
+ delete d->modelProxy;
delete d;
}
void QmlProfilerEventsWidget::profilerDataModelStateChanged()
{
- if (d->m_profilerDataModel) {
- QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
- if (newState == QmlProfilerDataModel::Empty)
- clear();
- }
-}
-
-void QmlProfilerEventsWidget::switchToV8View()
-{
- setObjectName(QLatin1String("QmlProfilerV8ProfileView"));
- d->m_eventTree->setViewType(QmlProfilerEventsMainView::V8ProfileView);
- d->m_eventParents->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ParentsView);
- d->m_eventChildren->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ChildrenView);
- setToolTip(tr("Trace information from the v8 JavaScript engine. Available only in Qt5 based applications."));
}
void QmlProfilerEventsWidget::clear()
@@ -194,9 +186,8 @@ void QmlProfilerEventsWidget::clear()
void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{
- clear();
- d->m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
- d->m_globalStatsEnabled = d->m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
+ d->modelProxy->limitToRange(rangeStart, rangeEnd);
+ d->globalStats = (rangeStart == -1) && (rangeEnd == -1);
}
QModelIndex QmlProfilerEventsWidget::selectedItem() const
@@ -230,23 +221,18 @@ void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
copyRowAction = menu.addAction(tr("Copy Row"));
copyTableAction = menu.addAction(tr("Copy Table"));
- if (isQml()) {
- // only for qml events view, not for v8
- showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
- showExtendedStatsAction->setCheckable(true);
- showExtendedStatsAction->setChecked(showExtendedStatistics());
- }
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
}
- if (isQml()) {
- menu.addSeparator();
- getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
- if (!d->m_viewContainer->hasValidSelection())
- getLocalStatsAction->setEnabled(false);
- getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
- if (hasGlobalStats())
- getGlobalStatsAction->setEnabled(false);
- }
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
QAction *selectedAction = menu.exec(position);
@@ -259,12 +245,8 @@ void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
getStatisticsInRange(d->m_viewContainer->selectionStart(),
d->m_viewContainer->selectionEnd());
}
- if (selectedAction == getGlobalStatsAction) {
- if (d->m_profilerDataModel) {
- getStatisticsInRange(d->m_profilerDataModel->traceStartTime(),
- d->m_profilerDataModel->traceEndTime());
- }
- }
+ if (selectedAction == getGlobalStatsAction)
+ getStatisticsInRange(-1, -1);
if (selectedAction == showExtendedStatsAction)
setShowExtendedStatistics(!showExtendedStatistics());
}
@@ -293,24 +275,20 @@ void QmlProfilerEventsWidget::copyRowToClipboard() const
d->m_eventTree->copyRowToClipboard();
}
-void QmlProfilerEventsWidget::updateSelectedEvent(int eventId) const
+void QmlProfilerEventsWidget::updateSelectedEvent(const QString &eventHash) const
{
- if (d->m_eventTree->selectedEventId() != eventId)
- d->m_eventTree->selectEvent(eventId);
+ if (d->m_eventTree->selectedEventHash() != eventHash)
+ d->m_eventTree->selectEvent(eventHash);
}
void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
{
- // This slot is used to connect the javascript pane with the qml events pane
- // Our javascript trace data does not store column information
- // thus we ignore it here
- Q_UNUSED(column);
- d->m_eventTree->selectEventByLocation(filename, line);
+ d->m_eventTree->selectEventByLocation(filename, line, column);
}
bool QmlProfilerEventsWidget::hasGlobalStats() const
{
- return d->m_globalStatsEnabled;
+ return d->globalStats;
}
void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
@@ -323,15 +301,6 @@ bool QmlProfilerEventsWidget::showExtendedStatistics() const
return d->m_eventTree->showExtendedStatistics();
}
-bool QmlProfilerEventsWidget::isQml() const
-{
- return d->m_eventTree->viewType() == QmlProfilerEventsMainView::EventsView;
-}
-bool QmlProfilerEventsWidget::isV8() const
-{
- return d->m_eventTree->viewType() == QmlProfilerEventsMainView::V8ProfileView;
-}
-
////////////////////////////////////////////////////////////////////////////////////
class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
@@ -339,17 +308,14 @@ class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
public:
QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
- void buildModelFromList(const QList<QmlRangeEventData *> &list, QStandardItem *parentItem );
- void buildV8ModelFromList( const QList<QV8EventData *> &list );
int getFieldCount();
- QString textForItem(QStandardItem *item, bool recursive) const;
+ QString textForItem(QStandardItem *item, bool recursive = false) const;
QmlProfilerEventsMainView *q;
- QmlProfilerEventsMainView::ViewTypes m_viewType;
- QmlProfilerDataModel *m_profilerDataModel;
+ QmlProfilerEventsModelProxy *modelProxy;
QStandardItemModel *m_model;
QList<bool> m_fieldShown;
QHash<int, int> m_columnIndex; // maps field enum to column index
@@ -361,49 +327,52 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsMainView::QmlProfilerEventsMainView(ViewTypes viewType,
- QWidget *parent,
- QmlProfilerDataModel *dataModel)
- : QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy)
+: QmlProfilerTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
{
setObjectName(QLatin1String("QmlProfilerEventsTable"));
- header()->setResizeMode(QHeaderView::Interactive);
- header()->setDefaultSectionSize(100);
- header()->setMinimumSectionSize(50);
+
setSortingEnabled(false);
- setFrameStyle(QFrame::NoFrame);
d->m_model = new QStandardItemModel(this);
setModel(d->m_model);
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
- d->m_profilerDataModel = dataModel;
- connect(d->m_profilerDataModel,SIGNAL(stateChanged()),
- this,SLOT(profilerDataModelStateChanged()));
- connect(d->m_profilerDataModel,SIGNAL(detailsChanged(int,QString)),
- this,SLOT(changeDetailsForEvent(int,QString)));
-
+ d->modelProxy = modelProxy;
+ connect(d->modelProxy,SIGNAL(dataAvailable()), this, SLOT(buildModel()));
+// connect(d->modelProxy,SIGNAL(stateChanged()),
+// this,SLOT(profilerDataModelStateChanged()));
d->m_firstNumericColumn = 0;
d->m_preventSelectBounce = false;
d->m_showExtendedStatistics = false;
- setViewType(viewType);
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, true);
+ setFieldViewable(TimeInPercent, true);
+ setFieldViewable(TotalTime, true);
+ setFieldViewable(SelfTimeInPercent, false);
+ setFieldViewable(SelfTime, false);
+ setFieldViewable(CallCount, true);
+ setFieldViewable(TimePerCall, true);
+ setFieldViewable(MaxTime, true);
+ setFieldViewable(MinTime, true);
+ setFieldViewable(MedianTime, true);
+ setFieldViewable(Details, true);
+
+ buildModel();
}
QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
{
clear();
+ //delete d->modelProxy;
delete d->m_model;
delete d;
}
void QmlProfilerEventsMainView::profilerDataModelStateChanged()
{
- if (d->m_profilerDataModel) {
- QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
- if (newState == QmlProfilerDataModel::Done)
- buildModel();
- }
}
void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
@@ -418,52 +387,6 @@ void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
}
}
-QmlProfilerEventsMainView::ViewTypes QmlProfilerEventsMainView::viewType() const
-{
- return d->m_viewType;
-}
-
-void QmlProfilerEventsMainView::setViewType(ViewTypes type)
-{
- d->m_viewType = type;
- switch (type) {
- case EventsView: {
- setObjectName(QLatin1String("QmlProfilerEventsTable"));
- setFieldViewable(Name, true);
- setFieldViewable(Type, true);
- setFieldViewable(Percent, true);
- setFieldViewable(TotalDuration, true);
- setFieldViewable(SelfPercent, false);
- setFieldViewable(SelfDuration, false);
- setFieldViewable(CallCount, true);
- setFieldViewable(TimePerCall, true);
- setFieldViewable(MaxTime, true);
- setFieldViewable(MinTime, true);
- setFieldViewable(MedianTime, true);
- setFieldViewable(Details, true);
- break;
- }
- case V8ProfileView: {
- setObjectName(QLatin1String("QmlProfilerV8ProfileTable"));
- setFieldViewable(Name, true);
- setFieldViewable(Type, false);
- setFieldViewable(Percent, true);
- setFieldViewable(TotalDuration, true);
- setFieldViewable(SelfPercent, true);
- setFieldViewable(SelfDuration, true);
- setFieldViewable(CallCount, false);
- setFieldViewable(TimePerCall, false);
- setFieldViewable(MaxTime, false);
- setFieldViewable(MinTime, false);
- setFieldViewable(MedianTime, false);
- setFieldViewable(Details, true);
- break;
- }
- default: break;
- }
-
- buildModel();
-}
void QmlProfilerEventsMainView::setHeaderLabels()
{
@@ -473,53 +396,53 @@ void QmlProfilerEventsMainView::setHeaderLabels()
d->m_columnIndex.clear();
if (d->m_fieldShown[Name]) {
d->m_columnIndex[Name] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Location")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Location)));
d->m_firstNumericColumn++;
}
if (d->m_fieldShown[Type]) {
d->m_columnIndex[Type] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
d->m_firstNumericColumn++;
}
- if (d->m_fieldShown[Percent]) {
- d->m_columnIndex[Percent] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
+ if (d->m_fieldShown[TimeInPercent]) {
+ d->m_columnIndex[TimeInPercent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimeInPercent)));
}
- if (d->m_fieldShown[TotalDuration]) {
- d->m_columnIndex[TotalDuration] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ if (d->m_fieldShown[TotalTime]) {
+ d->m_columnIndex[TotalTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
}
- if (d->m_fieldShown[SelfPercent]) {
+ if (d->m_fieldShown[SelfTimeInPercent]) {
d->m_columnIndex[Type] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTimeInPercent)));
}
- if (d->m_fieldShown[SelfDuration]) {
- d->m_columnIndex[SelfDuration] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
+ if (d->m_fieldShown[SelfTime]) {
+ d->m_columnIndex[SelfTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTime)));
}
if (d->m_fieldShown[CallCount]) {
d->m_columnIndex[CallCount] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
}
if (d->m_fieldShown[TimePerCall]) {
d->m_columnIndex[TimePerCall] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimePerCall)));
}
if (d->m_fieldShown[MedianTime]) {
d->m_columnIndex[MedianTime] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MedianTime)));
}
if (d->m_fieldShown[MaxTime]) {
d->m_columnIndex[MaxTime] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MaxTime)));
}
if (d->m_fieldShown[MinTime]) {
d->m_columnIndex[MinTime] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MinTime)));
}
if (d->m_fieldShown[Details]) {
d->m_columnIndex[Details] = fieldIndex;
- d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details")));
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Details)));
}
}
@@ -569,47 +492,41 @@ int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
void QmlProfilerEventsMainView::buildModel()
{
- if (d->m_profilerDataModel) {
- clear();
- if (d->m_viewType == V8ProfileView)
- d->buildV8ModelFromList( d->m_profilerDataModel->getV8Events() );
- else
- d->buildModelFromList( d->m_profilerDataModel->getEventDescriptions(), d->m_model->invisibleRootItem() );
-
- setShowExtendedStatistics(d->m_showExtendedStatistics);
+ clear();
+ parseModelProxy();
+ setShowExtendedStatistics(d->m_showExtendedStatistics);
- setRootIsDecorated(false);
- setSortingEnabled(true);
- sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
+ setRootIsDecorated(false);
+ setSortingEnabled(true);
+ sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
- expandAll();
- if (d->m_fieldShown[Name])
- resizeColumnToContents(0);
+ expandAll();
+ if (d->m_fieldShown[Name])
+ resizeColumnToContents(0);
- if (d->m_fieldShown[Type])
- resizeColumnToContents(d->m_fieldShown[Name]?1:0);
- collapseAll();
- }
+ if (d->m_fieldShown[Type])
+ resizeColumnToContents(d->m_fieldShown[Name]?1:0);
+ collapseAll();
}
-void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFromList( const QList<QmlRangeEventData *> &list, QStandardItem *parentItem)
+void QmlProfilerEventsMainView::parseModelProxy()
{
- foreach (QmlRangeEventData *binding, list) {
- if (binding->calls == 0)
- continue;
-
+ const QList <QmlProfilerEventsModelProxy::QmlEventStats> eventList = d->modelProxy->getData();
+ foreach (const QmlProfilerEventsModelProxy::QmlEventStats &event, eventList) {
+ QStandardItem *parentItem = d->m_model->invisibleRootItem();
QList<QStandardItem *> newRow;
- if (m_fieldShown[Name])
- newRow << new EventsViewItem(binding->displayName);
- if (m_fieldShown[Type]) {
- QString typeString = QmlProfilerEventsMainView::nameForType(binding->eventType);
+ if (d->m_fieldShown[Name])
+ newRow << new EventsViewItem(event.displayName);
+
+ if (d->m_fieldShown[Type]) {
+ QString typeString = QmlProfilerEventsMainView::nameForType(event.eventType);
QString toolTipText;
- if (binding->eventType == Binding) {
- if (binding->bindingType == (int)OptimizedBinding) {
+ if (event.eventType == Binding) {
+ if (event.bindingType == (int)OptimizedBinding) {
typeString = typeString + tr(" (Opt)");
toolTipText = tr("Binding is evaluated by the optimized engine.");
- } else if (binding->bindingType == (int)V8Binding) {
+ } else if (event.bindingType == (int)V8Binding) {
toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n"
"references to elements in other files, loops, etc.)");
@@ -621,44 +538,44 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
newRow.last()->setToolTip(toolTipText);
}
- if (m_fieldShown[Percent]) {
- newRow << new EventsViewItem(QString::number(binding->percentOfTime,'f',2)+QLatin1String(" %"));
- newRow.last()->setData(QVariant(binding->percentOfTime));
+ if (d->m_fieldShown[TimeInPercent]) {
+ newRow << new EventsViewItem(QString::number(event.percentOfTime,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(event.percentOfTime));
}
- if (m_fieldShown[TotalDuration]) {
- newRow << new EventsViewItem(displayTime(binding->duration));
- newRow.last()->setData(QVariant(binding->duration));
+ if (d->m_fieldShown[TotalTime]) {
+ newRow << new EventsViewItem(displayTime(event.duration));
+ newRow.last()->setData(QVariant(event.duration));
}
- if (m_fieldShown[CallCount]) {
- newRow << new EventsViewItem(QString::number(binding->calls));
- newRow.last()->setData(QVariant(binding->calls));
+ if (d->m_fieldShown[CallCount]) {
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow.last()->setData(QVariant(event.calls));
}
- if (m_fieldShown[TimePerCall]) {
- newRow << new EventsViewItem(displayTime(binding->timePerCall));
- newRow.last()->setData(QVariant(binding->timePerCall));
+ if (d->m_fieldShown[TimePerCall]) {
+ newRow << new EventsViewItem(displayTime(event.timePerCall));
+ newRow.last()->setData(QVariant(event.timePerCall));
}
- if (m_fieldShown[MedianTime]) {
- newRow << new EventsViewItem(displayTime(binding->medianTime));
- newRow.last()->setData(QVariant(binding->medianTime));
+ if (d->m_fieldShown[MedianTime]) {
+ newRow << new EventsViewItem(displayTime(event.medianTime));
+ newRow.last()->setData(QVariant(event.medianTime));
}
- if (m_fieldShown[MaxTime]) {
- newRow << new EventsViewItem(displayTime(binding->maxTime));
- newRow.last()->setData(QVariant(binding->maxTime));
+ if (d->m_fieldShown[MaxTime]) {
+ newRow << new EventsViewItem(displayTime(event.maxTime));
+ newRow.last()->setData(QVariant(event.maxTime));
}
- if (m_fieldShown[MinTime]) {
- newRow << new EventsViewItem(displayTime(binding->minTime));
- newRow.last()->setData(QVariant(binding->minTime));
+ if (d->m_fieldShown[MinTime]) {
+ newRow << new EventsViewItem(displayTime(event.minTime));
+ newRow.last()->setData(QVariant(event.minTime));
}
- if (m_fieldShown[Details]) {
- newRow << new EventsViewItem(binding->details);
- newRow.last()->setData(QVariant(binding->details));
+ if (d->m_fieldShown[Details]) {
+ newRow << new EventsViewItem(event.details);
+ newRow.last()->setData(QVariant(event.details));
}
@@ -669,16 +586,17 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
item->setEditable(false);
// metadata
- newRow.at(0)->setData(QVariant(binding->eventHashStr),EventHashStrRole);
- newRow.at(0)->setData(QVariant(binding->location.filename),FilenameRole);
- newRow.at(0)->setData(QVariant(binding->location.line),LineRole);
- newRow.at(0)->setData(QVariant(binding->location.column),ColumnRole);
- newRow.at(0)->setData(QVariant(binding->eventId),EventIdRole);
- if (binding->isBindingLoop)
+ newRow.at(0)->setData(QVariant(event.eventHashStr),EventHashStrRole);
+ newRow.at(0)->setData(QVariant(event.location.filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(event.location.line),LineRole);
+ newRow.at(0)->setData(QVariant(event.location.column),ColumnRole);
+
+ if (event.isBindingLoop) {
foreach (QStandardItem *item, newRow) {
item->setBackground(colors()->bindingLoopBackground);
item->setToolTip(tr("Binding loop detected."));
}
+ }
// append
parentItem->appendRow(newRow);
@@ -686,58 +604,6 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
}
}
-void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
-{
- for (int index = 0; index < list.count(); index++) {
- QV8EventData *v8event = list.at(index);
- QList<QStandardItem *> newRow;
-
- if (m_fieldShown[Name])
- newRow << new EventsViewItem(v8event->displayName);
-
- if (m_fieldShown[Percent]) {
- newRow << new EventsViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
- newRow.last()->setData(QVariant(v8event->totalPercent));
- }
-
- if (m_fieldShown[TotalDuration]) {
- newRow << new EventsViewItem(displayTime(v8event->totalTime));
- newRow.last()->setData(QVariant(v8event->totalTime));
- }
-
- if (m_fieldShown[SelfPercent]) {
- newRow << new EventsViewItem(QString::number(v8event->selfPercent,'f',2)+QLatin1String(" %"));
- newRow.last()->setData(QVariant(v8event->selfPercent));
- }
-
- if (m_fieldShown[SelfDuration]) {
- newRow << new EventsViewItem(displayTime(v8event->selfTime));
- newRow.last()->setData(QVariant(v8event->selfTime));
- }
-
- if (m_fieldShown[Details]) {
- newRow << new EventsViewItem(v8event->functionName);
- newRow.last()->setData(QVariant(v8event->functionName));
- }
-
- if (!newRow.isEmpty()) {
- // no edit
- foreach (QStandardItem *item, newRow)
- item->setEditable(false);
-
- // metadata
- newRow.at(0)->setData(QString::fromLatin1("%1:%2").arg(v8event->filename, QString::number(v8event->line)), EventHashStrRole);
- newRow.at(0)->setData(QVariant(v8event->filename), FilenameRole);
- newRow.at(0)->setData(QVariant(v8event->line), LineRole);
- newRow.at(0)->setData(QVariant(0),ColumnRole); // v8 events have no column info
- newRow.at(0)->setData(QVariant(v8event->eventId), EventIdRole);
-
- // append
- m_model->invisibleRootItem()->appendRow(newRow);
- }
- }
-}
-
QString QmlProfilerEventsMainView::displayTime(double time)
{
if (time < 1e6)
@@ -762,26 +628,16 @@ QString QmlProfilerEventsMainView::nameForType(int typeNumber)
void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{
- if (d->m_profilerDataModel)
- d->m_profilerDataModel->compileStatistics(rangeStart, rangeEnd);
- buildModel();
-}
-
-bool QmlProfilerEventsMainView::isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const
-{
- if (d->m_profilerDataModel)
- return d->m_profilerDataModel->traceStartTime() == rangeStart && d->m_profilerDataModel->traceEndTime() == rangeEnd;
- else
- return true;
+ d->modelProxy->limitToRange(rangeStart, rangeEnd);
}
-int QmlProfilerEventsMainView::selectedEventId() const
+QString QmlProfilerEventsMainView::selectedEventHash() const
{
QModelIndex index = selectedItem();
if (!index.isValid())
- return -1;
+ return QString();
QStandardItem *item = d->m_model->item(index.row(), 0);
- return item->data(EventIdRole).toInt();
+ return item->data(EventHashStrRole).toString();
}
@@ -806,20 +662,16 @@ void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
emit gotoSourceLocation(fileName, line, column);
// show in callers/callees subwindow
- emit eventSelected(infoItem->data(EventIdRole).toInt());
-
- // show in timelinerenderer
- if (d->m_viewType == EventsView)
- emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
+ emit eventSelected(infoItem->data(EventHashStrRole).toString());
d->m_preventSelectBounce = false;
}
-void QmlProfilerEventsMainView::selectEvent(int eventId)
+void QmlProfilerEventsMainView::selectEvent(const QString &eventHash)
{
for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0);
- if (infoItem->data(EventIdRole).toInt() == eventId) {
+ if (infoItem->data(EventHashStrRole).toString() == eventHash) {
setCurrentIndex(d->m_model->indexFromItem(infoItem));
jumpToItem(currentIndex());
return;
@@ -827,14 +679,18 @@ void QmlProfilerEventsMainView::selectEvent(int eventId)
}
}
-void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
+void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line, int column)
{
if (d->m_preventSelectBounce)
return;
for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0);
- if (currentIndex() != d->m_model->indexFromItem(infoItem) && infoItem->data(FilenameRole).toString() == filename && infoItem->data(LineRole).toInt() == line) {
+ if (currentIndex() != d->m_model->indexFromItem(infoItem) &&
+ infoItem->data(FilenameRole).toString() == filename &&
+ infoItem->data(LineRole).toInt() == line &&
+ (column == -1 ||
+ infoItem->data(ColumnRole).toInt() == column)) {
setCurrentIndex(d->m_model->indexFromItem(infoItem));
jumpToItem(currentIndex());
return;
@@ -851,23 +707,7 @@ QModelIndex QmlProfilerEventsMainView::selectedItem() const
return sel.first();
}
-void QmlProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString)
-{
- // available only for QML
- if (d->m_viewType != EventsView)
- return;
-
- for (int i=0; i<d->m_model->rowCount(); i++) {
- QStandardItem *infoItem = d->m_model->item(i, 0);
- if (infoItem->data(EventIdRole).toInt() == eventId) {
- d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
- d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
- return;
- }
- }
-}
-
-QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
+QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive) const
{
QString str;
@@ -931,114 +771,82 @@ void QmlProfilerEventsMainView::copyRowToClipboard() const
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventsParentsAndChildrenView::QmlProfilerEventsParentsAndChildrenView(
- SubViewType subtableType, QWidget *parent, QmlProfilerDataModel *model)
- : QTreeView(parent)
+class QmlProfilerEventRelativesView::QmlProfilerEventParentsViewPrivate
+{
+public:
+ QmlProfilerEventParentsViewPrivate(QmlProfilerEventRelativesView *qq):q(qq) {}
+ ~QmlProfilerEventParentsViewPrivate() {}
+
+ QmlProfilerEventRelativesModelProxy *modelProxy;
+
+ QmlProfilerEventRelativesView *q;
+};
+
+QmlProfilerEventRelativesView::QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager, QmlProfilerEventRelativesModelProxy *modelProxy, QWidget *parent)
+ : QmlProfilerTreeView(parent), d(new QmlProfilerEventParentsViewPrivate(this))
{
- m_profilerDataModel = model;
+ Q_UNUSED(modelManager);
+ setSortingEnabled(false);
+ d->modelProxy = modelProxy;
setModel(new QStandardItemModel(this));
setRootIsDecorated(false);
- setFrameStyle(QFrame::NoFrame);
- m_subtableType = subtableType;
updateHeader();
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
}
-QmlProfilerEventsParentsAndChildrenView::~QmlProfilerEventsParentsAndChildrenView()
-{
-}
-
-void QmlProfilerEventsParentsAndChildrenView::setViewType(SubViewType type)
+QmlProfilerEventRelativesView::~QmlProfilerEventRelativesView()
{
- m_subtableType = type;
- updateHeader();
+ delete d;
}
-void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
+void QmlProfilerEventRelativesView::displayEvent(const QString &eventHash)
{
- if (!m_profilerDataModel)
- return;
-
- bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
- bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
-
- if (isV8) {
- QV8EventData *v8event = m_profilerDataModel->v8EventDescription(eventId);
- if (v8event) {
- if (isChildren) {
- QList <QV8EventSub *> childrenList = v8event->childrenHash.values();
- rebuildTree((QObject *)&childrenList);
- } else {
- QList <QV8EventSub *> parentList = v8event->parentHash.values();
- rebuildTree((QObject *)&parentList);
- }
- }
- } else {
- QmlRangeEventData *qmlEvent = m_profilerDataModel->eventDescription(eventId);
- if (qmlEvent) {
- if (isChildren) {
- QList <QmlRangeEventRelative *> childrenList = qmlEvent->childrenHash.values();
- rebuildTree((QObject *)&childrenList);
- } else {
- QList <QmlRangeEventRelative *> parentList = qmlEvent->parentHash.values();
- rebuildTree((QObject *)&parentList);
- }
- }
- }
+ rebuildTree(d->modelProxy->getData(eventHash));
updateHeader();
resizeColumnToContents(0);
setSortingEnabled(true);
- if (isV8)
- sortByColumn(1);
- else
- sortByColumn(2);
+ sortByColumn(2);
}
-void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataModel)
+void QmlProfilerEventRelativesView::rebuildTree(QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap eventMap)
{
Q_ASSERT(treeModel());
treeModel()->clear();
QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
- bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
-
- QList <QmlRangeEventRelative *> *qmlList = static_cast< QList <QmlRangeEventRelative *> *>(profilerDataModel);
- QList <QV8EventSub*> *v8List = static_cast< QList <QV8EventSub *> *>(profilerDataModel);
- int listLength;
- if (!isV8)
- listLength = qmlList->length();
- else
- listLength = v8List->length();
-
- for (int index=0; index < listLength; index++) {
+ //foreach (const QmlProfilerEventParentsModelProxy::QmlEventParentData &event, eventMap.values()) {
+ foreach (const QString &key, eventMap.keys()) {
+ const QmlProfilerEventRelativesModelProxy::QmlEventRelativesData &event = eventMap[key];
QList<QStandardItem *> newRow;
- if (!isV8) {
- QmlRangeEventRelative *event = qmlList->at(index);
-
- newRow << new EventsViewItem(event->reference->displayName);
- newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
- newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
- newRow << new EventsViewItem(QString::number(event->calls));
- newRow << new EventsViewItem(event->reference->details);
- newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
- newRow.at(2)->setData(QVariant(event->duration));
- newRow.at(3)->setData(QVariant(event->calls));
- if (event->inLoopPath)
- foreach (QStandardItem *item, newRow) {
- item->setBackground(colors()->bindingLoopBackground);
- item->setToolTip(tr("Part of binding loop."));
- }
- } else {
- QV8EventSub *event = v8List->at(index);
- newRow << new EventsViewItem(event->reference->displayName);
- newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->totalTime));
- newRow << new EventsViewItem(event->reference->functionName);
- newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
- newRow.at(1)->setData(QVariant(event->totalTime));
+
+ // ToDo: here we were going to search for the data in the other modelproxy
+ // maybe we should store the data in this proxy and get it here
+ // no indirections at this level of abstraction!
+ newRow << new EventsViewItem(event.displayName);
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event.eventType));
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event.duration));
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow << new EventsViewItem(event.details);
+
+// newRow << new EventsViewItem(event->reference->displayName);
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
+// newRow << new EventsViewItem(QString::number(event->calls));
+// newRow << new EventsViewItem(event->reference->details);
+ newRow.at(0)->setData(QVariant(key), EventHashStrRole);
+ newRow.at(2)->setData(QVariant(event.duration));
+ newRow.at(3)->setData(QVariant(event.calls));
+
+ if (event.isBindingLoop) {
+ foreach (QStandardItem *item, newRow) {
+ item->setBackground(colors()->bindingLoopBackground);
+ item->setToolTip(tr("Part of binding loop."));
+ }
}
+
foreach (QStandardItem *item, newRow)
item->setEditable(false);
@@ -1046,7 +854,7 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataMode
}
}
-void QmlProfilerEventsParentsAndChildrenView::clear()
+void QmlProfilerEventRelativesView::clear()
{
if (treeModel()) {
treeModel()->clear();
@@ -1054,52 +862,43 @@ void QmlProfilerEventsParentsAndChildrenView::clear()
}
}
-void QmlProfilerEventsParentsAndChildrenView::updateHeader()
+void QmlProfilerEventRelativesView::updateHeader()
{
- bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
- bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
-
- header()->setResizeMode(QHeaderView::Interactive);
- header()->setDefaultSectionSize(100);
- header()->setMinimumSectionSize(50);
+ bool calleesView = qobject_cast<QmlProfilerEventChildrenModelProxy *>(d->modelProxy) != 0;
if (treeModel()) {
- if (isV8)
- treeModel()->setColumnCount(3);
- else
- treeModel()->setColumnCount(5);
+ treeModel()->setColumnCount(5);
int columnIndex = 0;
- if (isChildren)
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Callee)));
else
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Caller)));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
- if (!isV8)
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
- if (!isV8)
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
- if (isChildren)
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CalleeDescription)));
else
- treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallerDescription)));
}
}
-QStandardItemModel *QmlProfilerEventsParentsAndChildrenView::treeModel()
+QStandardItemModel *QmlProfilerEventRelativesView::treeModel()
{
return qobject_cast<QStandardItemModel *>(model());
}
-void QmlProfilerEventsParentsAndChildrenView::jumpToItem(const QModelIndex &index)
+void QmlProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
{
if (treeModel()) {
QStandardItem *infoItem = treeModel()->item(index.row(), 0);
- emit eventClicked(infoItem->data(EventIdRole).toInt());
+ emit eventClicked(infoItem->data(EventHashStrRole).toString());
}
}
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index 2dd0145c2e..747cc613d3 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -33,7 +33,9 @@
#include <QTreeView>
#include <QStandardItemModel>
#include <qmldebug/qmlprofilereventtypes.h>
-#include "qmlprofilerdatamodel.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilereventsmodelproxy.h"
+#include "qmlprofilertreeview.h"
#include <analyzerbase/ianalyzertool.h>
@@ -43,7 +45,8 @@ namespace QmlProfiler {
namespace Internal {
class QmlProfilerEventsMainView;
-class QmlProfilerEventsParentsAndChildrenView;
+class QmlProfilerEventChildrenView;
+class QmlProfilerEventRelativesView;
enum ItemRole {
EventHashStrRole = Qt::UserRole+1,
@@ -60,10 +63,9 @@ public:
explicit QmlProfilerEventsWidget(QWidget *parent,
QmlProfilerTool *profilerTool,
QmlProfilerViewManager *container,
- QmlProfilerDataModel *profilerDataModel );
+ QmlProfilerModelManager *profilerModelManager );
~QmlProfilerEventsWidget();
- void switchToV8View();
void clear();
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
@@ -76,16 +78,14 @@ public:
void setShowExtendedStatistics(bool show);
bool showExtendedStatistics() const;
- bool isQml() const;
- bool isV8() const;
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
- void showEventInTimeline(int eventId);
+ void eventSelectedByHash(const QString &eventHash);
void resized();
public slots:
- void updateSelectedEvent(int eventId) const;
+ void updateSelectedEvent(const QString &eventHash) const;
void selectBySourceLocation(const QString &filename, int line, int column);
private slots:
@@ -100,44 +100,15 @@ private:
QmlProfilerEventsWidgetPrivate *d;
};
-class QmlProfilerEventsMainView : public QTreeView
+class QmlProfilerEventsMainView : public QmlProfilerTreeView
{
Q_OBJECT
public:
- enum Fields {
- Name,
- Type,
- Percent,
- TotalDuration,
- SelfPercent,
- SelfDuration,
- CallCount,
- TimePerCall,
- MaxTime,
- MinTime,
- MedianTime,
- Details,
-
- MaxFields
- };
-
- enum ViewTypes {
- EventsView,
- CallersView,
- CalleesView,
- V8ProfileView,
-
- MaxViewTypes
- };
-
- explicit QmlProfilerEventsMainView(ViewTypes viewType,
- QWidget *parent,
- QmlProfilerDataModel *dataModel);
+ explicit QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy);
~QmlProfilerEventsMainView();
void setFieldViewable(Fields field, bool show);
- void setViewType(ViewTypes type);
- ViewTypes viewType() const;
void setShowAnonymousEvents( bool showThem );
QModelIndex selectedItem() const;
@@ -148,30 +119,30 @@ public:
static QString nameForType(int typeNumber);
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
- bool isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const;
- int selectedEventId() const;
+// int selectedEventId() const;
+ QString selectedEventHash() const;
void setShowExtendedStatistics(bool);
bool showExtendedStatistics() const;
+
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
- void eventSelected(int eventId);
- void showEventInTimeline(int eventId);
+ void eventSelected(const QString &eventHash);
public slots:
void clear();
void jumpToItem(const QModelIndex &index);
- void selectEvent(int eventId);
- void selectEventByLocation(const QString &filename, int line);
+ void selectEvent(const QString &eventHash);
+ void selectEventByLocation(const QString &filename, int line, int column);
void buildModel();
- void changeDetailsForEvent(int eventId, const QString &newString);
private slots:
void profilerDataModelStateChanged();
private:
void setHeaderLabels();
+ void parseModelProxy();
private:
class QmlProfilerEventsMainViewPrivate;
@@ -179,40 +150,31 @@ private:
};
-class QmlProfilerEventsParentsAndChildrenView : public QTreeView
+class QmlProfilerEventRelativesView : public QmlProfilerTreeView
{
Q_OBJECT
public:
- enum SubViewType {
- ParentsView,
- ChildrenView,
- V8ParentsView,
- V8ChildrenView,
- MaxSubtableTypes
- };
-
- explicit QmlProfilerEventsParentsAndChildrenView(SubViewType subtableType,
- QWidget *parent,
- QmlProfilerDataModel *model);
- ~QmlProfilerEventsParentsAndChildrenView();
-
- void setViewType(SubViewType type);
+ explicit QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventRelativesModelProxy *modelProxy,
+ QWidget *parent );
+ ~QmlProfilerEventRelativesView();
signals:
- void eventClicked(int eventId);
+ void eventClicked(const QString &eventHash);
public slots:
- void displayEvent(int eventId);
+ void displayEvent(const QString &eventHash);
void jumpToItem(const QModelIndex &);
void clear();
private:
- void rebuildTree(void *profilerDataModel);
+ void rebuildTree(QmlProfilerEventParentsModelProxy::QmlEventRelativesMap eventMap);
void updateHeader();
QStandardItemModel *treeModel();
- QmlProfilerDataModel *m_profilerDataModel;
+// QmlProfilerModelManager *m_profilerModelManager;
- SubViewType m_subtableType;
+ class QmlProfilerEventParentsViewPrivate;
+ QmlProfilerEventParentsViewPrivate *d;
};
} // namespace Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
new file mode 100644
index 0000000000..46df68f9b3
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
@@ -0,0 +1,364 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+#include "qmlprofilerprocessedmodel.h"
+#include "qv8profilerdatamodel.h"
+#include "qmlprofilertracefile.h"
+
+#include <utils/qtcassert.h>
+
+#include <QDebug>
+#include <QFile>
+
+namespace QmlProfiler {
+namespace Internal {
+
+
+/////////////////////////////////////////////////////////////////////
+QmlProfilerDataState::QmlProfilerDataState(QmlProfilerModelManager *modelManager, QObject *parent)
+ : QObject(parent), m_state(Empty), m_modelManager(modelManager)
+{
+ connect(this, SIGNAL(error(QString)), m_modelManager, SIGNAL(error(QString)));
+ connect(this, SIGNAL(stateChanged()), m_modelManager, SIGNAL(stateChanged()));
+}
+
+void QmlProfilerDataState::setState(QmlProfilerDataState::State state)
+{
+ // It's not an error, we are continuously calling "AcquiringData" for example
+ if (m_state == state)
+ return;
+
+ switch (state) {
+ case Empty:
+ // if it's not empty, complain but go on
+ QTC_ASSERT(m_modelManager->isEmpty(), /**/);
+ break;
+ case AcquiringData:
+ // we're not supposed to receive new data while processing older data
+ QTC_ASSERT(m_state != ProcessingData, return);
+ break;
+ case ProcessingData:
+ QTC_ASSERT(m_state == AcquiringData, return);
+ break;
+ case Done:
+ QTC_ASSERT(m_state == ProcessingData || m_state == Empty, return);
+ break;
+ default:
+ emit error(tr("Trying to set unknown state in events list"));
+ break;
+ }
+
+ m_state = state;
+ emit stateChanged();
+
+ return;
+}
+
+
+/////////////////////////////////////////////////////////////////////
+QmlProfilerTraceTime::QmlProfilerTraceTime(QObject *parent) : QObject(parent)
+{
+ clear();
+}
+
+QmlProfilerTraceTime::~QmlProfilerTraceTime()
+{
+}
+
+qint64 QmlProfilerTraceTime::startTime() const
+{
+ return m_startTime;
+}
+
+qint64 QmlProfilerTraceTime::endTime() const
+{
+ return m_endTime;
+}
+
+qint64 QmlProfilerTraceTime::duration() const
+{
+ return endTime() - startTime();
+}
+
+void QmlProfilerTraceTime::clear()
+{
+ m_startTime = -1;
+ m_endTime = 0;
+}
+
+void QmlProfilerTraceTime::setStartTime(qint64 time)
+{
+ m_startTime = time;
+}
+
+void QmlProfilerTraceTime::setEndTime(qint64 time)
+{
+ m_endTime = time;
+}
+
+
+} // namespace Internal
+
+/////////////////////////////////////////////////////////////////////
+
+class QmlProfilerModelManager::QmlProfilerModelManagerPrivate
+{
+public:
+ QmlProfilerModelManagerPrivate(QmlProfilerModelManager *qq) : q(qq) {}
+ ~QmlProfilerModelManagerPrivate() {}
+ QmlProfilerModelManager *q;
+
+ QmlProfilerSimpleModel *model;
+ QV8ProfilerDataModel *v8Model;
+ QmlProfilerDataState *dataState;
+ QmlProfilerTraceTime *traceTime;
+
+ QVector <double> partialCounts;
+ double progress;
+ qint64 estimatedTime;
+
+ // file to load
+ QString fileName;
+};
+
+
+QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent) :
+ QObject(parent), d(new QmlProfilerModelManagerPrivate(this))
+{
+ d->model = new QmlProfilerProcessedModel(finder, this);
+ d->v8Model = new QV8ProfilerDataModel(this);
+// d->model = new QmlProfilerSimpleModel(this);
+ d->dataState = new QmlProfilerDataState(this, this);
+ d->traceTime = new QmlProfilerTraceTime(this);
+}
+
+QmlProfilerModelManager::~QmlProfilerModelManager()
+{
+ delete d;
+}
+
+QmlProfilerTraceTime *QmlProfilerModelManager::traceTime() const
+{
+ return d->traceTime;
+}
+
+QmlProfilerSimpleModel *QmlProfilerModelManager::simpleModel() const
+{
+ return d->model;
+}
+
+QV8ProfilerDataModel *QmlProfilerModelManager::v8Model() const
+{
+ return d->v8Model;
+}
+
+bool QmlProfilerModelManager::isEmpty() const
+{
+ return d->model->isEmpty() && d->v8Model->isEmpty();
+}
+
+int QmlProfilerModelManager::count() const
+{
+ return d->model->count();
+}
+
+double QmlProfilerModelManager::progress() const
+{
+ return d->progress;
+}
+
+int QmlProfilerModelManager::registerModelProxy()
+{
+ d->partialCounts << 0;
+ return d->partialCounts.count()-1;
+}
+
+void QmlProfilerModelManager::modelProxyCountUpdated(int proxyId, qint64 count, qint64 max)
+{
+ d->progress -= d->partialCounts[proxyId] / d->partialCounts.count();
+
+ if (max <= 0)
+ d->partialCounts[proxyId] = 1;
+ else
+ d->partialCounts[proxyId] = (double)count / (double) max;
+
+ d->progress += d->partialCounts[proxyId] / d->partialCounts.count();
+
+ emit progressChanged();
+ if (d->progress > 0.99)
+ emit dataAvailable();
+}
+
+qint64 QmlProfilerModelManager::estimatedProfilingTime() const
+{
+ return d->estimatedTime;
+}
+
+void QmlProfilerModelManager::newTimeEstimation(qint64 estimation)
+{
+ d->estimatedTime = estimation;
+}
+
+void QmlProfilerModelManager::addQmlEvent(int type,
+ int bindingType,
+ qint64 startTime,
+ qint64 length,
+ const QStringList &data,
+ const QmlDebug::QmlEventLocation &location,
+ qint64 ndata1,
+ qint64 ndata2,
+ qint64 ndata3,
+ qint64 ndata4,
+ qint64 ndata5)
+{
+ // If trace start time was not explicitly set, use the first event
+ if (d->traceTime->startTime() == -1)
+ d->traceTime->setStartTime(startTime);
+
+ QTC_ASSERT(state() == QmlProfilerDataState::AcquiringData, /**/);
+ d->model->addQmlEvent(type, bindingType, startTime, length, data, location, ndata1, ndata2, ndata3, ndata4, ndata5);
+ emit countChanged();
+}
+
+void QmlProfilerModelManager::addV8Event(int depth, const QString &function, const QString &filename,
+ int lineNumber, double totalTime, double selfTime)
+{
+ d->v8Model->addV8Event(depth, function, filename, lineNumber,totalTime, selfTime);
+}
+
+void QmlProfilerModelManager::complete()
+{
+ if (state() == QmlProfilerDataState::AcquiringData) {
+ // If trace end time was not explicitly set, use the last event
+ if (d->traceTime->endTime() == 0)
+ d->traceTime->setEndTime(d->model->lastTimeMark());
+ setState(QmlProfilerDataState::ProcessingData);
+ d->model->complete();
+ d->v8Model->complete();
+ setState(QmlProfilerDataState::Done);
+ } else
+ if (state() == QmlProfilerDataState::Empty) {
+ setState(QmlProfilerDataState::Done);
+ } else
+ if (state() == QmlProfilerDataState::Done) {
+ // repeated Done states are ignored
+ } else {
+ emit error(tr("Unexpected complete signal in data model"));
+ }
+}
+
+void QmlProfilerModelManager::save(const QString &filename)
+{
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ emit error(tr("Could not open %1 for writing.").arg(filename));
+ return;
+ }
+
+ QmlProfilerFileWriter writer;
+
+ writer.setTraceTime(traceTime()->startTime(), traceTime()->endTime(), traceTime()->duration());
+ writer.setV8DataModel(d->v8Model);
+ writer.setQmlEvents(d->model->getEvents());
+ writer.save(&file);
+}
+
+void QmlProfilerModelManager::load(const QString &filename)
+{
+ d->fileName = filename;
+ load();
+}
+
+void QmlProfilerModelManager::setFilename(const QString &filename)
+{
+ d->fileName = filename;
+}
+
+void QmlProfilerModelManager::load()
+{
+ QString filename = d->fileName;
+
+ QFile file(filename);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ emit error(tr("Could not open %1 for reading.").arg(filename));
+ return;
+ }
+
+ // erase current
+ clear();
+
+ setState(QmlProfilerDataState::AcquiringData);
+
+ QmlProfilerFileReader reader;
+ connect(&reader, SIGNAL(error(QString)), this, SIGNAL(error(QString)));
+ connect(&reader, SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
+ qint64, qint64, qint64, qint64, qint64)),
+ this, SLOT(addQmlEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
+ qint64, qint64, qint64, qint64, qint64)));
+ connect(&reader, SIGNAL(traceStartTime(qint64)), traceTime(), SLOT(setStartTime(qint64)));
+ connect(&reader, SIGNAL(traceEndTime(qint64)), traceTime(), SLOT(setEndTime(qint64)));
+ reader.setV8DataModel(d->v8Model);
+ reader.load(&file);
+
+ complete();
+}
+
+
+void QmlProfilerModelManager::setState(QmlProfilerDataState::State state)
+{
+ d->dataState->setState(state);
+}
+
+QmlProfilerDataState::State QmlProfilerModelManager::state() const
+{
+ return d->dataState->state();
+}
+
+void QmlProfilerModelManager::clear()
+{
+ for (int i = 0; i < d->partialCounts.count(); i++)
+ d->partialCounts[i] = 0;
+ d->progress = 0;
+ d->model->clear();
+ d->v8Model->clear();
+ d->traceTime->clear();
+
+ emit countChanged();
+ setState(QmlProfilerDataState::Empty);
+}
+
+void QmlProfilerModelManager::prepareForWriting()
+{
+ setState(QmlProfilerDataState::AcquiringData);
+}
+
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
new file mode 100644
index 0000000000..43e2c46844
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERMODELMANAGER_H
+#define QMLPROFILERMODELMANAGER_H
+
+#include <QObject>
+#include "qmlprofiler_global.h"
+#include "qmldebug/qmlprofilereventlocation.h"
+#include <utils/fileinprojectfinder.h>
+
+namespace QmlProfiler {
+class QmlProfilerSimpleModel;
+class QmlProfilerModelManager;
+
+namespace Internal {
+
+class QV8ProfilerDataModel;
+class QmlProfilerDataState : public QObject
+{
+ Q_OBJECT
+public:
+ enum State {
+ Empty,
+ AcquiringData,
+ ProcessingData,
+ Done
+ };
+
+ explicit QmlProfilerDataState(QmlProfilerModelManager *modelManager, QObject *parent = 0);
+ ~QmlProfilerDataState() {}
+
+ State state() const { return m_state; }
+
+signals:
+ void stateChanged();
+ void error(const QString &error);
+
+private:
+ void setState(State state);
+ State m_state;
+ QmlProfilerModelManager *m_modelManager;
+
+ friend class QmlProfiler::QmlProfilerModelManager;
+};
+
+class QmlProfilerTraceTime : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerTraceTime(QObject *parent);
+ ~QmlProfilerTraceTime();
+
+ qint64 startTime() const;
+ qint64 endTime() const;
+ qint64 duration() const;
+
+public slots:
+ void clear();
+ void setStartTime(qint64 time);
+ void setEndTime(qint64 time);
+
+private:
+ qint64 m_startTime;
+ qint64 m_endTime;
+};
+
+} // End internal namespace
+
+using namespace Internal;
+
+// Interface between the Data Model and the Engine/Tool
+class QMLPROFILER_EXPORT QmlProfilerModelManager : public QObject
+{
+ Q_OBJECT
+public:
+
+ explicit QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent = 0);
+ ~QmlProfilerModelManager();
+
+ QmlProfilerDataState::State state() const;
+ QmlProfilerTraceTime *traceTime() const;
+ QmlProfilerSimpleModel *simpleModel() const;
+ QV8ProfilerDataModel *v8Model() const;
+
+ bool isEmpty() const;
+ int count() const;
+
+ double progress() const;
+ int registerModelProxy();
+ void modelProxyCountUpdated(int proxyId, qint64 count, qint64 max);
+
+ qint64 estimatedProfilingTime() const;
+
+signals:
+ void countChanged();
+ void error(const QString &error);
+ void stateChanged();
+ void progressChanged();
+ void dataAvailable();
+
+ void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
+
+public slots:
+ void clear();
+
+ void prepareForWriting();
+ void addQmlEvent(int type, int bindingType, qint64 startTime, qint64 length,
+ const QStringList &data, const QmlDebug::QmlEventLocation &location,
+ qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5);
+ void addV8Event(int depth, const QString &function,const QString &filename, int lineNumber,
+ double totalTime, double selfTime);
+
+ void complete();
+
+ void save(const QString &filename);
+ void load(const QString &filename);
+ void setFilename(const QString &filename);
+ void load();
+
+ void newTimeEstimation(qint64 estimation);
+private:
+ void setState(QmlProfilerDataState::State state);
+
+
+private:
+ class QmlProfilerModelManagerPrivate;
+ QmlProfilerModelManagerPrivate *d;
+};
+
+}
+
+#endif
diff --git a/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp
new file mode 100644
index 0000000000..9cc160b1a5
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.cpp
@@ -0,0 +1,458 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerpainteventsmodelproxy.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+#include <QCoreApplication>
+
+#include <QVector>
+#include <QHash>
+#include <QUrl>
+#include <QString>
+#include <QStack>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct CategorySpan {
+ bool expanded;
+ int expandedRows;
+ int contractedRows;
+};
+
+class PaintEventsModelProxy::PaintEventsModelProxyPrivate
+{
+public:
+ PaintEventsModelProxyPrivate(PaintEventsModelProxy *qq) : q(qq) {}
+ ~PaintEventsModelProxyPrivate() {}
+
+ QString displayTime(double time);
+ void computeAnimationCountLimit();
+
+ QVector <PaintEventsModelProxy::QmlPaintEventData> eventList;
+ int minAnimationCount;
+ int maxAnimationCount;
+ bool expanded;
+
+ PaintEventsModelProxy *q;
+};
+
+PaintEventsModelProxy::PaintEventsModelProxy(QObject *parent)
+ : AbstractTimelineModel(parent), d(new PaintEventsModelProxyPrivate(this))
+{
+}
+
+PaintEventsModelProxy::~PaintEventsModelProxy()
+{
+ delete d;
+}
+
+int PaintEventsModelProxy::categories() const
+{
+ return categoryCount();
+}
+
+QStringList PaintEventsModelProxy::categoryTitles() const
+{
+ QStringList retString;
+ for (int i=0; i<categories(); i++)
+ retString << categoryLabel(i);
+ return retString;
+}
+
+QString PaintEventsModelProxy::name() const
+{
+ return QLatin1String("PaintEventsModelProxy");
+}
+
+const QVector<PaintEventsModelProxy::QmlPaintEventData> PaintEventsModelProxy::getData() const
+{
+ return d->eventList;
+}
+
+const QVector<PaintEventsModelProxy::QmlPaintEventData> PaintEventsModelProxy::getData(qint64 fromTime, qint64 toTime) const
+{
+ int fromIndex = findFirstIndex(fromTime);
+ int toIndex = findLastIndex(toTime);
+ if (fromIndex != -1 && toIndex > fromIndex)
+ return d->eventList.mid(fromIndex, toIndex - fromIndex + 1);
+ else
+ return QVector<PaintEventsModelProxy::QmlPaintEventData>();
+}
+
+void PaintEventsModelProxy::clear()
+{
+ d->eventList.clear();
+ d->minAnimationCount = 1;
+ d->maxAnimationCount = 1;
+ d->expanded = false;
+ m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1);
+}
+
+void PaintEventsModelProxy::dataChanged()
+{
+ if (m_modelManager->state() == QmlProfilerDataState::ProcessingData)
+ loadData();
+
+ if (m_modelManager->state() == QmlProfilerDataState::Empty)
+ clear();
+
+ emit stateChanged();
+ emit dataAvailable();
+ emit emptyChanged();
+ emit expandedChanged();
+}
+
+bool compareStartTimes(const PaintEventsModelProxy::QmlPaintEventData &t1, const PaintEventsModelProxy::QmlPaintEventData &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+bool PaintEventsModelProxy::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const
+{
+ return (event.eventType == QmlDebug::Painting && event.bindingType == QmlDebug::AnimationFrame);
+}
+
+void PaintEventsModelProxy::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ // collect events
+ const QVector<QmlProfilerSimpleModel::QmlEventData> referenceList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, referenceList) {
+ if (!eventAccepted(event))
+ continue;
+
+ qint64 estimatedDuration = 0;
+ // initial estimation of the event duration: 1/framerate
+ if (event.numericData1 > 0)
+ estimatedDuration = 1e9/event.numericData1;
+
+ // the profiler registers the animation events at the end of them
+ qint64 realStartTime = event.startTime - estimatedDuration;
+
+ // the duration of the events is estimated from the framerate
+ // we need to correct it before appending a new event
+ if (d->eventList.count() > 0) {
+ QmlPaintEventData *lastEvent = &d->eventList[d->eventList.count()-1];
+ if (lastEvent->startTime + lastEvent->duration >= realStartTime) {
+ // 1 nanosecond less to prevent overlap
+ lastEvent->duration = realStartTime - lastEvent->startTime - 1;
+ lastEvent->framerate = 1e9/lastEvent->duration;
+ }
+ }
+
+ QmlPaintEventData newEvent = {
+ realStartTime,
+ estimatedDuration,
+ (int)event.numericData1,
+ (int)event.numericData2
+ };
+
+ d->eventList.append(newEvent);
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, d->eventList.count(), referenceList.count());
+ }
+
+ d->computeAnimationCountLimit();
+
+ qSort(d->eventList.begin(), d->eventList.end(), compareStartTimes);
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1);
+
+ emit countChanged();
+}
+
+/////////////////// QML interface
+
+bool PaintEventsModelProxy::isEmpty() const
+{
+ return count() == 0;
+}
+
+int PaintEventsModelProxy::count() const
+{
+ return d->eventList.count();
+}
+
+qint64 PaintEventsModelProxy::lastTimeMark() const
+{
+ return d->eventList.last().startTime + d->eventList.last().duration;
+}
+
+bool PaintEventsModelProxy::expanded(int ) const
+{
+ return d->expanded;
+}
+
+void PaintEventsModelProxy::setExpanded(int category, bool expanded)
+{
+ Q_UNUSED(category);
+ d->expanded = expanded;
+ emit expandedChanged();
+}
+
+int PaintEventsModelProxy::categoryDepth(int categoryIndex) const
+{
+ Q_UNUSED(categoryIndex);
+ if (isEmpty())
+ return 0;
+ else
+ return 2;
+}
+
+int PaintEventsModelProxy::categoryCount() const
+{
+ return 1;
+}
+
+const QString PaintEventsModelProxy::categoryLabel(int categoryIndex) const
+{
+ Q_UNUSED(categoryIndex);
+ return tr("Painting");
+}
+
+
+int PaintEventsModelProxy::findFirstIndex(qint64 startTime) const
+{
+ return findFirstIndexNoParents(startTime);
+}
+
+int PaintEventsModelProxy::findFirstIndexNoParents(qint64 startTime) const
+{
+ if (d->eventList.isEmpty())
+ return -1;
+ if (d->eventList.count() == 1 || d->eventList.first().startTime+d->eventList.first().duration >= startTime)
+ return 0;
+ else
+ if (d->eventList.last().startTime+d->eventList.last().duration <= startTime)
+ return -1;
+
+ int fromIndex = 0;
+ int toIndex = d->eventList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->eventList[midIndex].startTime + d->eventList[midIndex].duration < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+ return toIndex;
+}
+
+int PaintEventsModelProxy::findLastIndex(qint64 endTime) const
+{
+ if (d->eventList.isEmpty())
+ return -1;
+ if (d->eventList.first().startTime >= endTime)
+ return -1;
+ if (d->eventList.count() == 1)
+ return 0;
+ if (d->eventList.last().startTime <= endTime)
+ return d->eventList.count()-1;
+
+ int fromIndex = 0;
+ int toIndex = d->eventList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->eventList[midIndex].startTime < endTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ return fromIndex;
+}
+
+int PaintEventsModelProxy::getEventType(int index) const
+{
+ Q_UNUSED(index);
+ return (int)QmlDebug::Painting;
+}
+
+int PaintEventsModelProxy::getEventCategory(int index) const
+{
+ Q_UNUSED(index);
+ // there is only one category, all events belong to it
+ return 0;
+}
+
+int PaintEventsModelProxy::getEventRow(int index) const
+{
+ Q_UNUSED(index);
+ return 1;
+}
+
+qint64 PaintEventsModelProxy::getDuration(int index) const
+{
+ return d->eventList[index].duration;
+}
+
+qint64 PaintEventsModelProxy::getStartTime(int index) const
+{
+ return d->eventList[index].startTime;
+}
+
+qint64 PaintEventsModelProxy::getEndTime(int index) const
+{
+ return d->eventList[index].startTime + d->eventList[index].duration;
+}
+
+int PaintEventsModelProxy::getEventId(int index) const
+{
+ // there is only one event Id for all painting events
+ Q_UNUSED(index);
+ return 0;
+}
+
+QColor PaintEventsModelProxy::getColor(int index) const
+{
+ double fpsFraction = d->eventList[index].framerate / 60.0;
+ if (fpsFraction > 1.0)
+ fpsFraction = 1.0;
+ if (fpsFraction < 0.0)
+ fpsFraction = 0.0;
+ return QColor::fromHsl((fpsFraction*96)+10, 76, 166);
+}
+
+float PaintEventsModelProxy::getHeight(int index) const
+{
+ float scale = d->maxAnimationCount - d->minAnimationCount;
+ float fraction = 1.0f;
+ if (scale > 1)
+ fraction = (float)(d->eventList[index].animationcount -
+ d->minAnimationCount) / scale;
+
+ return fraction * 0.85f + 0.15f;
+}
+
+const QVariantList PaintEventsModelProxy::getLabelsForCategory(int category) const
+{
+ Q_UNUSED(category);
+ QVariantList result;
+
+ if (!isEmpty()) {
+ QVariantMap element;
+ element.insert(QLatin1String("displayName"), QVariant(QLatin1String("Animations")));
+ element.insert(QLatin1String("description"), QVariant(QLatin1String("Animations")));
+ element.insert(QLatin1String("id"), QVariant(0));
+ result << element;
+ }
+
+ return result;
+}
+
+QString PaintEventsModelProxy::PaintEventsModelProxyPrivate::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+void PaintEventsModelProxy::PaintEventsModelProxyPrivate::computeAnimationCountLimit()
+{
+ minAnimationCount = 1;
+ maxAnimationCount = 1;
+ if (eventList.isEmpty())
+ return;
+
+ for (int i=0; i < eventList.count(); i++) {
+ if (eventList[i].animationcount < minAnimationCount)
+ minAnimationCount = eventList[i].animationcount;
+ if (eventList[i].animationcount > maxAnimationCount)
+ maxAnimationCount = eventList[i].animationcount;
+ }
+}
+
+const QVariantList PaintEventsModelProxy::getEventDetails(int index) const
+{
+ QVariantList result;
+// int eventId = getEventId(index);
+
+ static const char trContext[] = "RangeDetails";
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QLatin1String("title"), QVariant(categoryLabel(0)));
+ result << valuePair;
+ }
+
+ // duration
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->eventList[index].duration)));
+ result << valuePair;
+ }
+
+ // duration
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QCoreApplication::translate(trContext, "Framerate:"), QVariant(QString::fromLatin1("%1 FPS").arg(d->eventList[index].framerate)));
+ result << valuePair;
+ }
+
+ // duration
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QCoreApplication::translate(trContext, "Animations:"), QVariant(QString::fromLatin1("%1").arg(d->eventList[index].animationcount)));
+ result << valuePair;
+ }
+
+ return result;
+}
+
+const QVariantMap PaintEventsModelProxy::getEventLocation(int /*index*/) const
+{
+ QVariantMap map;
+ return map;
+}
+
+int PaintEventsModelProxy::getEventIdForHash(const QString &/*eventHash*/) const
+{
+ // paint events do not have an eventHash
+ return -1;
+}
+
+int PaintEventsModelProxy::getEventIdForLocation(const QString &/*filename*/, int /*line*/, int /*column*/) const
+{
+ // paint events do not have a defined location
+ return -1;
+}
+
+}
+}
+
diff --git a/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h
new file mode 100644
index 0000000000..4be60850ef
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerpainteventsmodelproxy.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef QMLPROFILERPAINTEVENTSMODELPROXY_H
+#define QMLPROFILERPAINTEVENTSMODELPROXY_H
+
+#include <QObject>
+#include "abstracttimelinemodel.h"
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <qmldebug/qmlprofilereventlocation.h>
+//#include <QHash>
+//#include <QVector>
+#include <QVariantList>
+//#include <QVariantMap>
+#include "qmlprofilersimplemodel.h"
+#include <QColor>
+
+
+namespace QmlProfiler {
+class QmlProfilerModelManager;
+
+namespace Internal {
+
+class PaintEventsModelProxy : public AbstractTimelineModel
+{
+// Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
+
+ Q_OBJECT
+public:
+
+ struct QmlPaintEventData {
+ qint64 startTime;
+ qint64 duration;
+ int framerate;
+ int animationcount;
+ };
+
+ PaintEventsModelProxy(QObject *parent = 0);
+ ~PaintEventsModelProxy();
+
+
+ int categories() const;
+ QStringList categoryTitles() const;
+ QString name() const;
+
+ const QVector<QmlPaintEventData> getData() const;
+ const QVector<QmlPaintEventData> getData(qint64 fromTime, qint64 toTime) const;
+ void loadData();
+ Q_INVOKABLE int count() const;
+ void clear();
+
+ bool isEmpty() const;
+
+ Q_INVOKABLE qint64 lastTimeMark() const;
+
+ Q_INVOKABLE bool expanded(int category) const;
+ Q_INVOKABLE void setExpanded(int category, bool expanded);
+ Q_INVOKABLE int categoryDepth(int categoryIndex) const;
+ Q_INVOKABLE int categoryCount() const;
+ Q_INVOKABLE const QString categoryLabel(int categoryIndex) const;
+
+ int findFirstIndex(qint64 startTime) const;
+ int findFirstIndexNoParents(qint64 startTime) const;
+ int findLastIndex(qint64 endTime) const;
+
+ int getEventType(int index) const;
+ Q_INVOKABLE int getEventCategory(int index) const;
+ int getEventRow(int index) const;
+ Q_INVOKABLE qint64 getDuration(int index) const;
+ Q_INVOKABLE qint64 getStartTime(int index) const;
+ Q_INVOKABLE qint64 getEndTime(int index) const;
+ Q_INVOKABLE int getEventId(int index) const;
+ Q_INVOKABLE QColor getColor(int index) const;
+ Q_INVOKABLE float getHeight(int index) const;
+
+ Q_INVOKABLE const QVariantList getLabelsForCategory(int category) const;
+ Q_INVOKABLE const QVariantList getEventDetails(int index) const;
+ Q_INVOKABLE const QVariantMap getEventLocation(int index) const;
+ Q_INVOKABLE int getEventIdForHash(const QString &eventHash) const;
+ Q_INVOKABLE int getEventIdForLocation(const QString &filename, int line, int column) const;
+
+private slots:
+ bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const;
+protected slots:
+ void dataChanged();
+
+private:
+ class PaintEventsModelProxyPrivate;
+ PaintEventsModelProxyPrivate *d;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.cpp b/src/plugins/qmlprofiler/qmlprofilerplugin.cpp
index 87f580ce3d..e4fd95a5a4 100644
--- a/src/plugins/qmlprofiler/qmlprofilerplugin.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerplugin.cpp
@@ -31,8 +31,10 @@
#include "qmlprofilerruncontrolfactory.h"
#include "qmlprofilertool.h"
+#include "abstracttimelinemodel.h"
#include <analyzerbase/analyzermanager.h>
+#include <extensionsystem/pluginmanager.h>
#include <QtPlugin>
@@ -48,6 +50,7 @@ public:
};
bool QmlProfilerPlugin::debugOutput = false;
+QmlProfilerPlugin *QmlProfilerPlugin::instance = 0;
bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorString)
{
@@ -81,15 +84,14 @@ bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorS
AnalyzerManager::addAction(action);
addAutoReleasedObject(new QmlProfilerRunControlFactory());
+ QmlProfilerPlugin::instance = this;
return true;
}
void QmlProfilerPlugin::extensionsInitialized()
{
- // Retrieve objects from the plugin manager's object pool.
- // "In the extensionsInitialized method, a plugin can be sure that all
- // plugins that depend on it are completely initialized."
+ timelineModels = ExtensionSystem::PluginManager::getObjects<AbstractTimelineModel>();
}
ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown()
@@ -100,8 +102,12 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown()
return SynchronousShutdown;
}
+QList<AbstractTimelineModel *> QmlProfilerPlugin::getModels() const
+{
+ return timelineModels;
+}
+
} // namespace Internal
} // namespace QmlProfiler
Q_EXPORT_PLUGIN(QmlProfiler::Internal::QmlProfilerPlugin)
-
diff --git a/src/plugins/qmlprofiler/qmlprofilerplugin.h b/src/plugins/qmlprofiler/qmlprofilerplugin.h
index aa8d15d730..0550fa1eec 100644
--- a/src/plugins/qmlprofiler/qmlprofilerplugin.h
+++ b/src/plugins/qmlprofiler/qmlprofilerplugin.h
@@ -34,6 +34,8 @@
#include <extensionsystem/iplugin.h>
+#include "abstracttimelinemodel.h"
+
namespace QmlProfiler {
namespace Internal {
@@ -50,6 +52,14 @@ public:
ShutdownFlag aboutToShutdown();
static bool debugOutput;
+ static QmlProfilerPlugin *instance;
+
+ QList<AbstractTimelineModel *> getModels() const;
+
+private:
+ QList<AbstractTimelineModel*> timelineModels;
+
+
};
} // namespace Internal
diff --git a/src/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp
new file mode 100644
index 0000000000..2e4331f735
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerprocessedmodel.cpp
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilerprocessedmodel.h"
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <utils/qtcassert.h>
+#include <QUrl>
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+QmlDebug::QmlEventLocation getLocation(const QmlProfilerSimpleModel::QmlEventData &event);
+QString getDisplayName(const QmlProfilerSimpleModel::QmlEventData &event);
+QString getInitialDetails(const QmlProfilerSimpleModel::QmlEventData &event);
+
+QmlDebug::QmlEventLocation getLocation(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ QmlDebug::QmlEventLocation eventLocation = event.location;
+ if ((event.eventType == QmlDebug::Creating || event.eventType == QmlDebug::Compiling)
+ && eventLocation.filename.isEmpty()) {
+ eventLocation.filename = getInitialDetails(event);
+ eventLocation.line = 1;
+ eventLocation.column = 1;
+ }
+ return eventLocation;
+}
+
+QString getDisplayName(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ const QmlDebug::QmlEventLocation eventLocation = getLocation(event);
+ QString displayName;
+
+ // generate hash
+ if (eventLocation.filename.isEmpty()) {
+ displayName = QmlProfilerProcessedModel::tr("<bytecode>");
+ } else {
+ const QString filePath = QUrl(eventLocation.filename).path();
+ displayName = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
+ QString::number(eventLocation.line);
+ }
+
+ return displayName;
+}
+
+QString getInitialDetails(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ QString details;
+ // generate details string
+ if (event.data.isEmpty())
+ details = QmlProfilerProcessedModel::tr("Source code not available.");
+ else {
+ details = event.data.join(QLatin1String(" ")).replace(QLatin1Char('\n'),QLatin1Char(' ')).simplified();
+ QRegExp rewrite(QLatin1String("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
+ bool match = rewrite.exactMatch(details);
+ if (match)
+ details = rewrite.cap(1) + QLatin1String(": ") + rewrite.cap(3);
+ if (details.startsWith(QLatin1String("file://")))
+ details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
+ }
+
+ return details;
+}
+
+
+bool compareStartTimes(const QmlProfilerSimpleModel::QmlEventData &t1, const QmlProfilerSimpleModel::QmlEventData &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerProcessedModel::QmlProfilerProcessedModel(Utils::FileInProjectFinder *fileFinder, QObject *parent)
+ : QmlProfilerSimpleModel(parent)
+ , m_detailsRewriter(new QmlProfilerDetailsRewriter(this, fileFinder))
+ , m_emitChanged(false)
+{
+ connect(m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QString)),
+ this, SLOT(detailsChanged(int,QString)));
+ connect(m_detailsRewriter, SIGNAL(eventDetailsChanged()),
+ this, SLOT(detailsDone()));
+}
+
+QmlProfilerProcessedModel::~QmlProfilerProcessedModel()
+{
+}
+
+void QmlProfilerProcessedModel::clear()
+{
+ m_detailsRewriter->clearRequests();
+ QmlProfilerSimpleModel::clear();
+
+ emit changed();
+ m_emitChanged = false;
+}
+
+void QmlProfilerProcessedModel::complete()
+{
+ // post-processing
+
+ // sort events by start time
+ qSort(eventList.begin(), eventList.end(), compareStartTimes);
+
+ // rewrite strings
+ int n = eventList.count();
+ for (int i = 0; i < n; i++) {
+ QmlEventData *event = &eventList[i];
+ event->location = getLocation(*event);
+ event->displayName = getDisplayName(*event);
+ event->data = QStringList() << getInitialDetails(*event);
+
+ //
+ // request further details from files
+ //
+
+ if (event->eventType != QmlDebug::Binding && event->eventType != QmlDebug::HandlingSignal)
+ continue;
+
+ // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
+ if (event->location.filename.isEmpty())
+ continue;
+
+ // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
+ if (event->location.column == -1)
+ continue;
+
+ m_detailsRewriter->requestDetailsForLocation(i, event->location);
+ }
+
+ m_detailsRewriter->reloadDocuments();
+
+ QmlProfilerSimpleModel::complete();
+ emit changed();
+ m_emitChanged = false;
+}
+
+void QmlProfilerProcessedModel::detailsChanged(int requestId, const QString &newString)
+{
+ QTC_ASSERT(requestId < eventList.count(), return);
+
+ QmlEventData *event = &eventList[requestId];
+ event->data = QStringList(newString);
+
+ m_emitChanged = true;
+}
+
+void QmlProfilerProcessedModel::detailsDone()
+{
+ if (m_emitChanged) {
+ emit changed();
+ m_emitChanged = false;
+ }
+}
+
+}
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilerprocessedmodel.h b/src/plugins/qmlprofiler/qmlprofilerprocessedmodel.h
new file mode 100644
index 0000000000..4c293711f2
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerprocessedmodel.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERPROCESSEDMODEL_H
+#define QMLPROFILERPROCESSEDMODEL_H
+
+#include "qmlprofilersimplemodel.h"
+#include "qmlprofilerdetailsrewriter.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerProcessedModel : public QmlProfilerSimpleModel
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerProcessedModel(Utils::FileInProjectFinder *fileFinder, QObject *parent = 0);
+ ~QmlProfilerProcessedModel();
+
+ virtual void clear();
+ virtual void complete();
+
+private slots:
+ void detailsChanged(int requestId, const QString &newString);
+ void detailsDone();
+
+private:
+ QmlProfilerDetailsRewriter *m_detailsRewriter;
+ bool m_emitChanged;
+};
+
+}
+}
+
+#endif
diff --git a/src/plugins/qmlprofiler/qmlprofilersimplemodel.cpp b/src/plugins/qmlprofiler/qmlprofilersimplemodel.cpp
new file mode 100644
index 0000000000..99e353d59e
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilersimplemodel.cpp
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilersimplemodel.h"
+#include "qmlprofilermodelmanager.h"
+#include <QStringList>
+#include <QVector>
+#include <QDebug>
+#include "qmldebug/qmlprofilereventtypes.h"
+
+namespace QmlProfiler {
+
+QmlProfilerSimpleModel::QmlProfilerSimpleModel(QObject *parent)
+ : QObject(parent)
+{
+ m_modelManager = qobject_cast<QmlProfilerModelManager *>(parent);
+ Q_ASSERT(m_modelManager);
+ m_modelId = m_modelManager->registerModelProxy();
+}
+
+QmlProfilerSimpleModel::~QmlProfilerSimpleModel()
+{
+}
+
+void QmlProfilerSimpleModel::clear()
+{
+ m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1);
+ eventList.clear();
+ emit changed();
+}
+
+bool QmlProfilerSimpleModel::isEmpty() const
+{
+ return eventList.isEmpty();
+}
+
+const QVector<QmlProfilerSimpleModel::QmlEventData> &QmlProfilerSimpleModel::getEvents() const
+{
+ return eventList;
+}
+
+int QmlProfilerSimpleModel::count() const
+{
+ return eventList.count();
+}
+
+void QmlProfilerSimpleModel::addQmlEvent(int type, int bindingType, qint64 startTime, qint64 duration, const QStringList &data, const QmlDebug::QmlEventLocation &location, qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5)
+{
+ QString displayName;
+ if (type == QmlDebug::Painting && bindingType == QmlDebug::AnimationFrame) {
+ displayName = tr("Animations");
+ } else {
+ displayName = QString::fromLatin1("%1:%2").arg(
+ location.filename,
+ QString::number(location.line));
+ }
+
+ QmlEventData eventData = {displayName, type, bindingType, startTime, duration, data, location, ndata1, ndata2, ndata3, ndata4, ndata5};
+ eventList.append(eventData);
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, startTime, m_modelManager->estimatedProfilingTime());
+}
+
+qint64 QmlProfilerSimpleModel::lastTimeMark() const
+{
+ if (eventList.isEmpty())
+ return 0;
+
+ return eventList.last().startTime + eventList.last().duration;
+}
+
+void QmlProfilerSimpleModel::complete()
+{
+ m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1);
+ emit changed();
+}
+
+QString QmlProfilerSimpleModel::getHashString(const QmlProfilerSimpleModel::QmlEventData &event)
+{
+ return QString::fromLatin1("%1:%2:%3:%4:%5").arg(
+ event.location.filename,
+ QString::number(event.location.line),
+ QString::number(event.location.column),
+ QString::number(event.eventType),
+ QString::number(event.bindingType));
+}
+
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilersimplemodel.h b/src/plugins/qmlprofiler/qmlprofilersimplemodel.h
new file mode 100644
index 0000000000..30b0de124c
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilersimplemodel.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERSIMPLEMODEL_H
+#define QMLPROFILERSIMPLEMODEL_H
+
+#include "qmlprofiler_global.h"
+#include <QObject>
+#include <QVector>
+#include <QStringList>
+#include "qmldebug/qmlprofilereventlocation.h"
+
+namespace QmlProfiler {
+
+class QmlProfilerModelManager;
+
+// stores the data from the client as-is
+class QMLPROFILER_EXPORT QmlProfilerSimpleModel : public QObject
+{
+ Q_OBJECT
+public:
+ struct QmlEventData {
+ QString displayName;
+ int eventType;
+ int bindingType;
+ qint64 startTime;
+ qint64 duration;
+ QStringList data;
+ QmlDebug::QmlEventLocation location;
+ qint64 numericData1;
+ qint64 numericData2;
+ qint64 numericData3;
+ qint64 numericData4;
+ qint64 numericData5;
+ };
+
+ explicit QmlProfilerSimpleModel(QObject *parent = 0);
+ ~QmlProfilerSimpleModel();
+
+ virtual void clear();
+ bool isEmpty() const;
+ const QVector<QmlEventData> &getEvents() const;
+ int count() const;
+ void addQmlEvent(int type, int bindingType, qint64 startTime, qint64 duration, const QStringList &data, const QmlDebug::QmlEventLocation &location,
+ qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5);
+ qint64 lastTimeMark() const;
+ virtual void complete();
+
+ static QString getHashString(const QmlProfilerSimpleModel::QmlEventData &event);
+
+signals:
+ void changed();
+
+protected:
+ QVector<QmlEventData> eventList;
+ QmlProfilerModelManager *m_modelManager;
+ int m_modelId;
+};
+
+}
+
+#endif
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp b/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
index 25ec6c5789..953ed9852f 100644
--- a/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerstatemanager.cpp
@@ -38,8 +38,7 @@
namespace QmlProfiler {
namespace Internal {
-static QString stringForState(int state)
-{
+inline QString stringForState(int state) {
switch (state) {
case QmlProfilerStateManager::Idle: return QLatin1String("Idle");
case QmlProfilerStateManager::AppStarting: return QLatin1String("AppStarting");
@@ -54,79 +53,92 @@ static QString stringForState(int state)
return QString();
}
+class QmlProfilerStateManager::QmlProfilerStateManagerPrivate
+{
+public:
+ QmlProfilerStateManagerPrivate(QmlProfilerStateManager *qq) : q(qq) {}
+ ~QmlProfilerStateManagerPrivate() {}
+
+ QmlProfilerStateManager *q;
+
+ QmlProfilerStateManager::QmlProfilerState m_currentState;
+ bool m_clientRecording;
+ bool m_serverRecording;
+};
QmlProfilerStateManager::QmlProfilerStateManager(QObject *parent) :
- QObject(parent)
+ QObject(parent),d(new QmlProfilerStateManagerPrivate(this))
{
- m_currentState = Idle;
- m_clientRecording = true;
- m_serverRecording = false;
+ d->m_currentState = Idle;
+ d->m_clientRecording = true;
+ d->m_serverRecording = false;
}
QmlProfilerStateManager::~QmlProfilerStateManager()
{
+ delete d;
}
-QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState() const
+QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState()
{
- return m_currentState;
+ return d->m_currentState;
}
-bool QmlProfilerStateManager::clientRecording() const
+bool QmlProfilerStateManager::clientRecording()
{
- return m_clientRecording;
+ return d->m_clientRecording;
}
-bool QmlProfilerStateManager::serverRecording() const
+bool QmlProfilerStateManager::serverRecording()
{
- return m_serverRecording;
+ return d->m_serverRecording;
}
-QString QmlProfilerStateManager::currentStateAsString() const
+QString QmlProfilerStateManager::currentStateAsString()
{
- return stringForState(m_currentState);
+ return stringForState(d->m_currentState);
}
void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
{
#ifdef _DEBUG_PROFILERSTATE_
- qDebug() << "Profiler state change request from" << currentStateAsString() << "to" << stringForState(newState);
+ qDebug() << "Profiler state change request from" << stringForState(d->m_currentState) << "to" << stringForState(newState);
#endif
- QTC_ASSERT(m_currentState != newState, /**/);
+ QTC_ASSERT(d->m_currentState != newState, /**/);
switch (newState) {
case Idle:
- QTC_ASSERT(m_currentState == AppStarting ||
- m_currentState == AppStopped ||
- m_currentState == AppKilled,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppStarting ||
+ d->m_currentState == AppStopped ||
+ d->m_currentState == AppKilled,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppStarting:
- QTC_ASSERT(m_currentState == Idle,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == Idle,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppRunning:
- QTC_ASSERT(m_currentState == AppStarting,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppStarting,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppStopRequested:
- QTC_ASSERT(m_currentState == AppRunning,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppRunning,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppReadyToStop:
- QTC_ASSERT(m_currentState == AppStopRequested,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppStopRequested,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppStopped:
- QTC_ASSERT(m_currentState == AppReadyToStop ||
- m_currentState == AppDying,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppReadyToStop ||
+ d->m_currentState == AppDying,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppDying:
- QTC_ASSERT(m_currentState == AppRunning,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppRunning,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
case AppKilled:
- QTC_ASSERT(m_currentState == AppDying,
- qDebug() << "from" << currentStateAsString());
+ QTC_ASSERT(d->m_currentState == AppDying,
+ qDebug() << "from" << stringForState(d->m_currentState));
break;
default: {
const QString message = QString::fromLatin1("Switching to unknown state in %1:%2").arg(QString::fromLatin1(__FILE__), QString::number(__LINE__));
@@ -135,17 +147,17 @@ void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
break;
}
- m_currentState = newState;
+ d->m_currentState = newState;
emit stateChanged();
}
void QmlProfilerStateManager::setClientRecording(bool recording)
{
#ifdef _DEBUG_PROFILERSTATE_
- qDebug() << "Setting client recording flag from" << m_serverRecording << "to" << recording;
+ qDebug() << "Setting client recording flag from" << d->m_serverRecording << "to" << recording;
#endif
- if (m_clientRecording != recording) {
- m_clientRecording = recording;
+ if (d->m_clientRecording != recording) {
+ d->m_clientRecording = recording;
emit clientRecordingChanged();
}
}
@@ -153,13 +165,13 @@ void QmlProfilerStateManager::setClientRecording(bool recording)
void QmlProfilerStateManager::setServerRecording(bool recording)
{
#ifdef _DEBUG_PROFILERSTATE_
- qDebug() << "Setting server recording flag from" << m_serverRecording << "to" << recording;
+ qDebug() << "Setting server recording flag from" << d->m_serverRecording << "to" << recording;
#endif
- if (m_serverRecording != recording) {
- m_serverRecording = recording;
+ if (d->m_serverRecording != recording) {
+ d->m_serverRecording = recording;
emit serverRecordingChanged();
}
}
-} // namespace Internal
-} // namespace QmlProfiler
+}
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatemanager.h b/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
index 250c3325c3..be93fd4d32 100644
--- a/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
+++ b/src/plugins/qmlprofiler/qmlprofilerstatemanager.h
@@ -38,7 +38,6 @@ namespace Internal {
class QmlProfilerStateManager : public QObject
{
Q_OBJECT
-
public:
enum QmlProfilerState {
Idle,
@@ -54,11 +53,11 @@ public:
explicit QmlProfilerStateManager(QObject *parent = 0);
~QmlProfilerStateManager();
- QmlProfilerState currentState() const;
- bool clientRecording() const;
- bool serverRecording() const;
+ QmlProfilerState currentState();
+ bool clientRecording();
+ bool serverRecording();
- QString currentStateAsString() const;
+ QString currentStateAsString();
signals:
void stateChanged();
@@ -71,12 +70,11 @@ public slots:
void setServerRecording(bool recording);
private:
- QmlProfilerStateManager::QmlProfilerState m_currentState;
- bool m_clientRecording;
- bool m_serverRecording;
+ class QmlProfilerStateManagerPrivate;
+ QmlProfilerStateManagerPrivate *d;
};
-} // namespace Internal
-} // namespace QmlProfiler
+}
+}
#endif // QMLPROFILERSTATEMANAGER_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp b/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
index cf46cbeeb1..193f4f7569 100644
--- a/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerstatewidget.cpp
@@ -35,6 +35,7 @@
#include <QLabel>
#include <QProgressBar>
#include <QTime>
+#include <QDebug>
namespace QmlProfiler {
namespace Internal {
@@ -49,7 +50,7 @@ class QmlProfilerStateWidget::QmlProfilerStateWidgetPrivate
QPixmap shadowPic;
QmlProfilerStateManager *m_profilerState;
- QmlProfilerDataModel *m_profilerDataModel;
+ QmlProfilerModelManager *m_modelManager;
bool isRecording;
bool appKilled;
@@ -60,11 +61,9 @@ class QmlProfilerStateWidget::QmlProfilerStateWidgetPrivate
qint64 estimatedProfilingTime;
};
-
-
QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateManager,
- QmlProfilerDataModel *dataModel, QWidget *parent) :
- QWidget(parent), d(new QmlProfilerStateWidgetPrivate(this))
+ QmlProfilerModelManager *modelManager, QWidget *parent)
+ : QWidget(parent), d(new QmlProfilerStateWidgetPrivate(this))
{
setObjectName(QLatin1String("QML Profiler State Display"));
@@ -80,6 +79,7 @@ QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateMan
d->progressBar = new QProgressBar(this);
layout->addWidget(d->progressBar);
+ d->progressBar->setMaximum(1000);
d->progressBar->setVisible(false);
setLayout(layout);
@@ -92,9 +92,11 @@ QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateMan
d->emptyList = true;
// profiler state
- d->m_profilerDataModel = dataModel;
- connect(d->m_profilerDataModel,SIGNAL(stateChanged()), this, SLOT(dataStateChanged()));
- connect(d->m_profilerDataModel,SIGNAL(countChanged()), this, SLOT(dataStateChanged()));
+ d->m_modelManager = modelManager;
+ connect(d->m_modelManager,SIGNAL(stateChanged()), this, SLOT(dataStateChanged()));
+ connect(d->m_modelManager,SIGNAL(countChanged()), this, SLOT(dataStateChanged()));
+ connect(d->m_modelManager,SIGNAL(progressChanged()), this, SLOT(dataStateChanged()));
+ connect(this, SIGNAL(newTimeEstimation(qint64)), d->m_modelManager, SLOT(newTimeEstimation(qint64)));
d->m_profilerState = stateManager;
connect(d->m_profilerState,SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
connect(d->m_profilerState, SIGNAL(serverRecordingChanged()),
@@ -196,9 +198,9 @@ void QmlProfilerStateWidget::updateDisplay()
if (d->isRecording) {
d->isRecording = false;
d->estimatedProfilingTime = d->profilingTimer.elapsed();
+ emit newTimeEstimation(d->estimatedProfilingTime);
}
- d->progressBar->setMaximum(d->estimatedProfilingTime);
- d->progressBar->setValue(d->m_profilerDataModel->lastTimeMark() * 1e-6);
+ d->progressBar->setValue(d->m_modelManager->progress() * 1000);
d->progressBar->setVisible(true);
resize(300,70);
reposition();
@@ -232,9 +234,9 @@ void QmlProfilerStateWidget::updateDisplay()
if (d->isRecording) {
d->isRecording = false;
d->estimatedProfilingTime = d->profilingTimer.elapsed();
+ emit newTimeEstimation(d->estimatedProfilingTime);
}
- d->progressBar->setMaximum(d->estimatedProfilingTime);
- d->progressBar->setValue(d->m_profilerDataModel->lastTimeMark() * 1e-6);
+ d->progressBar->setValue(d->m_modelManager->progress() * 1000);
d->progressBar->setVisible(true);
resize(300,70);
reposition();
@@ -252,15 +254,17 @@ void QmlProfilerStateWidget::updateDisplay()
// }
// There is a trace on view, hide this dialog
+ d->progressBar->setVisible(false);
setVisible(false);
}
void QmlProfilerStateWidget::dataStateChanged()
{
- d->loadingDone = d->m_profilerDataModel->currentState() == QmlProfilerDataModel::Done ||
- d->m_profilerDataModel->currentState() == QmlProfilerDataModel::Empty;
- d->traceAvailable = d->m_profilerDataModel->traceDuration() > 0;
- d->emptyList = d->m_profilerDataModel->count() == 0;
+ // consider possible rounding errors
+ d->loadingDone = d->m_modelManager->progress() >= 0.99 ||
+ d->m_modelManager->state() == QmlProfilerDataState::Empty;
+ d->traceAvailable = d->m_modelManager->traceTime()->duration() > 0;
+ d->emptyList = d->m_modelManager->isEmpty() || d->m_modelManager->progress() == 0;
updateDisplay();
}
@@ -275,8 +279,11 @@ void QmlProfilerStateWidget::profilerStateChanged()
d->isRecording = d->m_profilerState->serverRecording();
if (d->isRecording)
d->profilingTimer.start();
- else
- d->estimatedProfilingTime = d->profilingTimer.elapsed();
+ else {
+ // estimated time in ns
+ d->estimatedProfilingTime = d->profilingTimer.elapsed() * 1e6;
+ emit newTimeEstimation(d->estimatedProfilingTime);
+ }
updateDisplay();
}
diff --git a/src/plugins/qmlprofiler/qmlprofilerstatewidget.h b/src/plugins/qmlprofiler/qmlprofilerstatewidget.h
index 57c27f78de..bf32aa2b47 100644
--- a/src/plugins/qmlprofiler/qmlprofilerstatewidget.h
+++ b/src/plugins/qmlprofiler/qmlprofilerstatewidget.h
@@ -33,7 +33,7 @@
#include <QWidget>
#include "qmlprofilerstatemanager.h"
-#include "qmlprofilerdatamodel.h"
+#include "qmlprofilermodelmanager.h"
namespace QmlProfiler {
namespace Internal {
@@ -43,7 +43,7 @@ class QmlProfilerStateWidget : public QWidget
Q_OBJECT
public:
explicit QmlProfilerStateWidget(QmlProfilerStateManager *stateManager,
- QmlProfilerDataModel *dataModel, QWidget *parent = 0);
+ QmlProfilerModelManager *modelManager, QWidget *parent = 0);
~QmlProfilerStateWidget();
private slots:
@@ -52,6 +52,9 @@ private slots:
void profilerStateChanged();
void reposition();
+signals:
+ void newTimeEstimation(qint64);
+
protected:
void paintEvent(QPaintEvent *event);
diff --git a/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp
new file mode 100644
index 0000000000..e6a280aadd
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.cpp
@@ -0,0 +1,721 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertimelinemodelproxy.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilersimplemodel.h"
+
+#include <QCoreApplication>
+#include <QVector>
+#include <QHash>
+#include <QUrl>
+#include <QString>
+#include <QStack>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct CategorySpan {
+ bool expanded;
+ int expandedRows;
+ int contractedRows;
+ int rowStart;
+ bool empty;
+};
+
+class BasicTimelineModel::BasicTimelineModelPrivate
+{
+public:
+ BasicTimelineModelPrivate(BasicTimelineModel *qq) : q(qq) {}
+ ~BasicTimelineModelPrivate() {}
+
+ // convenience functions
+ void prepare();
+ void computeNestingContracted();
+ void computeExpandedLevels();
+ void buildEndTimeList();
+ void findBindingLoops();
+ void computeRowStarts();
+
+ QString displayTime(double time);
+
+ QVector <BasicTimelineModel::QmlRangeEventData> eventDict;
+ QVector <QString> eventHashes;
+ QVector <BasicTimelineModel::QmlRangeEventStartInstance> startTimeData;
+ QVector <BasicTimelineModel::QmlRangeEventEndInstance> endTimeData;
+ QVector <CategorySpan> categorySpan;
+
+ BasicTimelineModel *q;
+};
+
+BasicTimelineModel::BasicTimelineModel(QObject *parent)
+ : AbstractTimelineModel(parent), d(new BasicTimelineModelPrivate(this))
+{
+}
+
+BasicTimelineModel::~BasicTimelineModel()
+{
+ delete d;
+}
+
+int BasicTimelineModel::categories() const
+{
+ return categoryCount();
+}
+
+QStringList BasicTimelineModel::categoryTitles() const
+{
+ QStringList retString;
+ for (int i=0; i<categories(); i++)
+ retString << categoryLabel(i);
+ return retString;
+}
+
+QString BasicTimelineModel::name() const
+{
+ return QLatin1String("BasicTimelineModel");
+}
+
+const QVector<BasicTimelineModel::QmlRangeEventStartInstance> BasicTimelineModel::getData() const
+{
+ return d->startTimeData;
+}
+
+const QVector<BasicTimelineModel::QmlRangeEventStartInstance> BasicTimelineModel::getData(qint64 fromTime, qint64 toTime) const
+{
+ int fromIndex = findFirstIndex(fromTime);
+ int toIndex = findLastIndex(toTime);
+ if (fromIndex != -1 && toIndex > fromIndex)
+ return d->startTimeData.mid(fromIndex, toIndex - fromIndex + 1);
+ else
+ return QVector<BasicTimelineModel::QmlRangeEventStartInstance>();
+}
+
+void BasicTimelineModel::clear()
+{
+ d->eventDict.clear();
+ d->eventHashes.clear();
+ d->startTimeData.clear();
+ d->endTimeData.clear();
+ d->categorySpan.clear();
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1);
+}
+
+void BasicTimelineModel::dataChanged()
+{
+ if (m_modelManager->state() == QmlProfilerDataState::ProcessingData)
+ loadData();
+
+ if (m_modelManager->state() == QmlProfilerDataState::Empty)
+ clear();
+
+ emit stateChanged();
+ emit dataAvailable();
+ emit emptyChanged();
+ emit expandedChanged();
+}
+
+void BasicTimelineModel::BasicTimelineModelPrivate::prepare()
+{
+ categorySpan.clear();
+ for (int i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
+ CategorySpan newCategory = {false, 1, 1, i, true};
+ categorySpan << newCategory;
+ }
+}
+
+bool compareStartTimes(const BasicTimelineModel::QmlRangeEventStartInstance&t1, const BasicTimelineModel::QmlRangeEventStartInstance &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+bool compareEndTimes(const BasicTimelineModel::QmlRangeEventEndInstance &t1, const BasicTimelineModel::QmlRangeEventEndInstance &t2)
+{
+ return t1.endTime < t2.endTime;
+}
+
+bool BasicTimelineModel::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const
+{
+ // only accept Qt4.x Painting events
+ if (event.eventType == QmlDebug::Painting)
+ return event.bindingType == QmlDebug::QPainterEvent;
+
+ return (event.eventType <= QmlDebug::HandlingSignal);
+}
+
+void BasicTimelineModel::loadData()
+{
+ clear();
+ QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
+ if (simpleModel->isEmpty())
+ return;
+
+ int lastEventId = 0;
+
+ d->prepare();
+
+ // collect events
+ const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
+ if (!eventAccepted(event))
+ continue;
+
+ QString eventHash = QmlProfilerSimpleModel::getHashString(event);
+
+ // store in dictionary
+ if (!d->eventHashes.contains(eventHash)) {
+ QmlRangeEventData rangeEventData = {
+ event.displayName,
+ event.data.join(QLatin1String(" ")),
+ event.location,
+ (QmlDebug::QmlEventType)event.eventType,
+ lastEventId++ // event id
+ };
+ d->eventDict << rangeEventData;
+ d->eventHashes << eventHash;
+ }
+
+ // store starttime-based instance
+ QmlRangeEventStartInstance eventStartInstance = {
+ event.startTime,
+ event.duration,
+ d->eventHashes.indexOf(eventHash), // event id
+ QmlDebug::Constants::QML_MIN_LEVEL, // displayRowExpanded;
+ QmlDebug::Constants::QML_MIN_LEVEL, // displayRowCollapsed;
+ 1,
+ -1 // bindingLoopHead
+ };
+ d->startTimeData.append(eventStartInstance);
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, d->startTimeData.count(), eventList.count() * 7);
+ }
+
+ qSort(d->startTimeData.begin(), d->startTimeData.end(), compareStartTimes);
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 2, 7);
+
+ // compute nestingLevel - nonexpanded
+ d->computeNestingContracted();
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 3, 7);
+
+ // compute nestingLevel - expanded
+ d->computeExpandedLevels();
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 4, 7);
+
+ // populate endtimelist
+ d->buildEndTimeList();
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 5, 7);
+
+ d->findBindingLoops();
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 6, 7);
+
+ d->computeRowStarts();
+
+ m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1);
+
+ emit countChanged();
+}
+
+void BasicTimelineModel::BasicTimelineModelPrivate::computeNestingContracted()
+{
+ int i;
+ int eventCount = startTimeData.count();
+
+ QHash<int, qint64> endtimesPerLevel;
+ QList<int> nestingLevels;
+ QList< QHash<int, qint64> > endtimesPerNestingLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[QmlDebug::Constants::QML_MIN_LEVEL] = 0;
+ int lastBaseEventIndex = 0;
+ qint64 lastBaseEventEndTime = q->m_modelManager->traceTime()->startTime();
+
+ for (i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
+ nestingLevels << QmlDebug::Constants::QML_MIN_LEVEL;
+ QHash<int, qint64> dummyHash;
+ dummyHash[QmlDebug::Constants::QML_MIN_LEVEL] = 0;
+ endtimesPerNestingLevel << dummyHash;
+ }
+
+ for (i = 0; i < eventCount; i++) {
+ qint64 st = startTimeData[i].startTime;
+ int type = q->getEventType(i);
+
+ // general level
+ if (endtimesPerLevel[level] > st) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st)
+ level--;
+ }
+ endtimesPerLevel[level] = st + startTimeData[i].duration;
+
+ // per type
+ if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
+ nestingLevels[type]++;
+ } else {
+ while (nestingLevels[type] > QmlDebug::Constants::QML_MIN_LEVEL &&
+ endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
+ nestingLevels[type]--;
+ }
+ endtimesPerNestingLevel[type][nestingLevels[type]] =
+ st + startTimeData[i].duration;
+
+ startTimeData[i].displayRowCollapsed = nestingLevels[type];
+
+ if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
+ if (lastBaseEventEndTime < startTimeData[i].startTime) {
+ lastBaseEventIndex = i;
+ lastBaseEventEndTime = startTimeData[i].startTime + startTimeData[i].duration;
+ }
+ }
+ startTimeData[i].baseEventIndex = lastBaseEventIndex;
+ }
+
+ // nestingdepth
+ for (i = 0; i < eventCount; i++) {
+ int eventType = q->getEventType(i);
+ categorySpan[eventType].empty = false;
+ if (categorySpan[eventType].contractedRows <= startTimeData[i].displayRowCollapsed)
+ categorySpan[eventType].contractedRows = startTimeData[i].displayRowCollapsed + 1;
+ }
+}
+
+void BasicTimelineModel::BasicTimelineModelPrivate::computeExpandedLevels()
+{
+ QHash<int, int> eventRow;
+ int eventCount = startTimeData.count();
+ for (int i = 0; i < eventCount; i++) {
+ int eventId = startTimeData[i].eventId;
+ int eventType = eventDict[eventId].eventType;
+ if (!eventRow.contains(eventId)) {
+ categorySpan[eventType].empty = false;
+ eventRow[eventId] = categorySpan[eventType].expandedRows++;
+ }
+ startTimeData[i].displayRowExpanded = eventRow[eventId];
+ }
+}
+
+void BasicTimelineModel::BasicTimelineModelPrivate::buildEndTimeList()
+{
+ endTimeData.clear();
+
+ int eventCount = startTimeData.count();
+ for (int i = 0; i < eventCount; i++) {
+ BasicTimelineModel::QmlRangeEventEndInstance endInstance = {
+ i,
+ startTimeData[i].startTime + startTimeData[i].duration
+ };
+
+ endTimeData << endInstance;
+ }
+
+ qSort(endTimeData.begin(), endTimeData.end(), compareEndTimes);
+}
+
+void BasicTimelineModel::BasicTimelineModelPrivate::findBindingLoops()
+{
+ typedef QPair<QString, int> CallStackEntry;
+ QStack<CallStackEntry> callStack;
+
+ for (int i = 0; i < startTimeData.size(); ++i) {
+ QmlRangeEventStartInstance *event = &startTimeData[i];
+
+ BasicTimelineModel::QmlRangeEventData data = eventDict.at(event->eventId);
+
+ static QVector<QmlDebug::QmlEventType> acceptedTypes =
+ QVector<QmlDebug::QmlEventType>() << QmlDebug::Compiling << QmlDebug::Creating
+ << QmlDebug::Binding << QmlDebug::HandlingSignal;
+
+ if (!acceptedTypes.contains(data.eventType))
+ continue;
+
+ const QString eventHash = eventHashes.at(event->eventId);
+ const QmlRangeEventStartInstance *potentialParent = callStack.isEmpty()
+ ? 0 : &startTimeData[callStack.top().second];
+
+ while (potentialParent
+ && !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
+ callStack.pop();
+ potentialParent = callStack.isEmpty() ? 0
+ : &startTimeData[callStack.top().second];
+ }
+
+ // check whether event is already in stack
+ for (int ii = 0; ii < callStack.size(); ++ii) {
+ if (callStack.at(ii).first == eventHash) {
+ event->bindingLoopHead = callStack.at(ii).second;
+ break;
+ }
+ }
+
+
+ CallStackEntry newEntry(eventHash, i);
+ callStack.push(newEntry);
+ }
+
+}
+
+void BasicTimelineModel::BasicTimelineModelPrivate::computeRowStarts()
+{
+ int rowStart = 0;
+ for (int i = 0; i < categorySpan.count(); i++) {
+ categorySpan[i].rowStart = rowStart;
+ rowStart += q->categoryDepth(i);
+ }
+}
+
+/////////////////// QML interface
+
+bool BasicTimelineModel::isEmpty() const
+{
+ return count() == 0;
+}
+
+int BasicTimelineModel::count() const
+{
+ return d->startTimeData.count();
+}
+
+qint64 BasicTimelineModel::lastTimeMark() const
+{
+ return d->startTimeData.last().startTime + d->startTimeData.last().duration;
+}
+
+bool BasicTimelineModel::expanded(int category) const
+{
+ if (d->categorySpan.count() <= category)
+ return false;
+ return d->categorySpan[category].expanded;
+}
+
+void BasicTimelineModel::setExpanded(int category, bool expanded)
+{
+ if (d->categorySpan.count() <= category)
+ return;
+
+ d->categorySpan[category].expanded = expanded;
+ d->computeRowStarts();
+ emit expandedChanged();
+}
+
+int BasicTimelineModel::categoryDepth(int categoryIndex) const
+{
+ if (d->categorySpan.count() <= categoryIndex)
+ return 1;
+ // special for paint events: show only when empty model or there's actual events
+ if (categoryIndex == QmlDebug::Painting && d->categorySpan[categoryIndex].empty && !isEmpty())
+ return 0;
+ if (d->categorySpan[categoryIndex].expanded)
+ return d->categorySpan[categoryIndex].expandedRows;
+ else
+ return d->categorySpan[categoryIndex].contractedRows;
+}
+
+int BasicTimelineModel::categoryCount() const
+{
+ return 5;
+}
+
+const QString BasicTimelineModel::categoryLabel(int categoryIndex) const
+{
+ switch (categoryIndex) {
+ case 0: return QCoreApplication::translate("MainView", "Painting"); break;
+ case 1: return QCoreApplication::translate("MainView", "Compiling"); break;
+ case 2: return QCoreApplication::translate("MainView", "Creating"); break;
+ case 3: return QCoreApplication::translate("MainView", "Binding"); break;
+ case 4: return QCoreApplication::translate("MainView", "Handling Signal"); break;
+ default: return QString();
+ }
+}
+
+
+int BasicTimelineModel::findFirstIndex(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endTimeData.isEmpty())
+ return -1;
+ if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endTimeData.last().endTime <= startTime)
+ return -1;
+
+ if (candidate == -1)
+ {
+ int fromIndex = 0;
+ int toIndex = d->endTimeData.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endTimeData[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int eventIndex = d->endTimeData[candidate].startTimeIndex;
+ return d->startTimeData[eventIndex].baseEventIndex;
+
+}
+
+int BasicTimelineModel::findFirstIndexNoParents(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->endTimeData.isEmpty())
+ return -1;
+ if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->endTimeData.last().endTime <= startTime)
+ return -1;
+
+ if (candidate == -1) {
+ int fromIndex = 0;
+ int toIndex = d->endTimeData.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->endTimeData[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int ndx = d->endTimeData[candidate].startTimeIndex;
+
+ return ndx;
+}
+
+int BasicTimelineModel::findLastIndex(qint64 endTime) const
+{
+ // in the "starttime" list, find the last event that starts before endtime
+ if (d->startTimeData.isEmpty())
+ return -1;
+ if (d->startTimeData.first().startTime >= endTime)
+ return -1;
+ if (d->startTimeData.count() == 1)
+ return 0;
+ if (d->startTimeData.last().startTime <= endTime)
+ return d->startTimeData.count()-1;
+
+ int fromIndex = 0;
+ int toIndex = d->startTimeData.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->startTimeData[midIndex].startTime < endTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ return fromIndex;
+}
+
+int BasicTimelineModel::getEventType(int index) const
+{
+ return d->eventDict[d->startTimeData[index].eventId].eventType;
+}
+
+int BasicTimelineModel::getEventCategory(int index) const
+{
+ int evTy = getEventType(index);
+ // special: paint events shown?
+ if (d->categorySpan[0].empty && !isEmpty())
+ return evTy - 1;
+ return evTy;
+}
+
+int BasicTimelineModel::getEventRow(int index) const
+{
+ if (d->categorySpan[getEventType(index)].expanded)
+ return d->startTimeData[index].displayRowExpanded + d->categorySpan[getEventType(index)].rowStart;
+ else
+ return d->startTimeData[index].displayRowCollapsed + d->categorySpan[getEventType(index)].rowStart;
+}
+
+qint64 BasicTimelineModel::getDuration(int index) const
+{
+ return d->startTimeData[index].duration;
+}
+
+qint64 BasicTimelineModel::getStartTime(int index) const
+{
+ return d->startTimeData[index].startTime;
+}
+
+qint64 BasicTimelineModel::getEndTime(int index) const
+{
+ return d->startTimeData[index].startTime + d->startTimeData[index].duration;
+}
+
+int BasicTimelineModel::getEventId(int index) const
+{
+ return d->startTimeData[index].eventId;
+}
+
+int BasicTimelineModel::getBindingLoopDest(int index) const
+{
+ return d->startTimeData[index].bindingLoopHead;
+}
+
+QColor BasicTimelineModel::getColor(int index) const
+{
+ int ndx = getEventId(index);
+ return QColor::fromHsl((ndx*25)%360, 76, 166);
+}
+
+float BasicTimelineModel::getHeight(int index) const
+{
+ Q_UNUSED(index);
+ // 100% height for regular events
+ return 1.0f;
+}
+
+const QVariantList BasicTimelineModel::getLabelsForCategory(int category) const
+{
+ QVariantList result;
+
+ if (d->categorySpan.count() > category && d->categorySpan[category].expanded) {
+ int eventCount = d->eventDict.count();
+ for (int i = 0; i < eventCount; i++) {
+ if (d->eventDict[i].eventType == category) {
+ QVariantMap element;
+ element.insert(QLatin1String("displayName"), QVariant(d->eventDict[i].displayName));
+ element.insert(QLatin1String("description"), QVariant(d->eventDict[i].details));
+ element.insert(QLatin1String("id"), QVariant(d->eventDict[i].eventId));
+ result << element;
+ }
+ }
+ }
+
+ return result;
+}
+
+QString BasicTimelineModel::BasicTimelineModelPrivate::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+const QVariantList BasicTimelineModel::getEventDetails(int index) const
+{
+ QVariantList result;
+ int eventId = getEventId(index);
+
+ static const char trContext[] = "RangeDetails";
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QLatin1String("title"), QVariant(categoryLabel(d->eventDict[eventId].eventType)));
+ result << valuePair;
+ }
+
+ // duration
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->startTimeData[index].duration)));
+ result << valuePair;
+ }
+
+ // details
+ {
+ QVariantMap valuePair;
+ QString detailsString = d->eventDict[eventId].details;
+ if (detailsString.length() > 40)
+ detailsString = detailsString.left(40) + QLatin1String("...");
+ valuePair.insert(QCoreApplication::translate(trContext, "Details:"), QVariant(detailsString));
+ result << valuePair;
+ }
+
+ // location
+ {
+ QVariantMap valuePair;
+ valuePair.insert(QCoreApplication::translate(trContext, "Location:"), QVariant(d->eventDict[eventId].displayName));
+ result << valuePair;
+ }
+
+ // isbindingloop
+ {}
+
+
+ return result;
+}
+
+const QVariantMap BasicTimelineModel::getEventLocation(int index) const
+{
+ QVariantMap result;
+ int eventId = getEventId(index);
+
+ QmlDebug::QmlEventLocation location
+ = d->eventDict.at(eventId).location;
+
+ result.insert(QLatin1String("file"), location.filename);
+ result.insert(QLatin1String("line"), location.line);
+ result.insert(QLatin1String("column"), location.column);
+
+ return result;
+}
+
+int BasicTimelineModel::getEventIdForHash(const QString &eventHash) const
+{
+ return d->eventHashes.indexOf(eventHash);
+}
+
+int BasicTimelineModel::getEventIdForLocation(const QString &filename, int line, int column) const
+{
+ // if this is called from v8 view, we don't have the column number, it will be -1
+ foreach (const QmlRangeEventData &eventData, d->eventDict) {
+ if (eventData.location.filename == filename &&
+ eventData.location.line == line &&
+ (column == -1 || eventData.location.column == column))
+ return eventData.eventId;
+ }
+ return -1;
+}
+
+
+
+}
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h
new file mode 100644
index 0000000000..3ffcf707ff
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertimelinemodelproxy.h
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+
+#ifndef QMLPROFILERTIMELINEMODELPROXY_H
+#define QMLPROFILERTIMELINEMODELPROXY_H
+
+#include <QObject>
+#include "abstracttimelinemodel.h"
+#include <qmldebug/qmlprofilereventtypes.h>
+#include <qmldebug/qmlprofilereventlocation.h>
+//#include <QHash>
+//#include <QVector>
+#include <QVariantList>
+//#include <QVariantMap>
+#include "qmlprofilersimplemodel.h"
+#include <QColor>
+
+
+namespace QmlProfiler {
+class QmlProfilerModelManager;
+
+namespace Internal {
+
+class BasicTimelineModel : public AbstractTimelineModel
+{
+// Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
+
+ Q_OBJECT
+public:
+ struct QmlRangeEventData
+ {
+ QString displayName;
+ QString details;
+ QmlDebug::QmlEventLocation location;
+ QmlDebug::QmlEventType eventType;
+
+ int eventId; // separate
+ };
+
+ struct QmlRangeEventStartInstance {
+ qint64 startTime;
+ qint64 duration;
+ int eventId;
+
+ // not-expanded, per type
+ int displayRowExpanded;
+ int displayRowCollapsed;
+ int baseEventIndex; // used by findfirstindex
+ int bindingLoopHead;
+ };
+
+ struct QmlRangeEventEndInstance {
+ int startTimeIndex;
+ qint64 endTime;
+ };
+
+ BasicTimelineModel(QObject *parent = 0);
+ ~BasicTimelineModel();
+
+
+ int categories() const;
+ QStringList categoryTitles() const;
+ QString name() const;
+
+ const QVector<QmlRangeEventStartInstance> getData() const;
+ const QVector<QmlRangeEventStartInstance> getData(qint64 fromTime, qint64 toTime) const;
+ void loadData();
+ Q_INVOKABLE int count() const;
+ void clear();
+
+
+// QML interface
+ bool isEmpty() const;
+
+ Q_INVOKABLE qint64 lastTimeMark() const;
+
+ Q_INVOKABLE bool expanded(int category) const;
+ Q_INVOKABLE void setExpanded(int category, bool expanded);
+ Q_INVOKABLE int categoryDepth(int categoryIndex) const;
+ Q_INVOKABLE int categoryCount() const;
+ Q_INVOKABLE const QString categoryLabel(int categoryIndex) const;
+
+ int findFirstIndex(qint64 startTime) const;
+ int findFirstIndexNoParents(qint64 startTime) const;
+ int findLastIndex(qint64 endTime) const;
+
+ int getEventType(int index) const;
+ int getEventCategory(int index) const;
+ int getEventRow(int index) const;
+ Q_INVOKABLE qint64 getDuration(int index) const;
+ Q_INVOKABLE qint64 getStartTime(int index) const;
+ Q_INVOKABLE qint64 getEndTime(int index) const;
+ Q_INVOKABLE int getEventId(int index) const;
+ int getBindingLoopDest(int index) const;
+ Q_INVOKABLE QColor getColor(int index) const;
+ Q_INVOKABLE float getHeight(int index) const;
+
+ Q_INVOKABLE const QVariantList getLabelsForCategory(int category) const;
+ Q_INVOKABLE const QVariantList getEventDetails(int index) const;
+ Q_INVOKABLE const QVariantMap getEventLocation(int index) const;
+
+ Q_INVOKABLE int getEventIdForHash(const QString &eventHash) const;
+ Q_INVOKABLE int getEventIdForLocation(const QString &filename, int line, int column) const;
+
+private slots:
+ bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const;
+protected slots:
+ void dataChanged();
+
+private:
+ class BasicTimelineModelPrivate;
+ BasicTimelineModelPrivate *d;
+
+};
+
+}
+}
+
+#endif
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 2064db5763..ed9235d623 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -34,7 +34,7 @@
#include "qmlprofilerattachdialog.h"
#include "qmlprofilerviewmanager.h"
#include "qmlprofilerclientmanager.h"
-#include "qmlprofilerdatamodel.h"
+#include "qmlprofilermodelmanager.h"
#include "qmlprofilerdetailsrewriter.h"
#include "timelinerenderer.h"
@@ -98,8 +98,7 @@ class QmlProfilerTool::QmlProfilerToolPrivate
public:
QmlProfilerStateManager *m_profilerState;
QmlProfilerClientManager *m_profilerConnections;
- QmlProfilerDataModel *m_profilerDataModel;
- QmlProfilerDetailsRewriter *m_detailsRewriter;
+ QmlProfilerModelManager *m_profilerModelManager;
QmlProfilerViewManager *m_viewContainer;
Utils::FileInProjectFinder m_projectFinder;
@@ -138,31 +137,11 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState);
connect(d->m_profilerConnections, SIGNAL(connectionClosed()), this, SLOT(clientsDisconnected()));
- d->m_profilerDataModel = new QmlProfilerDataModel(this);
- connect(d->m_profilerDataModel, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
- connect(d->m_profilerDataModel, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
- connect(d->m_profilerConnections,
- SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
- d->m_profilerDataModel,
- SLOT(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
- connect(d->m_profilerConnections,
- SIGNAL(addV8Event(int,QString,QString,int,double,double)),
- d->m_profilerDataModel,
- SLOT(addV8Event(int,QString,QString,int,double,double)));
- connect(d->m_profilerConnections, SIGNAL(addFrameEvent(qint64,int,int)), d->m_profilerDataModel, SLOT(addFrameEvent(qint64,int,int)));
- connect(d->m_profilerConnections, SIGNAL(traceStarted(qint64)), d->m_profilerDataModel, SLOT(setTraceStartTime(qint64)));
- connect(d->m_profilerConnections, SIGNAL(traceFinished(qint64)), d->m_profilerDataModel, SLOT(setTraceEndTime(qint64)));
- connect(d->m_profilerConnections, SIGNAL(dataReadyForProcessing()), d->m_profilerDataModel, SLOT(complete()));
-
-
- d->m_detailsRewriter = new QmlProfilerDetailsRewriter(this, &d->m_projectFinder);
- connect(d->m_profilerDataModel, SIGNAL(requestDetailsForLocation(int,QmlDebug::QmlEventLocation)),
- d->m_detailsRewriter, SLOT(requestDetailsForLocation(int,QmlDebug::QmlEventLocation)));
- connect(d->m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QmlDebug::QmlEventLocation,QString)),
- d->m_profilerDataModel, SLOT(rewriteDetailsString(int,QmlDebug::QmlEventLocation,QString)));
- connect(d->m_detailsRewriter, SIGNAL(eventDetailsChanged()), d->m_profilerDataModel, SLOT(finishedRewritingDetails()));
- connect(d->m_profilerDataModel, SIGNAL(reloadDocumentsForDetails()), d->m_detailsRewriter, SLOT(reloadDocuments()));
+ d->m_profilerModelManager = new QmlProfilerModelManager(&d->m_projectFinder, this);
+ connect(d->m_profilerModelManager, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
+ connect(d->m_profilerModelManager, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
+ d->m_profilerConnections->setModelManager(d->m_profilerModelManager);
Command *command = 0;
const Context globalContext(C_GLOBAL);
@@ -266,7 +245,7 @@ QWidget *QmlProfilerTool::createWidgets()
d->m_viewContainer = new QmlProfilerViewManager(this,
this,
- d->m_profilerDataModel,
+ d->m_profilerModelManager,
d->m_profilerState);
connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)),
this, SLOT(gotoSourceLocation(QString,int,int)));
@@ -398,8 +377,9 @@ void QmlProfilerTool::updateTimeDisplay()
if (d->m_profilerState->serverRecording() &&
d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
seconds = d->m_recordingElapsedTime.elapsed() / 1000.0;
- } else if (d->m_profilerDataModel->currentState() != QmlProfilerDataModel::Empty ) {
- seconds = (d->m_profilerDataModel->traceEndTime() - d->m_profilerDataModel->traceStartTime()) / 1.0e9;
+ } else if (d->m_profilerModelManager->state() != QmlProfilerDataState::Empty ) {
+ //seconds = d->m_profilerModelManager->traceDuration() / 1.0e9;
+ seconds = d->m_profilerModelManager->traceTime()->duration() / 1.0e9;
}
QString timeString = QString::number(seconds,'f',1);
QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6);
@@ -408,7 +388,7 @@ void QmlProfilerTool::updateTimeDisplay()
void QmlProfilerTool::clearData()
{
- d->m_profilerDataModel->clear();
+ d->m_profilerModelManager->clear();
d->m_profilerConnections->discardPendingData();
}
@@ -514,7 +494,7 @@ void QmlProfilerTool::showErrorDialog(const QString &error)
void QmlProfilerTool::showSaveOption()
{
- d->m_saveQmlTrace->setEnabled(!d->m_profilerDataModel->isEmpty());
+ d->m_saveQmlTrace->setEnabled(!d->m_profilerModelManager->isEmpty());
}
void QmlProfilerTool::showSaveDialog()
@@ -524,7 +504,7 @@ void QmlProfilerTool::showSaveDialog()
if (!filename.isEmpty()) {
if (!filename.endsWith(QLatin1String(TraceFileExtension)))
filename += QLatin1String(TraceFileExtension);
- d->m_profilerDataModel->save(filename);
+ d->m_profilerModelManager->save(filename);
}
}
@@ -540,8 +520,8 @@ void QmlProfilerTool::showLoadDialog()
if (!filename.isEmpty()) {
// delayed load (prevent graphical artifacts due to long load time)
- d->m_profilerDataModel->setFilename(filename);
- QTimer::singleShot(100, d->m_profilerDataModel, SLOT(load()));
+ d->m_profilerModelManager->setFilename(filename);
+ QTimer::singleShot(100, d->m_profilerModelManager, SLOT(load()));
}
}
@@ -549,7 +529,7 @@ void QmlProfilerTool::clientsDisconnected()
{
// If the application stopped by itself, check if we have all the data
if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppDying) {
- if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData)
+ if (d->m_profilerModelManager->state() == QmlProfilerDataState::AcquiringData)
d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
else
d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
@@ -557,21 +537,20 @@ void QmlProfilerTool::clientsDisconnected()
// ... and return to the "base" state
d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
}
-
// If the connection is closed while the app is still running, no special action is needed
}
void QmlProfilerTool::profilerDataModelStateChanged()
{
- switch (d->m_profilerDataModel->currentState()) {
- case QmlProfilerDataModel::Empty :
+ switch (d->m_profilerModelManager->state()) {
+ case QmlProfilerDataState::Empty :
clearDisplay();
break;
- case QmlProfilerDataModel::AcquiringData :
- case QmlProfilerDataModel::ProcessingData :
+ case QmlProfilerDataState::AcquiringData :
+ case QmlProfilerDataState::ProcessingData :
// nothing to be done for these two
break;
- case QmlProfilerDataModel::Done :
+ case QmlProfilerDataState::Done :
if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested)
d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
showSaveOption();
@@ -623,7 +602,7 @@ void QmlProfilerTool::profilerStateChanged()
}
case QmlProfilerStateManager::AppKilled : {
showNonmodalWarning(tr("Application finished before loading profiled data.\nPlease use the stop button instead."));
- d->m_profilerDataModel->clear();
+ d->m_profilerModelManager->clear();
break;
}
case QmlProfilerStateManager::Idle :
@@ -650,9 +629,13 @@ void QmlProfilerTool::serverRecordingChanged()
setRecording(d->m_profilerState->serverRecording());
// clear the old data each time we start a new profiling session
if (d->m_profilerState->serverRecording()) {
+ d->m_clearButton->setEnabled(false);
clearData();
- d->m_profilerDataModel->prepareForWriting();
+ d->m_profilerModelManager->prepareForWriting();
+ } else {
+ d->m_clearButton->setEnabled(true);
}
+ } else {
+ d->m_clearButton->setEnabled(true);
}
}
-
diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.cpp b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp
new file mode 100644
index 0000000000..c8405c3d3c
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertracefile.cpp
@@ -0,0 +1,562 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertracefile.h"
+
+#include <utils/qtcassert.h>
+
+#include <QIODevice>
+#include <QStringList>
+#include <QXmlStreamReader>
+#include <QXmlStreamWriter>
+#include <QDebug>
+
+// import QmlEventType, QmlBindingType enums, QmlEventLocation
+using namespace QmlDebug;
+
+
+const char PROFILER_FILE_VERSION[] = "1.02";
+
+const char TYPE_PAINTING_STR[] = "Painting";
+const char TYPE_COMPILING_STR[] = "Compiling";
+const char TYPE_CREATING_STR[] = "Creating";
+const char TYPE_BINDING_STR[] = "Binding";
+const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
+const char TYPE_PIXMAPCACHE_STR[] = "PixmapCache";
+const char TYPE_SCENEGRAPH_STR[] = "SceneGraph";
+
+#define _(X) QLatin1String(X)
+
+
+//
+// "be strict in your output but tolerant in your inputs"
+//
+
+namespace QmlProfiler {
+namespace Internal {
+
+static QmlEventType qmlEventTypeAsEnum(const QString &typeString)
+{
+ if (typeString == _(TYPE_PAINTING_STR)) {
+ return Painting;
+ } else if (typeString == _(TYPE_COMPILING_STR)) {
+ return Compiling;
+ } else if (typeString == _(TYPE_CREATING_STR)) {
+ return Creating;
+ } else if (typeString == _(TYPE_BINDING_STR)) {
+ return Binding;
+ } else if (typeString == _(TYPE_HANDLINGSIGNAL_STR)) {
+ return HandlingSignal;
+ } else if (typeString == _(TYPE_PIXMAPCACHE_STR)) {
+ return PixmapCacheEvent;
+ } else if (typeString == _(TYPE_SCENEGRAPH_STR)) {
+ return SceneGraphFrameEvent;
+ } else {
+ bool isNumber = false;
+ int type = typeString.toUInt(&isNumber);
+ if (isNumber)
+ return (QmlEventType)type;
+ else
+ return MaximumQmlEventType;
+ }
+}
+
+static QString qmlEventTypeAsString(QmlEventType typeEnum)
+{
+ switch (typeEnum) {
+ case Painting:
+ return _(TYPE_PAINTING_STR);
+ break;
+ case Compiling:
+ return _(TYPE_COMPILING_STR);
+ break;
+ case Creating:
+ return _(TYPE_CREATING_STR);
+ break;
+ case Binding:
+ return _(TYPE_BINDING_STR);
+ break;
+ case HandlingSignal:
+ return _(TYPE_HANDLINGSIGNAL_STR);
+ break;
+ case PixmapCacheEvent:
+ return _(TYPE_PIXMAPCACHE_STR);
+ break;
+ case SceneGraphFrameEvent:
+ return _(TYPE_SCENEGRAPH_STR);
+ break;
+ default:
+ return QString::number((int)typeEnum);
+ }
+}
+
+
+QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
+ QObject(parent),
+ m_v8Model(0)
+{
+}
+
+void QmlProfilerFileReader::setV8DataModel(QV8ProfilerDataModel *dataModel)
+{
+ m_v8Model = dataModel;
+}
+
+bool QmlProfilerFileReader::load(QIODevice *device)
+{
+ QXmlStreamReader stream(device);
+
+ bool validVersion = true;
+
+ while (validVersion && !stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ QString elementName = stream.name().toString();
+ switch (token) {
+ case QXmlStreamReader::StartDocument : continue;
+ case QXmlStreamReader::StartElement : {
+ if (elementName == _("trace")) {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("version")))
+ validVersion = attributes.value(_("version")).toString()
+ == _(PROFILER_FILE_VERSION);
+ else
+ validVersion = false;
+ if (attributes.hasAttribute(_("traceStart")))
+ emit traceStartTime(attributes.value(_("traceStart")).toString().toLongLong());
+ if (attributes.hasAttribute(_("traceEnd")))
+ emit traceEndTime(attributes.value(_("traceEnd")).toString().toLongLong());
+ }
+
+ if (elementName == _("eventData")) {
+ loadEventData(stream);
+ break;
+ }
+
+ if (elementName == _("profilerDataModel")) {
+ loadProfilerDataModel(stream);
+ break;
+ }
+
+ if (elementName == _("v8profile")) {
+ if (m_v8Model)
+ m_v8Model->load(stream);
+ break;
+ }
+
+ break;
+ }
+ default: break;
+ }
+ }
+
+ if (stream.hasError()) {
+ emit error(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
+ return false;
+ } else {
+ processQmlEvents();
+
+ return true;
+ }
+}
+
+void QmlProfilerFileReader::loadEventData(QXmlStreamReader &stream)
+{
+ QTC_ASSERT(stream.name().toString() == _("eventData"), return);
+
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("totalTime"))) {
+ // not used any more
+ }
+
+ int eventIndex = -1;
+ QmlEvent event = {
+ QString(), // displayname
+ QString(), // filename
+ QString(), // details
+ Painting, // type
+ QmlBinding, // bindingType, set for backwards compatibility
+ 0, // line
+ 0 // column
+ };
+ const QmlEvent defaultEvent = event;
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QString elementName = stream.name().toString();
+
+ switch (token) {
+ case QXmlStreamReader::StartElement: {
+ if (elementName == _("event")) {
+ event = defaultEvent;
+
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute(_("index"))) {
+ eventIndex = attributes.value(_("index")).toString().toInt();
+ } else {
+ // ignore event
+ eventIndex = -1;
+ }
+ break;
+ }
+
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+
+ const QString readData = stream.text().toString();
+
+ if (elementName == _("displayname")) {
+ event.displayName = readData;
+ break;
+ }
+
+ if (elementName == _("type")) {
+ event.type = qmlEventTypeAsEnum(readData);
+ break;
+ }
+
+ if (elementName == _("filename")) {
+ event.filename = readData;
+ break;
+ }
+
+ if (elementName == _("line")) {
+ event.line = readData.toInt();
+ break;
+ }
+
+ if (elementName == _("column")) {
+ event.column = readData.toInt();
+ break;
+ }
+
+ if (elementName == _("details")) {
+ event.details = readData;
+ break;
+ }
+
+ if (elementName == _("bindingType") ||
+ elementName == _("animationFrame") ||
+ elementName == _("cacheEventType") ||
+ elementName == _("sgEventType")) {
+ event.bindingType = readData.toInt();
+ break;
+ }
+
+ break;
+ }
+ case QXmlStreamReader::EndElement: {
+ if (elementName == _("event")) {
+ if (eventIndex >= 0) {
+ if (eventIndex >= m_qmlEvents.size())
+ m_qmlEvents.resize(eventIndex + 1);
+ m_qmlEvents[eventIndex] = event;
+ }
+ break;
+ }
+
+ if (elementName == _("eventData")) {
+ // done reading eventData
+ return;
+ }
+ break;
+ }
+ default: break;
+ } // switch
+ }
+}
+
+void QmlProfilerFileReader::loadProfilerDataModel(QXmlStreamReader &stream)
+{
+ QTC_ASSERT(stream.name().toString() == _("profilerDataModel"), return);
+
+ while (!stream.atEnd() && !stream.hasError()) {
+ QXmlStreamReader::TokenType token = stream.readNext();
+ const QString elementName = stream.name().toString();
+
+ switch (token) {
+ case QXmlStreamReader::StartElement: {
+ if (elementName == _("range")) {
+ Range range = { 0, 0, 0, 0, 0, 0, 0 };
+
+ const QXmlStreamAttributes attributes = stream.attributes();
+ if (!attributes.hasAttribute(_("startTime"))
+ || !attributes.hasAttribute(_("eventIndex"))) {
+ // ignore incomplete entry
+ continue;
+ }
+
+ range.startTime = attributes.value(_("startTime")).toString().toLongLong();
+ if (attributes.hasAttribute(_("duration")))
+ range.duration = attributes.value(_("duration")).toString().toLongLong();
+
+ // attributes for special events
+ if (attributes.hasAttribute(_("framerate")))
+ range.numericData1 = attributes.value(_("framerate")).toString().toLongLong();
+ if (attributes.hasAttribute(_("animationcount")))
+ range.numericData2 = attributes.value(_("animationcount")).toString().toLongLong();
+ if (attributes.hasAttribute(_("width")))
+ range.numericData1 = attributes.value(_("width")).toString().toLongLong();
+ if (attributes.hasAttribute(_("height")))
+ range.numericData2 = attributes.value(_("height")).toString().toLongLong();
+ if (attributes.hasAttribute(_("refCount")))
+ range.numericData3 = attributes.value(_("refCount")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing1")))
+ range.numericData1 = attributes.value(_("timing1")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing2")))
+ range.numericData2 = attributes.value(_("timing2")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing3")))
+ range.numericData3 = attributes.value(_("timing3")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing4")))
+ range.numericData4 = attributes.value(_("timing4")).toString().toLongLong();
+ if (attributes.hasAttribute(_("timing5")))
+ range.numericData5 = attributes.value(_("timing5")).toString().toLongLong();
+
+
+ int eventIndex = attributes.value(_("eventIndex")).toString().toInt();
+
+
+ m_ranges.append(QPair<Range,int>(range, eventIndex));
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement: {
+ if (elementName == _("profilerDataModel")) {
+ // done reading profilerDataModel
+ return;
+ }
+ break;
+ }
+ default: break;
+ } // switch
+ }
+}
+
+void QmlProfilerFileReader::processQmlEvents()
+{
+ for (int i = 0; i < m_ranges.size(); ++i) {
+ Range range = m_ranges[i].first;
+ int eventIndex = m_ranges[i].second;
+
+ if (eventIndex < 0 || eventIndex >= m_qmlEvents.size()) {
+ qWarning() << ".qtd file - range index" << eventIndex
+ << "is outside of bounds (0, " << m_qmlEvents.size() << ")";
+ continue;
+ }
+
+ QmlEvent &event = m_qmlEvents[eventIndex];
+
+ emit rangedEvent(event.type, event.bindingType, range.startTime, range.duration,
+ QStringList(event.displayName),
+ QmlEventLocation(event.filename, event.line, event.column),
+ range.numericData1,range.numericData2, range.numericData3, range.numericData4, range.numericData5);
+
+ }
+}
+
+QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
+ QObject(parent),
+ m_startTime(0),
+ m_endTime(0),
+ m_measuredTime(0),
+ m_v8Model(0)
+{
+ m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
+}
+
+void QmlProfilerFileWriter::setTraceTime(qint64 startTime, qint64 endTime, qint64 measuredTime)
+{
+ m_startTime = startTime;
+ m_endTime = endTime;
+ m_measuredTime = measuredTime;
+}
+
+void QmlProfilerFileWriter::setV8DataModel(QV8ProfilerDataModel *dataModel)
+{
+ m_v8Model = dataModel;
+}
+
+void QmlProfilerFileWriter::setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
+{
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
+ const QString hashStr = QmlProfilerSimpleModel::getHashString(event);
+ if (!m_qmlEvents.contains(hashStr)) {
+ QmlEvent e = { event.displayName,
+ event.location.filename,
+ event.data.join(_("")),
+ static_cast<QmlDebug::QmlEventType>(event.eventType),
+ event.bindingType,
+ event.location.line,
+ event.location.column
+ };
+ m_qmlEvents.insert(hashStr, e);
+ }
+
+ Range r = { event.startTime, event.duration, event.numericData1, event.numericData2, event.numericData3, event.numericData4, event.numericData5 };
+ m_ranges.append(QPair<Range, QString>(r, hashStr));
+ }
+
+ calculateMeasuredTime(events);
+}
+
+void QmlProfilerFileWriter::save(QIODevice *device)
+{
+ QXmlStreamWriter stream(device);
+
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement(_("trace"));
+ stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION));
+
+ stream.writeAttribute(_("traceStart"), QString::number(m_startTime));
+ stream.writeAttribute(_("traceEnd"), QString::number(m_endTime));
+
+ stream.writeStartElement(_("eventData"));
+ stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime));
+
+ QHash<QString,QmlEvent>::const_iterator eventIter = m_qmlEvents.constBegin();
+ for (; eventIter != m_qmlEvents.constEnd(); ++eventIter) {
+ QmlEvent event = eventIter.value();
+
+ stream.writeStartElement(_("event"));
+ stream.writeAttribute(_("index"), QString::number(m_qmlEvents.keys().indexOf(eventIter.key())));
+ stream.writeTextElement(_("displayname"), event.displayName);
+ stream.writeTextElement(_("type"), qmlEventTypeAsString(event.type));
+ if (!event.filename.isEmpty()) {
+ stream.writeTextElement(_("filename"), event.filename);
+ stream.writeTextElement(_("line"), QString::number(event.line));
+ stream.writeTextElement(_("column"), QString::number(event.column));
+ }
+ stream.writeTextElement(_("details"), event.details);
+ if (event.type == Binding)
+ stream.writeTextElement(_("bindingType"), QString::number(event.bindingType));
+ if (event.type == Painting && event.bindingType == AnimationFrame)
+ stream.writeTextElement(_("animationFrame"), QString::number(event.bindingType));
+ if (event.type == PixmapCacheEvent)
+ stream.writeTextElement(_("cacheEventType"), QString::number(event.bindingType));
+ if (event.type == SceneGraphFrameEvent)
+ stream.writeTextElement(_("sgEventType"), QString::number(event.bindingType));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement(_("profilerDataModel"));
+
+ QVector<QPair<Range, QString> >::const_iterator rangeIter = m_ranges.constBegin();
+ for (; rangeIter != m_ranges.constEnd(); ++rangeIter) {
+ Range range = rangeIter->first;
+ QString eventHash = rangeIter->second;
+
+ stream.writeStartElement(_("range"));
+ stream.writeAttribute(_("startTime"), QString::number(range.startTime));
+ if (range.duration > 0) // no need to store duration of instantaneous events
+ stream.writeAttribute(_("duration"), QString::number(range.duration));
+ stream.writeAttribute(_("eventIndex"), QString::number(m_qmlEvents.keys().indexOf(eventHash)));
+
+ QmlEvent event = m_qmlEvents.value(eventHash);
+
+ // special: animation event
+ if (event.type == QmlDebug::Painting && event.bindingType == QmlDebug::AnimationFrame) {
+
+ stream.writeAttribute(_("framerate"), QString::number(range.numericData1));
+ stream.writeAttribute(_("animationcount"), QString::number(range.numericData2));
+ }
+
+ // special: pixmap cache event
+ if (event.type == QmlDebug::PixmapCacheEvent) {
+ // pixmap image size
+ if (event.bindingType == 0) {
+ stream.writeAttribute(_("width"), QString::number(range.numericData1));
+ stream.writeAttribute(_("height"), QString::number(range.numericData2));
+ }
+
+ // reference count (1) / cache size changed (2)
+ if (event.bindingType == 1 || event.bindingType == 2)
+ stream.writeAttribute(_("refCount"), QString::number(range.numericData3));
+ }
+
+ if (event.type == QmlDebug::SceneGraphFrameEvent) {
+ // special: scenegraph frame events
+ if (range.numericData1 > 0)
+ stream.writeAttribute(_("timing1"), QString::number(range.numericData1));
+ if (range.numericData2 > 0)
+ stream.writeAttribute(_("timing2"), QString::number(range.numericData2));
+ if (range.numericData3 > 0)
+ stream.writeAttribute(_("timing3"), QString::number(range.numericData3));
+ if (range.numericData4 > 0)
+ stream.writeAttribute(_("timing4"), QString::number(range.numericData4));
+ if (range.numericData5 > 0)
+ stream.writeAttribute(_("timing5"), QString::number(range.numericData5));
+ }
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // profilerDataModel
+
+ m_v8Model->save(stream);
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+}
+
+void QmlProfilerFileWriter::calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
+{
+ // measured time isn't used, but old clients might still need it
+ // -> we calculate it explicitly
+
+ qint64 duration = 0;
+
+ QHash<int, qint64> endtimesPerLevel;
+ int level = QmlDebug::Constants::QML_MIN_LEVEL;
+ endtimesPerLevel[0] = 0;
+
+ foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
+ // whitelist
+ if (!m_acceptedTypes.contains(event.eventType))
+ continue;
+
+ // level computation
+ if (endtimesPerLevel[level] > event.startTime) {
+ level++;
+ } else {
+ while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
+ level--;
+ }
+ endtimesPerLevel[level] = event.startTime + event.duration;
+ if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
+ duration += event.duration;
+ }
+ }
+
+ m_measuredTime = duration;
+}
+
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilertracefile.h b/src/plugins/qmlprofiler/qmlprofilertracefile.h
new file mode 100644
index 0000000000..f4e1b71d10
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertracefile.h
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERTRACEFILE_H
+#define QMLPROFILERTRACEFILE_H
+
+#include <QObject>
+#include <QVector>
+#include <QString>
+
+#include <qmldebug/qmlprofilereventlocation.h>
+#include <qmldebug/qmlprofilereventtypes.h>
+
+#include "qmlprofilersimplemodel.h"
+#include "qv8profilerdatamodel.h"
+
+QT_FORWARD_DECLARE_CLASS(QIODevice)
+QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
+
+
+namespace QmlProfiler {
+namespace Internal {
+
+
+struct QmlEvent {
+ QString displayName;
+ QString filename;
+ QString details;
+ QmlDebug::QmlEventType type;
+ int bindingType;
+ int line;
+ int column;
+};
+
+struct Range {
+ qint64 startTime;
+ qint64 duration;
+
+ // numeric data used by animations, pixmap cache, scenegraph
+ qint64 numericData1;
+ qint64 numericData2;
+ qint64 numericData3;
+ qint64 numericData4;
+ qint64 numericData5;
+};
+
+class QmlProfilerFileReader : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerFileReader(QObject *parent = 0);
+
+ void setV8DataModel(QV8ProfilerDataModel *dataModel);
+
+ bool load(QIODevice *device);
+
+signals:
+ void traceStartTime(qint64 traceStartTime);
+ void traceEndTime(qint64 traceStartTime);
+
+ void rangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
+ const QStringList &data, const QmlDebug::QmlEventLocation &location,
+ qint64 param1, qint64 param2, qint64 param3, qint64 param4, qint64 param5);
+ void error(const QString &error);
+
+private:
+ void loadEventData(QXmlStreamReader &reader);
+ void loadProfilerDataModel(QXmlStreamReader &reader);
+
+ void processQmlEvents();
+
+
+ QV8ProfilerDataModel *m_v8Model;
+ QVector<QmlEvent> m_qmlEvents;
+ QVector<QPair<Range, int> > m_ranges;
+};
+
+
+class QmlProfilerFileWriter : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit QmlProfilerFileWriter(QObject *parent = 0);
+
+ void setTraceTime(qint64 startTime, qint64 endTime, qint64 measturedTime);
+ void setV8DataModel(QV8ProfilerDataModel *dataModel);
+ void setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events);
+
+ void save(QIODevice *device);
+
+private:
+ void calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events);
+
+
+ qint64 m_startTime, m_endTime, m_measuredTime;
+ QV8ProfilerDataModel *m_v8Model;
+ QHash<QString,QmlEvent> m_qmlEvents;
+ QVector<QPair<Range, QString> > m_ranges;
+ QVector <int> m_acceptedTypes;
+};
+
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERTRACEFILE_H
diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
index 5fbad1247a..c094a07f13 100644
--- a/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertraceview.cpp
@@ -30,12 +30,14 @@
#include "qmlprofilertraceview.h"
#include "qmlprofilertool.h"
#include "qmlprofilerstatemanager.h"
-#include "qmlprofilerdatamodel.h"
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilertimelinemodelproxy.h"
+#include "timelinemodelaggregator.h"
// Needed for the load&save actions in the context menu
#include <analyzerbase/ianalyzertool.h>
-// Comunication with the other views (limit events to range)
+// Communication with the other views (limit events to range)
#include "qmlprofilerviewmanager.h"
#include <utils/styledbar.h>
@@ -119,7 +121,9 @@ public:
ScrollableDeclarativeView *m_mainView;
QDeclarativeView *m_timebar;
QDeclarativeView *m_overview;
- QmlProfilerDataModel *m_profilerDataModel;
+ QmlProfilerModelManager *m_modelManager;
+ TimelineModelAggregator *m_modelProxy;
+
ZoomControl *m_zoomControl;
@@ -129,7 +133,7 @@ public:
int m_currentZoomLevel;
};
-QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState)
+QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerModelManager *modelManager, QmlProfilerStateManager *profilerState)
: QWidget(parent), d(new QmlProfilerTraceViewPrivate(this))
{
setObjectName(QLatin1String("QML Profiler"));
@@ -180,13 +184,15 @@ QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerT
d->m_profilerTool = profilerTool;
d->m_viewContainer = container;
- d->m_profilerDataModel = model;
- connect(d->m_profilerDataModel, SIGNAL(stateChanged()),
+ d->m_modelManager = modelManager;
+ d->m_modelProxy = new TimelineModelAggregator(this);
+ d->m_modelProxy->setModelManager(modelManager);
+ connect(d->m_modelManager, SIGNAL(stateChanged()),
this, SLOT(profilerDataModelStateChanged()));
- d->m_mainView->rootContext()->setContextProperty(QLatin1String("qmlProfilerDataModel"),
- d->m_profilerDataModel);
- d->m_overview->rootContext()->setContextProperty(QLatin1String("qmlProfilerDataModel"),
- d->m_profilerDataModel);
+ d->m_mainView->rootContext()->setContextProperty(QLatin1String("qmlProfilerModelProxy"),
+ d->m_modelProxy);
+ d->m_overview->rootContext()->setContextProperty(QLatin1String("qmlProfilerModelProxy"),
+ d->m_modelProxy);
d->m_profilerState = profilerState;
connect(d->m_profilerState, SIGNAL(stateChanged()),
@@ -372,12 +378,25 @@ void QmlProfilerTraceView::clearDisplay()
QMetaObject::invokeMethod(d->m_overview->rootObject(), "clearDisplay");
}
-void QmlProfilerTraceView::selectNextEventWithId(int eventId)
+void QmlProfilerTraceView::selectNextEventByHash(const QString &hash)
{
QGraphicsObject *rootObject = d->m_mainView->rootObject();
+
if (rootObject)
- QMetaObject::invokeMethod(rootObject, "selectNextWithId",
- Q_ARG(QVariant,QVariant(eventId)));
+ QMetaObject::invokeMethod(rootObject, "selectNextByHash",
+ Q_ARG(QVariant,QVariant(hash)));
+}
+
+void QmlProfilerTraceView::selectNextEventByLocation(const QString &filename, const int line, const int column)
+{
+ int eventId = d->m_modelProxy->getEventIdForLocation(filename, line, column);
+
+ if (eventId != -1) {
+ QGraphicsObject *rootObject = d->m_mainView->rootObject();
+ if (rootObject)
+ QMetaObject::invokeMethod(rootObject, "selectNextById",
+ Q_ARG(QVariant,QVariant(eventId)));
+ }
}
/////////////////////////////////////////////////////////
@@ -444,14 +463,14 @@ void QmlProfilerTraceView::setZoomLevel(int zoomLevel)
void QmlProfilerTraceView::updateRange()
{
- if (!d->m_profilerDataModel)
+ if (!d->m_modelManager)
return;
qreal duration = d->m_zoomControl->endTime() - d->m_zoomControl->startTime();
if (duration <= 0)
return;
- if (d->m_profilerDataModel->traceDuration() <= 0)
+ if (d->m_modelManager->traceTime()->duration() <= 0)
return;
- int newLevel = pow(duration / d->m_profilerDataModel->traceDuration(), 1/sliderExp) * sliderTicks;
+ int newLevel = pow(duration / d->m_modelManager->traceTime()->duration(), 1/sliderExp) * sliderTicks;
if (d->m_currentZoomLevel != newLevel) {
d->m_currentZoomLevel = newLevel;
emit zoomLevelChanged(newLevel);
@@ -513,7 +532,7 @@ void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
if (d->m_viewContainer->hasGlobalStats())
getGlobalStatsAction->setEnabled(false);
- if (d->m_profilerDataModel->count() > 0) {
+ if (!d->m_modelProxy->isEmpty()) {
menu.addSeparator();
viewAllAction = menu.addAction(tr("Reset Zoom"));
}
@@ -523,8 +542,8 @@ void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
if (selectedAction) {
if (selectedAction == viewAllAction) {
d->m_zoomControl->setRange(
- d->m_profilerDataModel->traceStartTime(),
- d->m_profilerDataModel->traceEndTime());
+ d->m_modelManager->traceTime()->startTime(),
+ d->m_modelManager->traceTime()->endTime());
}
if (selectedAction == getLocalStatsAction) {
d->m_viewContainer->getStatisticsInRange(
@@ -532,9 +551,7 @@ void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
d->m_viewContainer->selectionEnd());
}
if (selectedAction == getGlobalStatsAction) {
- d->m_viewContainer->getStatisticsInRange(
- d->m_profilerDataModel->traceStartTime(),
- d->m_profilerDataModel->traceEndTime());
+ d->m_viewContainer->getStatisticsInRange(-1, -1);
}
}
}
@@ -558,19 +575,15 @@ void QmlProfilerTraceView::setAppKilled()
// Profiler State
void QmlProfilerTraceView::profilerDataModelStateChanged()
{
- switch (d->m_profilerDataModel->currentState()) {
- case QmlProfilerDataModel::Empty :
- emit enableToolbar(false);
- break;
- case QmlProfilerDataModel::AcquiringData :
- // nothing to be done
+ switch (d->m_modelManager->state()) {
+ case QmlProfilerDataState::Empty:
+ emit enableToolbar(false);
break;
- case QmlProfilerDataModel::ProcessingData :
- // nothing to be done
+ case QmlProfilerDataState::AcquiringData: break;
+ case QmlProfilerDataState::ProcessingData: break;
+ case QmlProfilerDataState::Done:
+ emit enableToolbar(true);
break;
- case QmlProfilerDataModel::Done :
- emit enableToolbar(true);
- break;
default:
break;
}
@@ -580,7 +593,7 @@ void QmlProfilerTraceView::profilerStateChanged()
{
switch (d->m_profilerState->currentState()) {
case QmlProfilerStateManager::AppKilled : {
- if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData)
+ if (d->m_modelManager->state() == QmlProfilerDataState::AcquiringData)
setAppKilled();
break;
}
diff --git a/src/plugins/qmlprofiler/qmlprofilertraceview.h b/src/plugins/qmlprofiler/qmlprofilertraceview.h
index 0ff15025ab..dd7d233ff0 100644
--- a/src/plugins/qmlprofiler/qmlprofilertraceview.h
+++ b/src/plugins/qmlprofiler/qmlprofilertraceview.h
@@ -37,11 +37,12 @@ class IAnalyzerTool;
}
namespace QmlProfiler {
+
+class QmlProfilerModelManager;
namespace Internal {
class QmlProfilerStateManager;
class QmlProfilerViewManager;
-class QmlProfilerDataModel;
// capture mouse wheel events
class MouseWheelResizer : public QObject {
@@ -83,12 +84,13 @@ protected:
void scrollContentsBy(int dx, int dy);
};
+
class QmlProfilerTraceView : public QWidget
{
Q_OBJECT
public:
- explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState);
+ explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerModelManager *modelManager, QmlProfilerStateManager *profilerState);
~QmlProfilerTraceView();
void reset();
@@ -99,7 +101,8 @@ public:
public slots:
void clearDisplay();
- void selectNextEventWithId(int eventId);
+ void selectNextEventByHash(const QString &eventHash);
+ void selectNextEventByLocation(const QString &filename, const int line, const int column);
private slots:
void updateCursorPosition();
diff --git a/src/plugins/qmlprofiler/qmlprofilertreeview.cpp b/src/plugins/qmlprofiler/qmlprofilertreeview.cpp
new file mode 100644
index 0000000000..0d10bbb4e9
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertreeview.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilertreeview.h"
+#include <QCoreApplication>
+#include <QHeaderView>
+
+namespace QmlProfiler {
+namespace Internal {
+
+QmlProfilerTreeView::QmlProfilerTreeView(QWidget *parent)
+ : QTreeView(parent)
+{
+ setFrameStyle(QFrame::NoFrame);
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+}
+
+
+// Translate from "old" context to keep 2.8 string freeze
+QString QmlProfilerTreeView::displayHeader(Fields header) const
+{
+ static const char ctxt1[] = "QmlProfiler::Internal::QmlProfilerEventsParentsAndChildrenView";
+ static const char ctxt2[] = "QmlProfiler::Internal::QmlProfilerEventsMainView";
+
+ switch (header) {
+ case Callee:
+ return QCoreApplication::translate(ctxt1, "Callee");
+ case CalleeDescription:
+ return QCoreApplication::translate(ctxt1, "Callee Description");
+ case Caller:
+ return QCoreApplication::translate(ctxt1, "Caller");
+ case CallerDescription:
+ return QCoreApplication::translate(ctxt1, "Caller Description");
+ case CallCount:
+ return QCoreApplication::translate(ctxt2, "Calls");
+ case Details:
+ return QCoreApplication::translate(ctxt2, "Details");
+ case Location:
+ return QCoreApplication::translate(ctxt2, "Location");
+ case MaxTime:
+ return QCoreApplication::translate(ctxt2, "Longest Time");
+ case TimePerCall:
+ return QCoreApplication::translate(ctxt2, "Mean Time");
+ case SelfTime:
+ return QCoreApplication::translate(ctxt2, "Self Time");
+ case SelfTimeInPercent:
+ return QCoreApplication::translate(ctxt2, "Self Time in Percent");
+ case MinTime:
+ return QCoreApplication::translate(ctxt2, "Shortest Time");
+ case TimeInPercent:
+ return QCoreApplication::translate(ctxt2, "Time in Percent");
+ case TotalTime:
+ return QCoreApplication::translate(ctxt2, "Total Time");
+ case Type:
+ return QCoreApplication::translate(ctxt2, "Type");
+ case MedianTime:
+ return QCoreApplication::translate(ctxt2, "Median Time");
+ }
+ return QString();
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilertreeview.h b/src/plugins/qmlprofiler/qmlprofilertreeview.h
new file mode 100644
index 0000000000..73b2b5d769
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilertreeview.h
@@ -0,0 +1,72 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILERTREEVIEW
+#define QMLPROFILERTREEVIEW
+
+#include <QTreeView>
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerTreeView : public QTreeView
+{
+ Q_OBJECT
+
+protected:
+ QmlProfilerTreeView(QWidget *parent = 0);
+
+
+ enum Fields {
+ Name,
+ Callee,
+ CalleeDescription,
+ Caller,
+ CallerDescription,
+ CallCount,
+ Details,
+ Location,
+ MaxTime,
+ TimePerCall,
+ SelfTime,
+ SelfTimeInPercent,
+ MinTime,
+ TimeInPercent,
+ TotalTime,
+ Type,
+ MedianTime,
+ MaxFields
+ };
+ QString displayHeader(Fields header) const;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILERTREEVIEW
diff --git a/src/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp b/src/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp
new file mode 100644
index 0000000000..89bd948cfb
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerv8eventsview.cpp
@@ -0,0 +1,1016 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qmlprofilereventview.h"
+
+#include <QUrl>
+#include <QHash>
+
+#include <QStandardItem>
+#include <QHeaderView>
+
+#include <QApplication>
+#include <QClipboard>
+
+#include <QContextMenuEvent>
+#include <QDebug>
+
+#include <coreplugin/minisplitter.h>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+#ifdef SHOW_BINDINGLOOPS
+struct Colors {
+ Colors () {
+ this->bindingLoopBackground = QColor("orange").lighter();
+ }
+
+ QColor bindingLoopBackground;
+};
+
+Q_GLOBAL_STATIC(Colors, colors)
+#endif
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class EventsViewItem : public QStandardItem
+{
+public:
+ EventsViewItem(const QString &text) : QStandardItem(text) {}
+
+ virtual bool operator<(const QStandardItem &other) const
+ {
+ if (data().type() == QVariant::String) {
+ // first column
+ if (column() == 0) {
+ return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
+ data(LineRole).toInt() < other.data(LineRole).toInt() :
+ data(FilenameRole).toString() < other.data(FilenameRole).toString();
+ } else {
+ return data().toString().toLower() < other.data().toString().toLower();
+ }
+ }
+
+ return data().toDouble() < other.data().toDouble();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsWidget::QmlProfilerEventsWidgetPrivate
+{
+public:
+ QmlProfilerEventsWidgetPrivate(QmlProfilerEventsWidget *qq):q(qq) {}
+ ~QmlProfilerEventsWidgetPrivate() {}
+
+ QmlProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QmlProfilerEventsMainView *m_eventTree;
+ QmlProfilerEventRelativesView *m_eventChildren;
+ QmlProfilerEventRelativesView *m_eventParents;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+
+ bool m_globalStatsEnabled;
+};
+
+QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager )
+ : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsView"));
+
+ d->modelProxy = new QmlProfilerEventsModelProxy(profilerModelManager, this);
+ connect(profilerModelManager, SIGNAL(stateChanged()),
+ this, SLOT(profilerDataModelStateChanged()));
+
+ d->m_eventTree = new QmlProfilerEventsMainView(this, d->modelProxy);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int)));
+
+ d->m_eventChildren = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventChildrenModelProxy(profilerModelManager, this),
+ this);
+ d->m_eventParents = new QmlProfilerEventRelativesView(
+ profilerModelManager,
+ new QmlProfilerEventParentsModelProxy(profilerModelManager, this),
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventChildren, SLOT(displayEvent(QString)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventParents, SLOT(displayEvent(QString)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
+
+ // widget arrangement
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0,0,0,0);
+ groupLayout->setSpacing(0);
+
+ Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
+ splitterVertical->addWidget(d->m_eventTree);
+ Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
+ splitterHorizontal->setOrientation(Qt::Horizontal);
+ splitterVertical->addWidget(splitterHorizontal);
+ splitterVertical->setOrientation(Qt::Vertical);
+ splitterVertical->setStretchFactor(0,5);
+ splitterVertical->setStretchFactor(1,2);
+ groupLayout->addWidget(splitterVertical);
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+ d->m_globalStatsEnabled = true;
+
+}
+
+QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
+{
+ delete d->modelProxy;
+ delete d;
+}
+
+void QmlProfilerEventsWidget::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsWidget::clear()
+{
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
+}
+
+void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ clear();
+ d->m_eventTree->getStatisticsInRange(rangeStart, rangeEnd);
+ d->m_globalStatsEnabled = d->m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
+}
+
+QModelIndex QmlProfilerEventsWidget::selectedItem() const
+{
+ return d->m_eventTree->selectedItem();
+}
+
+void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+ QAction *showExtendedStatsAction = 0;
+ QAction *getLocalStatsAction = 0;
+ QAction *getGlobalStatsAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(tr("Copy Row"));
+ copyTableAction = menu.addAction(tr("Copy Table"));
+
+ showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
+ showExtendedStatsAction->setCheckable(true);
+ showExtendedStatsAction->setChecked(showExtendedStatistics());
+ }
+
+ menu.addSeparator();
+ getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
+ if (!d->m_viewContainer->hasValidSelection())
+ getLocalStatsAction->setEnabled(false);
+ getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
+ if (hasGlobalStats())
+ getGlobalStatsAction->setEnabled(false);
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ if (selectedAction == getLocalStatsAction) {
+ getStatisticsInRange(d->m_viewContainer->selectionStart(),
+ d->m_viewContainer->selectionEnd());
+ }
+
+ if (selectedAction == showExtendedStatsAction)
+ setShowExtendedStatistics(!showExtendedStatistics());
+ }
+}
+
+void QmlProfilerEventsWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ emit resized();
+}
+
+bool QmlProfilerEventsWidget::mouseOnTable(const QPoint &position) const
+{
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
+ return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
+}
+
+void QmlProfilerEventsWidget::copyTableToClipboard() const
+{
+ d->m_eventTree->copyTableToClipboard();
+}
+
+void QmlProfilerEventsWidget::copyRowToClipboard() const
+{
+ d->m_eventTree->copyRowToClipboard();
+}
+
+void QmlProfilerEventsWidget::updateSelectedEvent(const QString &eventHash) const
+{
+ if (d->m_eventTree->selectedEventHash() != eventHash)
+ d->m_eventTree->selectEvent(eventHash);
+}
+
+void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
+{
+ // This slot is used to connect the javascript pane with the qml events pane
+ // Our javascript trace data does not store column information
+ // thus we ignore it here
+ Q_UNUSED(column);
+ d->m_eventTree->selectEventByLocation(filename, line);
+}
+
+bool QmlProfilerEventsWidget::hasGlobalStats() const
+{
+ return d->m_globalStatsEnabled;
+}
+
+void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
+{
+ d->m_eventTree->setShowExtendedStatistics(show);
+}
+
+bool QmlProfilerEventsWidget::showExtendedStatistics() const
+{
+ return d->m_eventTree->showExtendedStatistics();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
+{
+public:
+ QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
+
+ int getFieldCount();
+
+ QString textForItem(QStandardItem *item, bool recursive) const;
+
+
+ QmlProfilerEventsMainView *q;
+
+ QmlProfilerEventsModelProxy *modelProxy;
+ QStandardItemModel *m_model;
+ QList<bool> m_fieldShown;
+ QHash<int, int> m_columnIndex; // maps field enum to column index
+ bool m_showExtendedStatistics;
+ int m_firstNumericColumn;
+ bool m_preventSelectBounce;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QmlProfilerEventsMainView::QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy)
+: QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsTable"));
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+ setSortingEnabled(false);
+ setFrameStyle(QFrame::NoFrame);
+
+ d->m_model = new QStandardItemModel(this);
+ setModel(d->m_model);
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+
+ d->modelProxy = modelProxy;
+ connect(d->modelProxy,SIGNAL(dataAvailable()), this, SLOT(buildModel()));
+// connect(d->modelProxy,SIGNAL(stateChanged()),
+// this,SLOT(profilerDataModelStateChanged()));
+ d->m_firstNumericColumn = 0;
+ d->m_preventSelectBounce = false;
+ d->m_showExtendedStatistics = false;
+
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, true);
+ setFieldViewable(Percent, true);
+ setFieldViewable(TotalDuration, true);
+ setFieldViewable(SelfPercent, false);
+ setFieldViewable(SelfDuration, false);
+ setFieldViewable(CallCount, true);
+ setFieldViewable(TimePerCall, true);
+ setFieldViewable(MaxTime, true);
+ setFieldViewable(MinTime, true);
+ setFieldViewable(MedianTime, true);
+ setFieldViewable(Details, true);
+
+ buildModel();
+}
+
+QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
+{
+ clear();
+ //delete d->modelProxy;
+ delete d->m_model;
+ delete d;
+}
+
+void QmlProfilerEventsMainView::profilerDataModelStateChanged()
+{
+}
+
+void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
+{
+ if (field < MaxFields) {
+ int length = d->m_fieldShown.count();
+ if (field >= length) {
+ for (int i=length; i<MaxFields; i++)
+ d->m_fieldShown << false;
+ }
+ d->m_fieldShown[field] = show;
+ }
+}
+
+
+void QmlProfilerEventsMainView::setHeaderLabels()
+{
+ int fieldIndex = 0;
+ d->m_firstNumericColumn = 0;
+
+ d->m_columnIndex.clear();
+ if (d->m_fieldShown[Name]) {
+ d->m_columnIndex[Name] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Location")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Type]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type")));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Percent]) {
+ d->m_columnIndex[Percent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent")));
+ }
+ if (d->m_fieldShown[TotalDuration]) {
+ d->m_columnIndex[TotalDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+ }
+ if (d->m_fieldShown[SelfPercent]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent")));
+ }
+ if (d->m_fieldShown[SelfDuration]) {
+ d->m_columnIndex[SelfDuration] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time")));
+ }
+ if (d->m_fieldShown[CallCount]) {
+ d->m_columnIndex[CallCount] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+ }
+ if (d->m_fieldShown[TimePerCall]) {
+ d->m_columnIndex[TimePerCall] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time")));
+ }
+ if (d->m_fieldShown[MedianTime]) {
+ d->m_columnIndex[MedianTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time")));
+ }
+ if (d->m_fieldShown[MaxTime]) {
+ d->m_columnIndex[MaxTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time")));
+ }
+ if (d->m_fieldShown[MinTime]) {
+ d->m_columnIndex[MinTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time")));
+ }
+ if (d->m_fieldShown[Details]) {
+ d->m_columnIndex[Details] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details")));
+ }
+}
+
+void QmlProfilerEventsMainView::setShowExtendedStatistics(bool show)
+{
+ // Not checking if already set because we don't want the first call to skip
+ d->m_showExtendedStatistics = show;
+ if (show) {
+ if (d->m_fieldShown[MedianTime])
+ showColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ showColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ showColumn(d->m_columnIndex[MinTime]);
+ } else{
+ if (d->m_fieldShown[MedianTime])
+ hideColumn(d->m_columnIndex[MedianTime]);
+ if (d->m_fieldShown[MaxTime])
+ hideColumn(d->m_columnIndex[MaxTime]);
+ if (d->m_fieldShown[MinTime])
+ hideColumn(d->m_columnIndex[MinTime]);
+ }
+}
+
+bool QmlProfilerEventsMainView::showExtendedStatistics() const
+{
+ return d->m_showExtendedStatistics;
+}
+
+void QmlProfilerEventsMainView::clear()
+{
+ d->m_model->clear();
+ d->m_model->setColumnCount(d->getFieldCount());
+
+ setHeaderLabels();
+ setSortingEnabled(false);
+}
+
+int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
+{
+ int count = 0;
+ for (int i=0; i < m_fieldShown.count(); ++i)
+ if (m_fieldShown[i])
+ count++;
+ return count;
+}
+
+void QmlProfilerEventsMainView::buildModel()
+{
+ clear();
+ parseModelProxy();
+ setShowExtendedStatistics(d->m_showExtendedStatistics);
+
+ setRootIsDecorated(false);
+ setSortingEnabled(true);
+ sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
+
+ expandAll();
+ if (d->m_fieldShown[Name])
+ resizeColumnToContents(0);
+
+ if (d->m_fieldShown[Type])
+ resizeColumnToContents(d->m_fieldShown[Name]?1:0);
+ collapseAll();
+}
+
+void QmlProfilerEventsMainView::parseModelProxy()
+{
+ const QList <QmlProfilerEventsModelProxy::QmlEventStats> eventList = d->modelProxy->getData();
+ foreach (const QmlProfilerEventsModelProxy::QmlEventStats &event, eventList) {
+ QStandardItem *parentItem = d->m_model->invisibleRootItem();
+ QList<QStandardItem *> newRow;
+
+ if (d->m_fieldShown[Name])
+ newRow << new EventsViewItem(event.displayName);
+
+ if (d->m_fieldShown[Type]) {
+ QString typeString = QmlProfilerEventsMainView::nameForType(event.eventType);
+ QString toolTipText;
+ if (event.eventType == Binding) {
+ if (event.bindingType == (int)OptimizedBinding) {
+ typeString = typeString + tr(" (Opt)");
+ toolTipText = tr("Binding is evaluated by the optimized engine.");
+ } else if (event.bindingType == (int)V8Binding) {
+ toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n"
+ "references to elements in other files, loops, etc.)");
+
+ }
+ }
+ newRow << new EventsViewItem(typeString);
+ newRow.last()->setData(QVariant(typeString));
+ if (!toolTipText.isEmpty())
+ newRow.last()->setToolTip(toolTipText);
+ }
+
+ if (d->m_fieldShown[Percent]) {
+ newRow << new EventsViewItem(QString::number(event.percentOfTime,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(event.percentOfTime));
+ }
+
+ if (d->m_fieldShown[TotalDuration]) {
+ newRow << new EventsViewItem(displayTime(event.duration));
+ newRow.last()->setData(QVariant(event.duration));
+ }
+
+ if (d->m_fieldShown[CallCount]) {
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow.last()->setData(QVariant(event.calls));
+ }
+
+ if (d->m_fieldShown[TimePerCall]) {
+ newRow << new EventsViewItem(displayTime(event.timePerCall));
+ newRow.last()->setData(QVariant(event.timePerCall));
+ }
+
+ if (d->m_fieldShown[MedianTime]) {
+ newRow << new EventsViewItem(displayTime(event.medianTime));
+ newRow.last()->setData(QVariant(event.medianTime));
+ }
+
+ if (d->m_fieldShown[MaxTime]) {
+ newRow << new EventsViewItem(displayTime(event.maxTime));
+ newRow.last()->setData(QVariant(event.maxTime));
+ }
+
+ if (d->m_fieldShown[MinTime]) {
+ newRow << new EventsViewItem(displayTime(event.minTime));
+ newRow.last()->setData(QVariant(event.minTime));
+ }
+
+ if (d->m_fieldShown[Details]) {
+ newRow << new EventsViewItem(event.details);
+ newRow.last()->setData(QVariant(event.details));
+ }
+
+
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QVariant(event.eventHashStr),EventHashStrRole);
+ newRow.at(0)->setData(QVariant(event.location.filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(event.location.line),LineRole);
+ newRow.at(0)->setData(QVariant(event.location.column),ColumnRole);
+// newRow.at(0)->setData(QVariant(event.eventId),EventIdRole);
+// if (event.isBindingLoop)
+// foreach (QStandardItem *item, newRow) {
+// item->setBackground(colors()->bindingLoopBackground);
+// item->setToolTip(tr("Binding loop detected."));
+// }
+
+ // append
+ parentItem->appendRow(newRow);
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+QString QmlProfilerEventsMainView::nameForType(int typeNumber)
+{
+ switch (typeNumber) {
+ case 0: return QmlProfilerEventsMainView::tr("Paint");
+ case 1: return QmlProfilerEventsMainView::tr("Compile");
+ case 2: return QmlProfilerEventsMainView::tr("Create");
+ case 3: return QmlProfilerEventsMainView::tr("Binding");
+ case 4: return QmlProfilerEventsMainView::tr("Signal");
+ }
+ return QString();
+}
+
+void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
+{
+}
+
+bool QmlProfilerEventsMainView::isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const
+{
+}
+
+const QString &QmlProfilerEventsMainView::selectedEventHash() const
+{
+ QModelIndex index = selectedItem();
+ if (!index.isValid())
+ return QString();
+ QStandardItem *item = d->m_model->item(index.row(), 0);
+ return item->data(EventHashStrRole).toString();
+}
+
+
+void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ d->m_preventSelectBounce = true;
+ QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
+ QStandardItem *infoItem;
+ if (clickedItem->parent())
+ infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
+ else
+ infoItem = d->m_model->item(index.row(), 0);
+
+ // show in editor
+ int line = infoItem->data(LineRole).toInt();
+ int column = infoItem->data(ColumnRole).toInt();
+ QString fileName = infoItem->data(FilenameRole).toString();
+ if (line!=-1 && !fileName.isEmpty())
+ emit gotoSourceLocation(fileName, line, column);
+
+ // show in callers/callees subwindow
+ emit eventSelected(infoItem->data(EventHashStrRole).toString());
+
+ // show in timelinerenderer
+ emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
+
+ d->m_preventSelectBounce = false;
+}
+
+void QmlProfilerEventsMainView::selectEvent(const QString &eventHash)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventHashStrRole).toString() == eventHash) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (currentIndex() != d->m_model->indexFromItem(infoItem) && infoItem->data(FilenameRole).toString() == filename && infoItem->data(LineRole).toInt() == line) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+QModelIndex QmlProfilerEventsMainView::selectedItem() const
+{
+ QModelIndexList sel = selectedIndexes();
+ if (sel.isEmpty())
+ return QModelIndex();
+ else
+ return sel.first();
+}
+
+void QmlProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
+ d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
+ return;
+ }
+ }
+}
+
+QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
+{
+ QString str;
+
+ if (recursive) {
+ // indentation
+ QStandardItem *itemParent = item->parent();
+ while (itemParent) {
+ str += QLatin1String(" ");
+ itemParent = itemParent->parent();
+ }
+ }
+
+ // item's data
+ int colCount = m_model->columnCount();
+ for (int j = 0; j < colCount; ++j) {
+ QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
+ str += colItem->data(Qt::DisplayRole).toString();
+ if (j < colCount-1) str += QLatin1Char('\t');
+ }
+ str += QLatin1Char('\n');
+
+ // recursively print children
+ if (recursive && item->child(0))
+ for (int j = 0; j != item->rowCount(); j++)
+ str += textForItem(item->child(j));
+
+ return str;
+}
+
+void QmlProfilerEventsMainView::copyTableToClipboard() const
+{
+ QString str;
+ // headers
+ int columnCount = d->m_model->columnCount();
+ for (int i = 0; i < columnCount; ++i) {
+ str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if (i < columnCount - 1)
+ str += QLatin1Char('\t');
+ else
+ str += QLatin1Char('\n');
+ }
+ // data
+ int rowCount = d->m_model->rowCount();
+ for (int i = 0; i != rowCount; ++i) {
+ str += d->textForItem(d->m_model->item(i));
+ }
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+void QmlProfilerEventsMainView::copyRowToClipboard() const
+{
+ QString str;
+ str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+#ifdef SEE_CHILDREN
+
+QmlProfilerEventsParentsAndChildrenView::~QmlProfilerEventsParentsAndChildrenView()
+{
+}
+
+void QmlProfilerEventsParentsAndChildrenView::setViewType(SubViewType type)
+{
+ m_subtableType = type;
+ updateHeader();
+}
+
+void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
+{
+
+}
+
+void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataModel)
+{
+}
+
+void QmlProfilerEventsParentsAndChildrenView::clear()
+{
+ if (treeModel()) {
+ treeModel()->clear();
+ updateHeader();
+ }
+}
+
+void QmlProfilerEventsParentsAndChildrenView::updateHeader()
+{
+ bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
+ bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
+
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+
+ if (treeModel()) {
+ if (isV8)
+ treeModel()->setColumnCount(3);
+ else
+ treeModel()->setColumnCount(5);
+
+ int columnIndex = 0;
+ if (isChildren)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ if (!isV8)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ if (!isV8)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+ if (isChildren)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ }
+}
+
+QStandardItemModel *QmlProfilerEventsParentsAndChildrenView::treeModel()
+{
+ return qobject_cast<QStandardItemModel *>(model());
+}
+
+void QmlProfilerEventsParentsAndChildrenView::jumpToItem(const QModelIndex &index)
+{
+ if (treeModel()) {
+ QStandardItem *infoItem = treeModel()->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventIdRole).toInt());
+ }
+}
+#else
+class QmlProfilerEventRelativesView::QmlProfilerEventParentsViewPrivate
+{
+public:
+ QmlProfilerEventParentsViewPrivate(QmlProfilerEventRelativesView *qq):q(qq) {}
+ ~QmlProfilerEventParentsViewPrivate() {}
+
+ QmlProfilerEventRelativesModelProxy *modelProxy;
+
+ QmlProfilerEventRelativesView *q;
+};
+
+QmlProfilerEventRelativesView::QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager, QmlProfilerEventRelativesModelProxy *modelProxy, QWidget *parent)
+ : QTreeView(parent), d(new QmlProfilerEventParentsViewPrivate(this))
+{
+ Q_UNUSED(modelManager);
+ //m_profilerModelManager = modelManager;
+ d->modelProxy = modelProxy; //new QmlProfilerEventParentsModelProxy(modelManager, this);
+ setModel(new QStandardItemModel(this));
+ setRootIsDecorated(false);
+ setFrameStyle(QFrame::NoFrame);
+ updateHeader();
+
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+}
+
+QmlProfilerEventRelativesView::~QmlProfilerEventRelativesView()
+{
+ //delete d->modelProxy;
+ delete d;
+}
+
+void QmlProfilerEventRelativesView::displayEvent(const QString &eventHash)
+{
+ // TODO: what if it's not there?
+// QmlProfilerEventParentsModelProxy::QmlEventLinks qmlEvent = d->modelProxy->getData(eventHash);
+ rebuildTree(d->modelProxy->getData(eventHash));
+
+ updateHeader();
+ resizeColumnToContents(0);
+ setSortingEnabled(true);
+ sortByColumn(2);
+}
+
+void QmlProfilerEventRelativesView::rebuildTree(QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap eventMap)
+{
+ Q_ASSERT(treeModel());
+ treeModel()->clear();
+
+ QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
+
+ //foreach (const QmlProfilerEventParentsModelProxy::QmlEventParentData &event, eventMap.values()) {
+ foreach (const QString &key, eventMap.keys()) {
+ const QmlProfilerEventRelativesModelProxy::QmlEventRelativesData &event = eventMap[key];
+ QList<QStandardItem *> newRow;
+
+ // ToDo: here we were going to search for the data in the other modelproxy
+ // maybe we should store the data in this proxy and get it here
+ // no indirections at this level of abstraction!
+ newRow << new EventsViewItem(event.displayName);
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event.eventType));
+ newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event.duration));
+ newRow << new EventsViewItem(QString::number(event.calls));
+ newRow << new EventsViewItem(event.details);
+
+// newRow << new EventsViewItem(event->reference->displayName);
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
+// newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
+// newRow << new EventsViewItem(QString::number(event->calls));
+// newRow << new EventsViewItem(event->reference->details);
+ newRow.at(0)->setData(QVariant(key), EventHashStrRole);
+// newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
+ newRow.at(2)->setData(QVariant(event.duration));
+ newRow.at(3)->setData(QVariant(event.calls));
+// if (event->inLoopPath)
+// foreach (QStandardItem *item, newRow) {
+// item->setBackground(colors()->bindingLoopBackground);
+// item->setToolTip(tr("Part of binding loop."));
+// }
+
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ topLevelItem->appendRow(newRow);
+ }
+}
+
+void QmlProfilerEventRelativesView::clear()
+{
+ if (treeModel()) {
+ treeModel()->clear();
+ updateHeader();
+ }
+}
+
+void QmlProfilerEventRelativesView::updateHeader()
+{
+ bool calleesView = qobject_cast<QmlProfilerEventChildrenModelProxy *>(d->modelProxy) != 0;
+
+ header()->setResizeMode(QHeaderView::Interactive);
+ header()->setDefaultSectionSize(100);
+ header()->setMinimumSectionSize(50);
+
+ if (treeModel()) {
+ treeModel()->setColumnCount(5);
+
+ int columnIndex = 0;
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time")));
+
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
+
+
+ if (calleesView)
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description")));
+ else
+ treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description")));
+ }
+}
+
+QStandardItemModel *QmlProfilerEventRelativesView::treeModel()
+{
+ return qobject_cast<QStandardItemModel *>(model());
+}
+
+void QmlProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
+{
+ if (treeModel()) {
+ QStandardItem *infoItem = treeModel()->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventHashStrRole).toString());
+ }
+}
+
+#endif
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerv8eventsview.h b/src/plugins/qmlprofiler/qmlprofilerv8eventsview.h
new file mode 100644
index 0000000000..c15443b7f2
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilerv8eventsview.h
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QMLPROFILEREVENTVIEW_H
+#define QMLPROFILEREVENTVIEW_H
+
+#include <QTreeView>
+#include <QStandardItemModel>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilereventsmodelproxy.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QmlProfilerEventsMainView;
+class QmlProfilerEventChildrenView;
+class QmlProfilerEventRelativesView;
+
+enum ItemRole {
+ EventHashStrRole = Qt::UserRole+1,
+ FilenameRole = Qt::UserRole+2,
+ LineRole = Qt::UserRole+3,
+ ColumnRole = Qt::UserRole+4,
+ EventIdRole = Qt::UserRole+5
+};
+
+class QmlProfilerEventsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager );
+ ~QmlProfilerEventsWidget();
+
+ void clear();
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ QModelIndex selectedItem() const;
+ bool mouseOnTable(const QPoint &position) const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ bool hasGlobalStats() const;
+ void setShowExtendedStatistics(bool show);
+ bool showExtendedStatistics() const;
+
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void showEventInTimeline(int eventId);
+ void resized();
+
+public slots:
+ void updateSelectedEvent(const QString &eventHash) const;
+ void selectBySourceLocation(const QString &filename, int line, int column);
+
+private slots:
+ void profilerDataModelStateChanged();
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev);
+ virtual void resizeEvent(QResizeEvent *event);
+
+private:
+ class QmlProfilerEventsWidgetPrivate;
+ QmlProfilerEventsWidgetPrivate *d;
+};
+
+class QmlProfilerEventsMainView : public QTreeView
+{
+ Q_OBJECT
+public:
+ enum Fields {
+ Name,
+ Type,
+ Percent,
+ TotalDuration,
+ SelfPercent,
+ SelfDuration,
+ CallCount,
+ TimePerCall,
+ MaxTime,
+ MinTime,
+ MedianTime,
+ Details,
+
+ MaxFields
+ };
+
+ explicit QmlProfilerEventsMainView(QWidget *parent,
+ QmlProfilerEventsModelProxy *modelProxy);
+ ~QmlProfilerEventsMainView();
+
+ void setFieldViewable(Fields field, bool show);
+ void setShowAnonymousEvents( bool showThem );
+
+ QModelIndex selectedItem() const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ static QString displayTime(double time);
+ static QString nameForType(int typeNumber);
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ bool isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const;
+// int selectedEventId() const;
+ const QString &selectedEventHash() const;
+
+ void setShowExtendedStatistics(bool);
+ bool showExtendedStatistics() const;
+
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void eventSelected(const QString &eventHash);
+ void showEventInTimeline(int eventId);
+
+public slots:
+ void clear();
+ void jumpToItem(const QModelIndex &index);
+ void selectEvent(const QString &eventHash);
+ void selectEventByLocation(const QString &filename, int line);
+ void buildModel();
+ void changeDetailsForEvent(int eventId, const QString &newString);
+
+private slots:
+ void profilerDataModelStateChanged();
+
+private:
+ void setHeaderLabels();
+ void parseModelProxy();
+
+private:
+ class QmlProfilerEventsMainViewPrivate;
+ QmlProfilerEventsMainViewPrivate *d;
+
+};
+
+class QmlProfilerEventRelativesView : public QTreeView
+{
+ Q_OBJECT
+public:
+ explicit QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager,
+ QmlProfilerEventRelativesModelProxy *modelProxy,
+ QWidget *parent );
+ ~QmlProfilerEventRelativesView();
+
+signals:
+ void eventClicked(const QString &eventHash);
+
+public slots:
+ void displayEvent(const QString &eventHash);
+ void jumpToItem(const QModelIndex &);
+ void clear();
+
+private:
+ void rebuildTree(QmlProfilerEventParentsModelProxy::QmlEventRelativesMap eventMap);
+ void updateHeader();
+ QStandardItemModel *treeModel();
+// QmlProfilerModelManager *m_profilerModelManager;
+
+ class QmlProfilerEventParentsViewPrivate;
+ QmlProfilerEventParentsViewPrivate *d;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILEREVENTVIEW_H
diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
index 9b9fa32e12..5374b1c3c7 100644
--- a/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.cpp
@@ -33,8 +33,9 @@
#include "qmlprofilereventview.h"
#include "qmlprofilertool.h"
#include "qmlprofilerstatemanager.h"
-#include "qmlprofilerdatamodel.h"
+#include "qmlprofilermodelmanager.h"
#include "qmlprofilerstatewidget.h"
+#include "qv8profilereventview.h"
#include <utils/qtcassert.h>
#include <utils/fancymainwindow.h>
@@ -53,15 +54,15 @@ public:
QmlProfilerTraceView *traceView;
QmlProfilerEventsWidget *eventsView;
- QmlProfilerEventsWidget *v8profilerView;
+ QV8ProfilerEventsWidget *v8profilerView;
QmlProfilerStateManager *profilerState;
- QmlProfilerDataModel *profilerDataModel;
+ QmlProfilerModelManager *profilerModelManager;
QmlProfilerTool *profilerTool;
};
QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
QmlProfilerTool *profilerTool,
- QmlProfilerDataModel *model,
+ QmlProfilerModelManager *modelManager,
QmlProfilerStateManager *profilerState)
: QObject(parent), d(new QmlProfilerViewManagerPrivate(this))
{
@@ -70,7 +71,7 @@ QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
d->eventsView = 0;
d->v8profilerView = 0;
d->profilerState = profilerState;
- d->profilerDataModel = model;
+ d->profilerModelManager = modelManager;
d->profilerTool = profilerTool;
createViews();
}
@@ -84,7 +85,8 @@ QmlProfilerViewManager::~QmlProfilerViewManager()
// Views
void QmlProfilerViewManager::createViews()
{
- QTC_ASSERT(d->profilerDataModel, return);
+
+ QTC_ASSERT(d->profilerModelManager, return);
QTC_ASSERT(d->profilerState, return);
Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
@@ -92,26 +94,28 @@ void QmlProfilerViewManager::createViews()
d->traceView = new QmlProfilerTraceView(mw,
d->profilerTool,
this,
- d->profilerDataModel,
+ d->profilerModelManager,
d->profilerState);
connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
this, SIGNAL(gotoSourceLocation(QString,int,int)));
-
d->traceView->reset();
- d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this, d->profilerDataModel);
+
+ d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this,
+ d->profilerModelManager);
connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this,
SIGNAL(gotoSourceLocation(QString,int,int)));
- connect(d->eventsView, SIGNAL(showEventInTimeline(int)), d->traceView,
- SLOT(selectNextEventWithId(int)));
- connect(d->traceView, SIGNAL(selectedEventChanged(int)), d->eventsView,
- SLOT(updateSelectedEvent(int)));
-
- d->v8profilerView = new QmlProfilerEventsWidget(mw, d->profilerTool,
- this, d->profilerDataModel);
- d->v8profilerView->switchToV8View();
+ connect(d->eventsView, SIGNAL(eventSelectedByHash(QString)), d->traceView,
+ SLOT(selectNextEventByHash(QString)));
+ connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ d->eventsView, SLOT(selectBySourceLocation(QString,int,int)));
+
+ d->v8profilerView = new QV8ProfilerEventsWidget(mw, d->profilerTool, this,
+ d->profilerModelManager);
+ connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
+ d->v8profilerView, SLOT(selectBySourceLocation(QString,int,int)));
connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
- this, SIGNAL(gotoSourceLocation(QString,int,int)));
+ d->traceView, SLOT(selectNextEventByLocation(QString,int,int)));
connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
d->eventsView, SLOT(selectBySourceLocation(QString,int,int)));
connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)),
@@ -121,8 +125,8 @@ void QmlProfilerViewManager::createViews()
(d->profilerTool, tr("Events"), d->eventsView, Qt::BottomDockWidgetArea);
QDockWidget *timelineDock = AnalyzerManager::createDockWidget
(d->profilerTool, tr("Timeline"), d->traceView, Qt::BottomDockWidgetArea);
- QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget
- (d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea);
+ QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget(
+ d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea);
eventsDock->show();
timelineDock->show();
@@ -132,9 +136,9 @@ void QmlProfilerViewManager::createViews()
mw->tabifyDockWidget(timelineDock, eventsDock);
mw->tabifyDockWidget(eventsDock, v8profilerDock);
- new QmlProfilerStateWidget(d->profilerState, d->profilerDataModel, d->traceView);
- new QmlProfilerStateWidget(d->profilerState, d->profilerDataModel, d->eventsView);
- new QmlProfilerStateWidget(d->profilerState, d->profilerDataModel, d->v8profilerView);
+ new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->eventsView);
+ new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->traceView);
+ new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->v8profilerView);
}
bool QmlProfilerViewManager::hasValidSelection() const
diff --git a/src/plugins/qmlprofiler/qmlprofilerviewmanager.h b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
index 8b4b2bda8d..122e5bb644 100644
--- a/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
+++ b/src/plugins/qmlprofiler/qmlprofilerviewmanager.h
@@ -33,10 +33,11 @@
#include <QObject>
namespace QmlProfiler {
+class QmlProfilerModelManager;
+
namespace Internal {
class QmlProfilerTool;
-class QmlProfilerDataModel;
class QmlProfilerStateManager;
class QmlProfilerViewManager : public QObject
@@ -45,7 +46,7 @@ class QmlProfilerViewManager : public QObject
public:
explicit QmlProfilerViewManager(QObject *parent,
QmlProfilerTool *profilerTool,
- QmlProfilerDataModel *model,
+ QmlProfilerModelManager *modelManager,
QmlProfilerStateManager *profilerState);
~QmlProfilerViewManager();
diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
index 482677d31d..406d010e80 100644
--- a/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
+++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.cpp
@@ -28,12 +28,9 @@
****************************************************************************/
#include "qv8profilerdatamodel.h"
-#include "qmlprofilerdatamodel.h"
#include <QStringList>
-using namespace QmlDebug;
-
QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventData, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventSub, Q_MOVABLE_TYPE);
@@ -66,7 +63,7 @@ QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
totalTime = ref.totalTime;
totalPercent = ref.totalPercent;
selfTime = ref.selfTime;
- selfPercent = ref.selfPercent;
+ SelfTimeInPercent = ref.SelfTimeInPercent;
eventId = ref.eventId;
qDeleteAll(parentHash);
@@ -85,7 +82,7 @@ QV8EventData::QV8EventData()
totalTime = 0;
selfTime = 0;
totalPercent = 0;
- selfPercent = 0;
+ SelfTimeInPercent = 0;
}
QV8EventData::~QV8EventData()
@@ -101,8 +98,6 @@ class QV8ProfilerDataModel::QV8ProfilerDataModelPrivate
public:
QV8ProfilerDataModelPrivate(QV8ProfilerDataModel *qq) {Q_UNUSED(qq);}
- QmlProfilerDataModel *qmlProfilerDataModel;
-
void clearV8RootEvent();
void collectV8Statistics();
@@ -112,13 +107,12 @@ public:
qint64 v8MeasuredTime;
};
-QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent,
- QmlProfilerDataModel *profilerDataModel)
- : QObject(parent), d(new QV8ProfilerDataModelPrivate(this))
+QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent)
+ : QObject(parent)
+ , d(new QV8ProfilerDataModelPrivate(this))
{
d->v8MeasuredTime = 0;
d->clearV8RootEvent();
- d->qmlProfilerDataModel = profilerDataModel;
}
QV8ProfilerDataModel::~QV8ProfilerDataModel()
@@ -133,6 +127,8 @@ void QV8ProfilerDataModel::clear()
d->v8parents.clear();
d->clearV8RootEvent();
d->v8MeasuredTime = 0;
+
+ emit changed();
}
bool QV8ProfilerDataModel::isEmpty() const
@@ -159,6 +155,11 @@ QList<QV8EventData *> QV8ProfilerDataModel::getV8Events() const
return d->v8EventHash.values();
}
+QString getHashStringForV8Event(const QString &displayName, const QString &function)
+{
+ return QString::fromLatin1("%1:%2").arg(displayName, function);
+}
+
void QV8ProfilerDataModel::addV8Event(int depth,
const QString &function,
const QString &filename,
@@ -168,9 +169,7 @@ void QV8ProfilerDataModel::addV8Event(int depth,
{
QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) +
QLatin1Char(':') + QString::number(lineNumber);
- QString hashStr = QmlProfilerDataModel::getHashStringForV8Event(displayName, function);
-
- d->qmlProfilerDataModel->setState(QmlProfilerDataModel::AcquiringData);
+ QString hashStr = getHashStringForV8Event(displayName, function);
// time is given in milliseconds, but internally we store it in microseconds
totalTime *= 1e6;
@@ -223,6 +222,7 @@ void QV8ProfilerDataModel::addV8Event(int depth,
newChildSub->totalTime += totalTime;
}
}
+
}
void QV8ProfilerDataModel::collectV8Statistics()
@@ -250,9 +250,9 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
v8RootEvent.totalTime = v8MeasuredTime + 1;
v8RootEvent.selfTime = 0;
- QString rootEventHash = QmlProfilerDataModel::getHashStringForV8Event(
- QmlProfilerDataModel::rootEventName(),
- QmlProfilerDataModel::rootEventDescription());
+ QString rootEventHash = getHashStringForV8Event(
+ tr("<program>"),
+ tr("Main Program"));
QV8EventData *v8RootEventPointer = v8EventHash[rootEventHash];
if (v8RootEventPointer) {
v8RootEvent = *v8RootEventPointer;
@@ -263,7 +263,7 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
foreach (QV8EventData *v8event, v8EventHash.values()) {
v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
- v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes;
+ v8event->SelfTimeInPercent = v8event->selfTime * 100.0 / selfTimes;
}
int index = 0;
@@ -274,25 +274,20 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
} else {
// On empty data, still add a fake root event
clearV8RootEvent();
- v8RootEvent.totalPercent = 100;
- QString rootEventHash = QmlProfilerDataModel::getHashStringForV8Event(
- QmlProfilerDataModel::rootEventName(),
- QmlProfilerDataModel::rootEventDescription());
- v8EventHash[rootEventHash] = new QV8EventData;
- *v8EventHash[rootEventHash] = v8RootEvent;
}
}
void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::clearV8RootEvent()
{
- v8RootEvent.displayName = QmlProfilerDataModel::rootEventName();
- v8RootEvent.eventHashStr = QmlProfilerDataModel::rootEventName();
- v8RootEvent.functionName = QmlProfilerDataModel::rootEventDescription();
+ v8RootEvent.displayName = tr("<program>");
+ v8RootEvent.eventHashStr = tr("<program>");
+ v8RootEvent.functionName = tr("Main Program");
+
v8RootEvent.line = -1;
v8RootEvent.totalTime = 0;
v8RootEvent.totalPercent = 0;
v8RootEvent.selfTime = 0;
- v8RootEvent.selfPercent = 0;
+ v8RootEvent.SelfTimeInPercent = 0;
v8RootEvent.eventId = -1;
qDeleteAll(v8RootEvent.parentHash.values());
@@ -439,7 +434,7 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
}
default: break;
}
-}
+ }
// backwards compatibility
if (d->v8MeasuredTime == 0)
@@ -472,11 +467,10 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
}
}
}
-
// store v8 events
foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) {
storedV8Event->eventHashStr =
- QmlProfilerDataModel::getHashStringForV8Event(
+ getHashStringForV8Event(
storedV8Event->displayName, storedV8Event->functionName);
d->v8EventHash[storedV8Event->eventHashStr] = storedV8Event;
}
@@ -485,5 +479,11 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
}
+void QV8ProfilerDataModel::complete()
+{
+ collectV8Statistics();
+ emit changed();
+}
+
} // namespace Internal
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qv8profilerdatamodel.h b/src/plugins/qmlprofiler/qv8profilerdatamodel.h
index b4430613d0..633c2107f2 100644
--- a/src/plugins/qmlprofiler/qv8profilerdatamodel.h
+++ b/src/plugins/qmlprofiler/qv8profilerdatamodel.h
@@ -39,7 +39,6 @@
namespace QmlProfiler {
namespace Internal {
-class QmlProfilerDataModel;
struct QV8EventSub;
struct QV8EventData
@@ -55,7 +54,7 @@ struct QV8EventData
double totalTime; // given in milliseconds
double totalPercent;
double selfTime;
- double selfPercent;
+ double SelfTimeInPercent;
QHash <QString, QV8EventSub *> parentHash;
QHash <QString, QV8EventSub *> childrenHash;
int eventId;
@@ -75,7 +74,7 @@ class QV8ProfilerDataModel : public QObject
{
Q_OBJECT
public:
- explicit QV8ProfilerDataModel(QObject *parent, QmlProfilerDataModel *profilerDataModel);
+ QV8ProfilerDataModel(QObject *parent = 0);
~QV8ProfilerDataModel();
void clear();
@@ -89,6 +88,11 @@ public:
void save(QXmlStreamWriter &stream);
void load(QXmlStreamReader &stream);
+ void complete();
+
+signals:
+ void changed();
+
public slots:
void addV8Event(int depth,
const QString &function,
diff --git a/src/plugins/qmlprofiler/qv8profilereventview.cpp b/src/plugins/qmlprofiler/qv8profilereventview.cpp
new file mode 100644
index 0000000000..fdc3ee9592
--- /dev/null
+++ b/src/plugins/qmlprofiler/qv8profilereventview.cpp
@@ -0,0 +1,725 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "qv8profilereventview.h"
+
+#include <QUrl>
+#include <QHash>
+
+#include <QStandardItem>
+#include <QHeaderView>
+
+#include <QApplication>
+#include <QClipboard>
+
+#include <QContextMenuEvent>
+#include <QDebug>
+
+#include <coreplugin/minisplitter.h>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+
+#include "qmlprofilerviewmanager.h"
+#include "qmlprofilertool.h"
+#include "qv8profilerdatamodel.h"
+#include <QMenu>
+
+#include <utils/qtcassert.h>
+
+using namespace QmlDebug;
+
+namespace QmlProfiler {
+namespace Internal {
+
+enum ItemRole {
+ EventHashStrRole = Qt::UserRole+1,
+ FilenameRole = Qt::UserRole+2,
+ LineRole = Qt::UserRole+3,
+ ColumnRole = Qt::UserRole+4,
+ EventIdRole = Qt::UserRole+5
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class EventsViewItem : public QStandardItem
+{
+public:
+ EventsViewItem(const QString &text) : QStandardItem(text) {}
+
+ virtual bool operator<(const QStandardItem &other) const
+ {
+ if (data().type() == QVariant::String) {
+ // first column
+ if (column() == 0) {
+ return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
+ data(LineRole).toInt() < other.data(LineRole).toInt() :
+ data(FilenameRole).toString() < other.data(FilenameRole).toString();
+ } else {
+ return data().toString().toLower() < other.data().toString().toLower();
+ }
+ }
+
+ return data().toDouble() < other.data().toDouble();
+ }
+};
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QV8ProfilerEventsWidget::QV8ProfilerEventsWidgetPrivate
+{
+public:
+ QV8ProfilerEventsWidgetPrivate(QV8ProfilerEventsWidget *qq):q(qq) {}
+ ~QV8ProfilerEventsWidgetPrivate() {}
+
+ QV8ProfilerEventsWidget *q;
+
+ Analyzer::IAnalyzerTool *m_profilerTool;
+ QmlProfilerViewManager *m_viewContainer;
+
+ QV8ProfilerEventsMainView *m_eventTree;
+ QV8ProfilerEventRelativesView *m_eventChildren;
+ QV8ProfilerEventRelativesView *m_eventParents;
+
+ QV8ProfilerDataModel *v8Model;
+};
+
+QV8ProfilerEventsWidget::QV8ProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager )
+ : QWidget(parent), d(new QV8ProfilerEventsWidgetPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerV8ProfileView"));
+
+ d->v8Model = profilerModelManager->v8Model();
+
+ d->m_eventTree = new QV8ProfilerEventsMainView(this, d->v8Model);
+ connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
+
+ d->m_eventChildren = new QV8ProfilerEventRelativesView(d->v8Model,
+ QV8ProfilerEventRelativesView::ChildrenView,
+ this);
+ d->m_eventParents = new QV8ProfilerEventRelativesView(d->v8Model,
+ QV8ProfilerEventRelativesView::ParentsView,
+ this);
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
+ connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
+ connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+ connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
+
+ // widget arrangement
+ QVBoxLayout *groupLayout = new QVBoxLayout;
+ groupLayout->setContentsMargins(0,0,0,0);
+ groupLayout->setSpacing(0);
+
+ Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
+ splitterVertical->addWidget(d->m_eventTree);
+ Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
+ splitterHorizontal->addWidget(d->m_eventParents);
+ splitterHorizontal->addWidget(d->m_eventChildren);
+ splitterHorizontal->setOrientation(Qt::Horizontal);
+ splitterVertical->addWidget(splitterHorizontal);
+ splitterVertical->setOrientation(Qt::Vertical);
+ splitterVertical->setStretchFactor(0,5);
+ splitterVertical->setStretchFactor(1,2);
+ groupLayout->addWidget(splitterVertical);
+ setLayout(groupLayout);
+
+ d->m_profilerTool = profilerTool;
+ d->m_viewContainer = container;
+
+}
+
+QV8ProfilerEventsWidget::~QV8ProfilerEventsWidget()
+{
+ delete d;
+}
+
+void QV8ProfilerEventsWidget::clear()
+{
+ d->m_eventTree->clear();
+ d->m_eventChildren->clear();
+ d->m_eventParents->clear();
+}
+
+QModelIndex QV8ProfilerEventsWidget::selectedItem() const
+{
+ return d->m_eventTree->selectedItem();
+}
+
+void QV8ProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QTC_ASSERT(d->m_viewContainer, return;);
+
+ QMenu menu;
+ QAction *copyRowAction = 0;
+ QAction *copyTableAction = 0;
+
+ QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
+ QPoint position = ev->globalPos();
+
+ if (profilerTool) {
+ QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
+ foreach (QAction *act, commonActions) {
+ menu.addAction(act);
+ }
+ }
+
+ if (mouseOnTable(position)) {
+ menu.addSeparator();
+ if (selectedItem().isValid())
+ copyRowAction = menu.addAction(QCoreApplication::translate("QmlProfiler::Internal::QmlProfilerEventsWidget", "Copy Row"));
+ copyTableAction = menu.addAction(QCoreApplication::translate("QmlProfiler::Internal::QmlProfilerEventsWidget", "Copy Table"));
+ }
+
+ QAction *selectedAction = menu.exec(position);
+
+ if (selectedAction) {
+ if (selectedAction == copyRowAction)
+ copyRowToClipboard();
+ if (selectedAction == copyTableAction)
+ copyTableToClipboard();
+ }
+}
+
+void QV8ProfilerEventsWidget::resizeEvent(QResizeEvent *event)
+{
+ QWidget::resizeEvent(event);
+ emit resized();
+}
+
+bool QV8ProfilerEventsWidget::mouseOnTable(const QPoint &position) const
+{
+ QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
+ QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
+ return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
+}
+
+void QV8ProfilerEventsWidget::copyTableToClipboard() const
+{
+ d->m_eventTree->copyTableToClipboard();
+}
+
+void QV8ProfilerEventsWidget::copyRowToClipboard() const
+{
+ d->m_eventTree->copyRowToClipboard();
+}
+
+void QV8ProfilerEventsWidget::updateSelectedEvent(int eventId) const
+{
+ if (d->m_eventTree->selectedEventId() != eventId)
+ d->m_eventTree->selectEvent(eventId);
+}
+
+void QV8ProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
+{
+ // This slot is used to connect the javascript pane with the qml events pane
+ // Our javascript trace data does not store column information
+ // thus we ignore it here
+ Q_UNUSED(column);
+ d->m_eventTree->selectEventByLocation(filename, line);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+class QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate
+{
+public:
+ QV8ProfilerEventsMainViewPrivate(QV8ProfilerEventsMainView *qq) : q(qq) {}
+
+ void buildV8ModelFromList( const QList<QV8EventData *> &list );
+ int getFieldCount();
+
+ QString textForItem(QStandardItem *item, bool recursive) const;
+
+
+ QV8ProfilerEventsMainView *q;
+
+ QV8ProfilerDataModel *m_v8Model;
+ QStandardItemModel *m_model;
+ QList<bool> m_fieldShown;
+ QHash<int, int> m_columnIndex; // maps field enum to column index
+ int m_firstNumericColumn;
+ bool m_preventSelectBounce;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QV8ProfilerEventsMainView::QV8ProfilerEventsMainView(QWidget *parent,
+ QV8ProfilerDataModel *v8Model)
+: QmlProfilerTreeView(parent), d(new QV8ProfilerEventsMainViewPrivate(this))
+{
+ setObjectName(QLatin1String("QmlProfilerEventsTable"));
+ setSortingEnabled(false);
+
+ d->m_model = new QStandardItemModel(this);
+ setModel(d->m_model);
+ connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
+
+ d->m_v8Model = v8Model;
+ connect(d->m_v8Model, SIGNAL(changed()), this, SLOT(buildModel()));
+ d->m_firstNumericColumn = 0;
+ d->m_preventSelectBounce = false;
+
+ setFieldViewable(Name, true);
+ setFieldViewable(Type, false);
+ setFieldViewable(TimeInPercent, true);
+ setFieldViewable(TotalTime, true);
+ setFieldViewable(SelfTimeInPercent, true);
+ setFieldViewable(SelfTime, true);
+ setFieldViewable(CallCount, false);
+ setFieldViewable(TimePerCall, false);
+ setFieldViewable(MaxTime, false);
+ setFieldViewable(MinTime, false);
+ setFieldViewable(MedianTime, false);
+ setFieldViewable(Details, true);
+
+ buildModel();
+}
+
+QV8ProfilerEventsMainView::~QV8ProfilerEventsMainView()
+{
+ clear();
+ delete d->m_model;
+ delete d;
+}
+
+void QV8ProfilerEventsMainView::setFieldViewable(Fields field, bool show)
+{
+ if (field < MaxFields) {
+ int length = d->m_fieldShown.count();
+ if (field >= length) {
+ for (int i=length; i<MaxFields; i++)
+ d->m_fieldShown << false;
+ }
+ d->m_fieldShown[field] = show;
+ }
+}
+
+
+void QV8ProfilerEventsMainView::setHeaderLabels()
+{
+ int fieldIndex = 0;
+ d->m_firstNumericColumn = 0;
+
+ d->m_columnIndex.clear();
+ if (d->m_fieldShown[Name]) {
+ d->m_columnIndex[Name] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Location)));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[Type]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
+ d->m_firstNumericColumn++;
+ }
+ if (d->m_fieldShown[TimeInPercent]) {
+ d->m_columnIndex[TimeInPercent] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimeInPercent)));
+ }
+ if (d->m_fieldShown[TotalTime]) {
+ d->m_columnIndex[TotalTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
+ }
+ if (d->m_fieldShown[SelfTimeInPercent]) {
+ d->m_columnIndex[Type] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTimeInPercent)));
+ }
+ if (d->m_fieldShown[SelfTime]) {
+ d->m_columnIndex[SelfTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTime)));
+ }
+ if (d->m_fieldShown[CallCount]) {
+ d->m_columnIndex[CallCount] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
+ }
+ if (d->m_fieldShown[TimePerCall]) {
+ d->m_columnIndex[TimePerCall] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimePerCall)));
+ }
+ if (d->m_fieldShown[MedianTime]) {
+ d->m_columnIndex[MedianTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MedianTime)));
+ }
+ if (d->m_fieldShown[MaxTime]) {
+ d->m_columnIndex[MaxTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MaxTime)));
+ }
+ if (d->m_fieldShown[MinTime]) {
+ d->m_columnIndex[MinTime] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MinTime)));
+ }
+ if (d->m_fieldShown[Details]) {
+ d->m_columnIndex[Details] = fieldIndex;
+ d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Details)));
+ }
+}
+
+void QV8ProfilerEventsMainView::clear()
+{
+ d->m_model->clear();
+ d->m_model->setColumnCount(d->getFieldCount());
+
+ setHeaderLabels();
+ setSortingEnabled(false);
+}
+
+int QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::getFieldCount()
+{
+ int count = 0;
+ for (int i=0; i < m_fieldShown.count(); ++i)
+ if (m_fieldShown[i])
+ count++;
+ return count;
+}
+
+void QV8ProfilerEventsMainView::buildModel()
+{
+ clear();
+ d->buildV8ModelFromList( d->m_v8Model->getV8Events() );
+
+ setRootIsDecorated(false);
+ setSortingEnabled(true);
+ sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
+
+ expandAll();
+ if (d->m_fieldShown[Name])
+ resizeColumnToContents(0);
+
+ if (d->m_fieldShown[Type])
+ resizeColumnToContents(d->m_fieldShown[Name]?1:0);
+ collapseAll();
+}
+
+void QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
+{
+ for (int index = 0; index < list.count(); index++) {
+ QV8EventData *v8event = list.at(index);
+ QList<QStandardItem *> newRow;
+
+ if (m_fieldShown[Name])
+ newRow << new EventsViewItem(v8event->displayName);
+
+ if (m_fieldShown[TimeInPercent]) {
+ newRow << new EventsViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(v8event->totalPercent));
+ }
+
+ if (m_fieldShown[TotalTime]) {
+ newRow << new EventsViewItem(displayTime(v8event->totalTime));
+ newRow.last()->setData(QVariant(v8event->totalTime));
+ }
+
+ if (m_fieldShown[SelfTimeInPercent]) {
+ newRow << new EventsViewItem(QString::number(v8event->SelfTimeInPercent,'f',2)+QLatin1String(" %"));
+ newRow.last()->setData(QVariant(v8event->SelfTimeInPercent));
+ }
+
+ if (m_fieldShown[SelfTime]) {
+ newRow << new EventsViewItem(displayTime(v8event->selfTime));
+ newRow.last()->setData(QVariant(v8event->selfTime));
+ }
+
+ if (m_fieldShown[Details]) {
+ newRow << new EventsViewItem(v8event->functionName);
+ newRow.last()->setData(QVariant(v8event->functionName));
+ }
+
+ if (!newRow.isEmpty()) {
+ // no edit
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ // metadata
+ newRow.at(0)->setData(QString::fromLatin1("%1:%2").arg(v8event->filename, QString::number(v8event->line)), EventHashStrRole);
+ newRow.at(0)->setData(QVariant(v8event->filename), FilenameRole);
+ newRow.at(0)->setData(QVariant(v8event->line), LineRole);
+ newRow.at(0)->setData(QVariant(-1),ColumnRole); // v8 events have no column info
+ newRow.at(0)->setData(QVariant(v8event->eventId), EventIdRole);
+
+ // append
+ m_model->invisibleRootItem()->appendRow(newRow);
+ }
+ }
+}
+
+QString QV8ProfilerEventsMainView::displayTime(double time)
+{
+ if (time < 1e6)
+ return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
+ if (time < 1e9)
+ return QString::number(time/1e6,'f',3) + tr(" ms");
+
+ return QString::number(time/1e9,'f',3) + tr(" s");
+}
+
+QString QV8ProfilerEventsMainView::nameForType(int typeNumber)
+{
+ switch (typeNumber) {
+ case 0: return QV8ProfilerEventsMainView::tr("Paint");
+ case 1: return QV8ProfilerEventsMainView::tr("Compile");
+ case 2: return QV8ProfilerEventsMainView::tr("Create");
+ case 3: return QV8ProfilerEventsMainView::tr("Binding");
+ case 4: return QV8ProfilerEventsMainView::tr("Signal");
+ }
+ return QString();
+}
+
+int QV8ProfilerEventsMainView::selectedEventId() const
+{
+ QModelIndex index = selectedItem();
+ if (!index.isValid())
+ return -1;
+ QStandardItem *item = d->m_model->item(index.row(), 0);
+ return item->data(EventIdRole).toInt();
+}
+
+void QV8ProfilerEventsMainView::jumpToItem(const QModelIndex &index)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ d->m_preventSelectBounce = true;
+ QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
+ QStandardItem *infoItem;
+ if (clickedItem->parent())
+ infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
+ else
+ infoItem = d->m_model->item(index.row(), 0);
+
+ // show in editor
+ int line = infoItem->data(LineRole).toInt();
+ int column = infoItem->data(ColumnRole).toInt();
+ QString fileName = infoItem->data(FilenameRole).toString();
+ if (line!=-1 && !fileName.isEmpty())
+ emit gotoSourceLocation(fileName, line, column);
+
+ // show in callers/callees subwindow
+ emit eventSelected(infoItem->data(EventIdRole).toInt());
+
+ d->m_preventSelectBounce = false;
+}
+
+void QV8ProfilerEventsMainView::selectEvent(int eventId)
+{
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (infoItem->data(EventIdRole).toInt() == eventId) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+void QV8ProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
+{
+ if (d->m_preventSelectBounce)
+ return;
+
+ for (int i=0; i<d->m_model->rowCount(); i++) {
+ QStandardItem *infoItem = d->m_model->item(i, 0);
+ if (currentIndex() != d->m_model->indexFromItem(infoItem) &&
+ infoItem->data(FilenameRole).toString() == filename &&
+ infoItem->data(LineRole).toInt() == line) {
+ setCurrentIndex(d->m_model->indexFromItem(infoItem));
+ jumpToItem(currentIndex());
+ return;
+ }
+ }
+}
+
+QModelIndex QV8ProfilerEventsMainView::selectedItem() const
+{
+ QModelIndexList sel = selectedIndexes();
+ if (sel.isEmpty())
+ return QModelIndex();
+ else
+ return sel.first();
+}
+
+QString QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
+{
+ QString str;
+
+ if (recursive) {
+ // indentation
+ QStandardItem *itemParent = item->parent();
+ while (itemParent) {
+ str += QLatin1String(" ");
+ itemParent = itemParent->parent();
+ }
+ }
+
+ // item's data
+ int colCount = m_model->columnCount();
+ for (int j = 0; j < colCount; ++j) {
+ QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
+ str += colItem->data(Qt::DisplayRole).toString();
+ if (j < colCount-1) str += QLatin1Char('\t');
+ }
+ str += QLatin1Char('\n');
+
+ // recursively print children
+ if (recursive && item->child(0))
+ for (int j = 0; j != item->rowCount(); j++)
+ str += textForItem(item->child(j));
+
+ return str;
+}
+
+void QV8ProfilerEventsMainView::copyTableToClipboard() const
+{
+ QString str;
+ // headers
+ int columnCount = d->m_model->columnCount();
+ for (int i = 0; i < columnCount; ++i) {
+ str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
+ if (i < columnCount - 1)
+ str += QLatin1Char('\t');
+ else
+ str += QLatin1Char('\n');
+ }
+ // data
+ int rowCount = d->m_model->rowCount();
+ for (int i = 0; i != rowCount; ++i) {
+ str += d->textForItem(d->m_model->item(i));
+ }
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+void QV8ProfilerEventsMainView::copyRowToClipboard() const
+{
+ QString str;
+ str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
+
+ QClipboard *clipboard = QApplication::clipboard();
+ clipboard->setText(str, QClipboard::Selection);
+ clipboard->setText(str, QClipboard::Clipboard);
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+QV8ProfilerEventRelativesView::QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model,
+ SubViewType viewType,
+ QWidget *parent)
+ : QmlProfilerTreeView(parent)
+ , m_type(viewType)
+ , m_v8Model(model)
+ , m_model(new QStandardItemModel(this))
+{
+ setModel(m_model);
+
+ updateHeader();
+ setSortingEnabled(false);
+
+ connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(jumpToItem(QModelIndex)));
+}
+
+QV8ProfilerEventRelativesView::~QV8ProfilerEventRelativesView()
+{
+}
+
+void QV8ProfilerEventRelativesView::displayEvent(int index)
+{
+ QV8EventData *event = m_v8Model->v8EventDescription(index);
+ QTC_CHECK(event);
+
+ QList<QV8EventSub*> events;
+ if (m_type == ParentsView)
+ events = event->parentHash.values();
+ else
+ events = event->childrenHash.values();
+
+ rebuildTree(events);
+
+ updateHeader();
+ resizeColumnToContents(0);
+ setSortingEnabled(true);
+ sortByColumn(1);
+}
+
+void QV8ProfilerEventRelativesView::rebuildTree(QList<QV8EventSub*> events)
+{
+ clear();
+
+ QStandardItem *topLevelItem = m_model->invisibleRootItem();
+
+ foreach (QV8EventSub *event, events) {
+ QList<QStandardItem *> newRow;
+ newRow << new EventsViewItem(event->reference->displayName);
+ newRow << new EventsViewItem(QV8ProfilerEventsMainView::displayTime(event->totalTime));
+ newRow << new EventsViewItem(event->reference->functionName);
+ newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
+ newRow.at(1)->setData(QVariant(event->totalTime));
+
+ foreach (QStandardItem *item, newRow)
+ item->setEditable(false);
+
+ topLevelItem->appendRow(newRow);
+ }
+}
+
+void QV8ProfilerEventRelativesView::clear()
+{
+ m_model->clear();
+}
+
+void QV8ProfilerEventRelativesView::updateHeader()
+{
+ m_model->setColumnCount(3);
+
+ int columnIndex = 0;
+ if (m_type == ChildrenView)
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Callee)));
+ else
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Caller)));
+
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
+
+ if (m_type == ChildrenView)
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CalleeDescription)));
+ else
+ m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallerDescription)));
+}
+
+void QV8ProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
+{
+ QStandardItem *infoItem = m_model->item(index.row(), 0);
+ emit eventClicked(infoItem->data(EventIdRole).toInt());
+}
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qv8profilereventview.h b/src/plugins/qmlprofiler/qv8profilereventview.h
new file mode 100644
index 0000000000..d32b499c1c
--- /dev/null
+++ b/src/plugins/qmlprofiler/qv8profilereventview.h
@@ -0,0 +1,163 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef QV8PROFILEREVENTVIEW_H
+#define QV8PROFILEREVENTVIEW_H
+
+#include <QStandardItemModel>
+#include <qmldebug/qmlprofilereventtypes.h>
+#include "qmlprofilermodelmanager.h"
+#include "qmlprofilereventsmodelproxy.h"
+#include "qmlprofilertreeview.h"
+
+#include <analyzerbase/ianalyzertool.h>
+
+#include "qmlprofilerviewmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class QV8ProfilerEventsMainView;
+class QV8ProfilerEventRelativesView;
+struct QV8EventSub;
+
+
+class QV8ProfilerEventsWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit QV8ProfilerEventsWidget(QWidget *parent,
+ Analyzer::IAnalyzerTool *profilerTool,
+ QmlProfilerViewManager *container,
+ QmlProfilerModelManager *profilerModelManager );
+ ~QV8ProfilerEventsWidget();
+
+ void clear();
+
+ void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
+ QModelIndex selectedItem() const;
+ bool mouseOnTable(const QPoint &position) const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void showEventInTimeline(int eventId);
+ void resized();
+
+public slots:
+ void updateSelectedEvent(int eventId) const;
+ void selectBySourceLocation(const QString &filename, int line, int column);
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev);
+ virtual void resizeEvent(QResizeEvent *event);
+
+private:
+ class QV8ProfilerEventsWidgetPrivate;
+ QV8ProfilerEventsWidgetPrivate *d;
+};
+
+class QV8ProfilerEventsMainView : public QmlProfilerTreeView
+{
+ Q_OBJECT
+public:
+
+ explicit QV8ProfilerEventsMainView(QWidget *parent,
+ QV8ProfilerDataModel *v8Model);
+ ~QV8ProfilerEventsMainView();
+
+ void setFieldViewable(Fields field, bool show);
+ void setShowAnonymousEvents( bool showThem );
+
+ QModelIndex selectedItem() const;
+ void copyTableToClipboard() const;
+ void copyRowToClipboard() const;
+
+ static QString displayTime(double time);
+ static QString nameForType(int typeNumber);
+
+ int selectedEventId() const;
+
+ void setShowExtendedStatistics(bool);
+ bool showExtendedStatistics() const;
+
+signals:
+ void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
+ void eventSelected(int eventId);
+
+public slots:
+ void clear();
+ void jumpToItem(const QModelIndex &index);
+ void selectEvent(int eventId);
+ void selectEventByLocation(const QString &filename, int line);
+ void buildModel();
+
+private:
+ void setHeaderLabels();
+
+private:
+ class QV8ProfilerEventsMainViewPrivate;
+ QV8ProfilerEventsMainViewPrivate *d;
+};
+
+class QV8ProfilerEventRelativesView : public QmlProfilerTreeView
+{
+ Q_OBJECT
+public:
+ enum SubViewType {
+ ParentsView,
+ ChildrenView
+ };
+
+ QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model, SubViewType viewType,
+ QWidget *parent);
+ ~QV8ProfilerEventRelativesView();
+
+signals:
+ void eventClicked(int eventId);
+
+public slots:
+ void displayEvent(int eventId);
+ void jumpToItem(const QModelIndex &);
+ void clear();
+
+private:
+ void rebuildTree(QList<QV8EventSub*> events);
+ void updateHeader();
+
+ QV8ProfilerEventRelativesView::SubViewType m_type;
+ QV8ProfilerDataModel *m_v8Model;
+ QStandardItemModel *m_model;
+};
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QV8PROFILEREVENTVIEW_H
diff --git a/src/plugins/qmlprofiler/timelinemodelaggregator.cpp b/src/plugins/qmlprofiler/timelinemodelaggregator.cpp
new file mode 100644
index 0000000000..ccdf84dd5f
--- /dev/null
+++ b/src/plugins/qmlprofiler/timelinemodelaggregator.cpp
@@ -0,0 +1,373 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "timelinemodelaggregator.h"
+
+#include "qmlprofilertimelinemodelproxy.h"
+#include "qmlprofilerpainteventsmodelproxy.h"
+#include "qmlprofilerplugin.h"
+
+#include <QStringList>
+#include <QVariant>
+
+namespace QmlProfiler {
+namespace Internal {
+
+
+class TimelineModelAggregator::TimelineModelAggregatorPrivate {
+public:
+ TimelineModelAggregatorPrivate(TimelineModelAggregator *qq):q(qq) {}
+ ~TimelineModelAggregatorPrivate() {}
+
+ TimelineModelAggregator *q;
+
+ int basicModelIndex;
+ QList <AbstractTimelineModel *> modelList;
+ QmlProfilerModelManager *modelManager;
+};
+
+TimelineModelAggregator::TimelineModelAggregator(QObject *parent)
+ : QObject(parent), d(new TimelineModelAggregatorPrivate(this))
+{
+}
+
+TimelineModelAggregator::~TimelineModelAggregator()
+{
+ delete d;
+}
+
+void TimelineModelAggregator::setModelManager(QmlProfilerModelManager *modelManager)
+{
+ d->modelManager = modelManager;
+ connect(modelManager,SIGNAL(stateChanged()),this,SLOT(dataChanged()));
+ connect(modelManager,SIGNAL(countChanged()),this,SIGNAL(countChanged()));
+ connect(modelManager,SIGNAL(dataAvailable()),this,SIGNAL(dataAvailable()));
+
+ // external models pushed on top
+ foreach (AbstractTimelineModel *timelineModel, QmlProfilerPlugin::instance->getModels()) {
+ timelineModel->setModelManager(modelManager);
+ addModel(timelineModel);
+ }
+
+ PaintEventsModelProxy *paintEventsModelProxy = new PaintEventsModelProxy(this);
+ paintEventsModelProxy->setModelManager(modelManager);
+ addModel(paintEventsModelProxy);
+
+ BasicTimelineModel *basicTimelineModel = new BasicTimelineModel(this);
+ basicTimelineModel->setModelManager(modelManager);
+ addModel(basicTimelineModel);
+ // the basic model is the last one here
+ d->basicModelIndex = d->modelList.count() - 1;
+
+}
+
+void TimelineModelAggregator::addModel(AbstractTimelineModel *m)
+{
+ d->modelList << m;
+ connect(m,SIGNAL(countChanged()),this,SIGNAL(countChanged()));
+ connect(m,SIGNAL(emptyChanged()),this,SIGNAL(emptyChanged()));
+ connect(m,SIGNAL(expandedChanged()),this,SIGNAL(expandedChanged()));
+ connect(m,SIGNAL(stateChanged()),this,SIGNAL(stateChanged()));
+}
+
+// order?
+int TimelineModelAggregator::categories() const
+{
+ int categoryCount = 0;
+ foreach (const AbstractTimelineModel *modelProxy, d->modelList)
+ categoryCount += modelProxy->categories();
+ return categoryCount;
+}
+
+int TimelineModelAggregator::visibleCategories() const
+{
+ int categoryCount = 0;
+ foreach (const AbstractTimelineModel *modelProxy, d->modelList) {
+ for (int i = 0; i < modelProxy->categories(); i++)
+ if (modelProxy->categoryDepth(i) > 0)
+ categoryCount ++;
+ }
+ return categoryCount;
+}
+
+QStringList TimelineModelAggregator::categoryTitles() const
+{
+ QStringList retString;
+ foreach (const AbstractTimelineModel *modelProxy, d->modelList)
+ retString += modelProxy->categoryTitles();
+ return retString;
+}
+
+int TimelineModelAggregator::count(int modelIndex) const
+{
+ if (modelIndex == -1) {
+ int totalCount = 0;
+ foreach (const AbstractTimelineModel *modelProxy, d->modelList)
+ totalCount += modelProxy->count();
+
+ return totalCount;
+ } else {
+ return d->modelList[modelIndex]->count();
+ }
+}
+
+bool TimelineModelAggregator::isEmpty() const
+{
+ foreach (const AbstractTimelineModel *modelProxy, d->modelList)
+ if (!modelProxy->isEmpty())
+ return false;
+ return true;
+}
+
+bool TimelineModelAggregator::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &/*event*/) const
+{
+ // accept all events
+ return true;
+}
+
+int TimelineModelAggregator::basicModelIndex() const
+{
+ return d->basicModelIndex;
+}
+
+qint64 TimelineModelAggregator::lastTimeMark() const
+{
+ qint64 mark = -1;
+ foreach (const AbstractTimelineModel *modelProxy, d->modelList) {
+ if (!modelProxy->isEmpty()) {
+ qint64 mk = modelProxy->lastTimeMark();
+ if (mark > mk)
+ mark = mk;
+ }
+ }
+ return mark;
+}
+
+bool TimelineModelAggregator::expanded(int modelIndex, int category) const
+{
+ return d->modelList[modelIndex]->expanded(category);
+}
+
+void TimelineModelAggregator::setExpanded(int modelIndex, int category, bool expanded)
+{
+// int modelIndex = modelIndexForCategory(category);
+// category = correctedCategoryIndexForModel(modelIndex, categoryIndex);
+ d->modelList[modelIndex]->setExpanded(category, expanded);
+}
+
+int TimelineModelAggregator::categoryDepth(int modelIndex, int categoryIndex) const
+{
+ return d->modelList[modelIndex]->categoryDepth(categoryIndex);
+}
+
+int TimelineModelAggregator::categoryCount(int modelIndex) const
+{
+ return d->modelList[modelIndex]->categoryCount();
+}
+
+int TimelineModelAggregator::rowCount(int modelIndex) const
+{
+ return d->modelList[modelIndex]->rowCount();
+}
+
+const QString TimelineModelAggregator::categoryLabel(int modelIndex, int categoryIndex) const
+{
+// int modelIndex = modelIndexForCategory(categoryIndex);
+// categoryIndex = correctedCategoryIndexForModel(modelIndex, categoryIndex);
+ return d->modelList[modelIndex]->categoryLabel(categoryIndex);
+}
+
+int TimelineModelAggregator::modelIndexForCategory(int absoluteCategoryIndex) const
+{
+ int categoryIndex = absoluteCategoryIndex;
+ for (int modelIndex = 0; modelIndex < d->modelList.count(); modelIndex++)
+ if (categoryIndex < d->modelList[modelIndex]->categoryCount()) {
+ return modelIndex;
+ } else {
+ categoryIndex -= d->modelList[modelIndex]->categoryCount();
+ }
+
+ return modelCount()-1;
+}
+
+int TimelineModelAggregator::correctedCategoryIndexForModel(int modelIndex, int absoluteCategoryIndex) const
+{
+ int categoryIndex = absoluteCategoryIndex;
+ for (int mi = 0; mi < modelIndex; mi++)
+ categoryIndex -= d->modelList[mi]->categoryCount();
+ return categoryIndex;
+}
+
+int TimelineModelAggregator::findFirstIndex(int modelIndex, qint64 startTime) const
+{
+ return d->modelList[modelIndex]->findFirstIndex(startTime);
+}
+
+int TimelineModelAggregator::findFirstIndexNoParents(int modelIndex, qint64 startTime) const
+{
+ return d->modelList[modelIndex]->findFirstIndexNoParents(startTime);
+}
+
+int TimelineModelAggregator::findLastIndex(int modelIndex, qint64 endTime) const
+{
+ return d->modelList[modelIndex]->findLastIndex(endTime);
+}
+
+int TimelineModelAggregator::getEventType(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEventType(index);
+}
+
+int TimelineModelAggregator::getEventCategoryInModel(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEventCategory(index);
+}
+
+int TimelineModelAggregator::getEventRow(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEventRow(index);
+}
+
+qint64 TimelineModelAggregator::getDuration(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getDuration(index);
+}
+
+qint64 TimelineModelAggregator::getStartTime(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getStartTime(index);
+}
+
+qint64 TimelineModelAggregator::getEndTime(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEndTime(index);
+}
+
+int TimelineModelAggregator::getEventId(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEventId(index);
+}
+
+int TimelineModelAggregator::getBindingLoopDest(int modelIndex,int index) const
+{
+ return d->modelList[modelIndex]->getBindingLoopDest(index);
+}
+
+QColor TimelineModelAggregator::getColor(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getColor(index);
+}
+
+QVariantList TimelineModelAggregator::getColorRGB(int modelIndex, int itemIndex) const
+{
+ // return color as RGB list, for use in Qml
+ QColor c = getColor(modelIndex, itemIndex);
+ QVariantList res;
+ res.append(QVariant(c.red()));
+ res.append(QVariant(c.green()));
+ res.append(QVariant(c.blue()));
+ return res;
+}
+
+float TimelineModelAggregator::getHeight(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getHeight(index);
+}
+
+const QVariantList TimelineModelAggregator::getLabelsForCategory(int modelIndex, int category) const
+{
+// int modelIndex = modelIndexForCategory(category);
+// category = correctedCategoryIndexForModel(modelIndex, category);
+ return d->modelList[modelIndex]->getLabelsForCategory(category);
+}
+
+const QVariantList TimelineModelAggregator::getEventDetails(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEventDetails(index);
+}
+
+const QVariantMap TimelineModelAggregator::getEventLocation(int modelIndex, int index) const
+{
+ return d->modelList[modelIndex]->getEventLocation(index);
+}
+
+int TimelineModelAggregator::getEventIdForHash(const QString &hash) const
+{
+ foreach (const AbstractTimelineModel *model, d->modelList) {
+ int eventId = model->getEventIdForHash(hash);
+ if (eventId != -1)
+ return eventId;
+ }
+ return -1;
+}
+
+int TimelineModelAggregator::getEventIdForLocation(const QString &filename, int line, int column) const
+{
+ foreach (const AbstractTimelineModel *model, d->modelList) {
+ int eventId = model->getEventIdForLocation(filename, line, column);
+ if (eventId != -1)
+ return eventId;
+ }
+ return -1;
+}
+
+void TimelineModelAggregator::dataChanged()
+{
+ // this is a slot connected for every modelproxy
+ // nothing to do here, each model will take care of itself
+}
+
+int TimelineModelAggregator::modelCount() const
+{
+ return d->modelList.count();
+}
+
+qint64 TimelineModelAggregator::traceStartTime() const
+{
+ return d->modelManager->traceTime()->startTime();
+}
+
+qint64 TimelineModelAggregator::traceEndTime() const
+{
+ return d->modelManager->traceTime()->endTime();
+}
+
+qint64 TimelineModelAggregator::traceDuration() const
+{
+ return d->modelManager->traceTime()->duration();
+}
+
+int TimelineModelAggregator::getState() const
+{
+ return (int)d->modelManager->state();
+}
+
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/timelinemodelaggregator.h b/src/plugins/qmlprofiler/timelinemodelaggregator.h
new file mode 100644
index 0000000000..7b1e832840
--- /dev/null
+++ b/src/plugins/qmlprofiler/timelinemodelaggregator.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef TIMELINEMODELAGGREGATOR_H
+#define TIMELINEMODELAGGREGATOR_H
+
+#include "abstracttimelinemodel.h"
+#include "qmlprofilermodelmanager.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+class TimelineModelAggregator : public QObject
+{
+ Q_OBJECT
+public:
+ TimelineModelAggregator(QObject *parent = 0);
+ ~TimelineModelAggregator();
+
+ void setModelManager(QmlProfilerModelManager *modelManager);
+ void addModel(AbstractTimelineModel *m);
+
+
+ Q_INVOKABLE int categories() const;
+ Q_INVOKABLE int visibleCategories() const;
+ Q_INVOKABLE QStringList categoryTitles() const;
+ Q_INVOKABLE int count(int modelIndex = -1) const;
+ void clear();
+ Q_INVOKABLE int modelCount() const;
+
+ Q_INVOKABLE qint64 traceStartTime() const;
+ Q_INVOKABLE qint64 traceEndTime() const;
+ Q_INVOKABLE qint64 traceDuration() const;
+ Q_INVOKABLE int getState() const;
+
+ bool isEmpty() const;
+
+ bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const;
+
+ Q_INVOKABLE int basicModelIndex() const;
+
+ Q_INVOKABLE qint64 lastTimeMark() const;
+
+ Q_INVOKABLE bool expanded(int modelIndex, int category) const;
+ Q_INVOKABLE void setExpanded(int modelIndex, int category, bool expanded);
+ Q_INVOKABLE int categoryDepth(int modelIndex, int categoryIndex) const;
+ Q_INVOKABLE int categoryCount(int modelIndex) const;
+ Q_INVOKABLE int rowCount(int modelIndex) const;
+ Q_INVOKABLE const QString categoryLabel(int modelIndex, int categoryIndex) const;
+
+ int findFirstIndex(int modelIndex, qint64 startTime) const;
+ int findFirstIndexNoParents(int modelIndex, qint64 startTime) const;
+ int findLastIndex(int modelIndex, qint64 endTime) const;
+
+ int getEventType(int modelIndex, int index) const;
+ Q_INVOKABLE int getEventCategoryInModel(int modelIndex, int index) const;
+ int getEventRow(int modelIndex, int index) const;
+ Q_INVOKABLE qint64 getDuration(int modelIndex, int index) const;
+ Q_INVOKABLE qint64 getStartTime(int modelIndex, int index) const;
+ Q_INVOKABLE qint64 getEndTime(int modelIndex, int index) const;
+ Q_INVOKABLE int getEventId(int modelIndex, int index) const;
+ Q_INVOKABLE int getBindingLoopDest(int modelIndex, int index) const;
+ Q_INVOKABLE QColor getColor(int modelIndex, int index) const;
+ Q_INVOKABLE QVariantList getColorRGB(int modelIndex, int itemIndex) const;
+ Q_INVOKABLE float getHeight(int modelIndex, int index) const;
+
+ Q_INVOKABLE const QVariantList getLabelsForCategory(int modelIndex, int category) const;
+
+ Q_INVOKABLE const QVariantList getEventDetails(int modelIndex, int index) const;
+ Q_INVOKABLE const QVariantMap getEventLocation(int modelIndex, int index) const;
+
+ Q_INVOKABLE int getEventIdForHash(const QString &hash) const;
+ Q_INVOKABLE int getEventIdForLocation(const QString &filename, int line, int column) const;
+
+ Q_INVOKABLE int modelIndexForCategory(int absoluteCategoryIndex) const;
+ Q_INVOKABLE int correctedCategoryIndexForModel(int modelIndex, int absoluteCategoryIndex) const;
+
+signals:
+ void countChanged();
+ void dataAvailable();
+ void stateChanged();
+ void emptyChanged();
+ void expandedChanged();
+
+protected slots:
+ void dataChanged();
+
+private:
+ class TimelineModelAggregatorPrivate;
+ TimelineModelAggregatorPrivate *d;
+};
+
+}
+}
+
+#endif // TIMELINEMODELAGGREGATOR_H
diff --git a/src/plugins/qmlprofiler/timelinerenderer.cpp b/src/plugins/qmlprofiler/timelinerenderer.cpp
index 9af59c3d64..4f86f2cd9a 100644
--- a/src/plugins/qmlprofiler/timelinerenderer.cpp
+++ b/src/plugins/qmlprofiler/timelinerenderer.cpp
@@ -35,6 +35,7 @@
#include <QPixmap>
#include <QPainter>
#include <QGraphicsSceneMouseEvent>
+#include <QVarLengthArray>
#include <math.h>
@@ -44,14 +45,26 @@ const int DefaultRowHeight = 30;
TimelineRenderer::TimelineRenderer(QDeclarativeItem *parent) :
QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0),
- m_lastStartTime(0), m_lastEndTime(0), m_profilerDataModel(0)
+ m_lastStartTime(0), m_lastEndTime(0)
+ , m_profilerModelProxy(0)
{
clearData();
setFlag(QGraphicsItem::ItemHasNoContents, false);
setAcceptedMouseButtons(Qt::LeftButton);
setAcceptHoverEvents(true);
- for (int i=0; i<QmlDebug::MaximumQmlEventType; i++)
- m_rowsExpanded << false;
+}
+
+void TimelineRenderer::setProfilerModelProxy(QObject *profilerModelProxy)
+{
+ if (m_profilerModelProxy) {
+ disconnect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
+ }
+ m_profilerModelProxy = qobject_cast<TimelineModelAggregator *>(profilerModelProxy);
+
+ if (m_profilerModelProxy) {
+ connect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
+ }
+ emit profilerModelProxyChanged(m_profilerModelProxy);
}
void TimelineRenderer::componentComplete()
@@ -80,105 +93,68 @@ void TimelineRenderer::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWid
m_spacing = qreal(width()) / windowDuration;
- m_rowWidths.clear();
- // The "1+" is because the reference screenshot features an empty row per type, in order to leave space for the title
- for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) {
- m_rowWidths << 1 + (m_rowsExpanded[i] ? m_profilerDataModel->uniqueEventsOfType(i) :
- m_profilerDataModel->maxNestingForType(i));
- }
-
- // event rows
- m_rowStarts.clear();
- int pos = 0;
- for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) {
- m_rowStarts << pos;
- pos += DefaultRowHeight * m_rowWidths[i];
- }
-
p->setPen(Qt::transparent);
- // speedup: don't draw overlapping events, just skip them
- m_rowLastX.clear();
- for (int i=0; i<QmlDebug::MaximumQmlEventType; i++)
- for (int j=0; j<m_rowWidths[i]; j++)
- m_rowLastX << -m_startTime * m_spacing;
-
- int firstIndex = m_profilerDataModel->findFirstIndex(m_startTime);
- int lastIndex = m_profilerDataModel->findLastIndex(m_endTime);
-
- if (lastIndex < m_profilerDataModel->count()) {
- drawItemsToPainter(p, firstIndex, lastIndex);
- drawSelectionBoxes(p, firstIndex, lastIndex);
- drawBindingLoopMarkers(p, firstIndex, lastIndex);
+ for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
+ int lastIndex = m_profilerModelProxy->findLastIndex(modelIndex, m_endTime);
+ if (lastIndex >= 0 && lastIndex < m_profilerModelProxy->count(modelIndex)) {
+ int firstIndex = m_profilerModelProxy->findFirstIndex(modelIndex, m_startTime);
+ if (firstIndex >= 0) {
+ drawItemsToPainter(p, modelIndex, firstIndex, lastIndex);
+ if (m_selectedModel == modelIndex)
+ drawSelectionBoxes(p, modelIndex, firstIndex, lastIndex);
+ drawBindingLoopMarkers(p, modelIndex, firstIndex, lastIndex);
+ }
+ }
}
-
m_lastStartTime = m_startTime;
m_lastEndTime = m_endTime;
-}
-QColor TimelineRenderer::colorForItem(int itemIndex)
-{
- int ndx = m_profilerDataModel->getEventId(itemIndex);
- return QColor::fromHsl((ndx*25)%360, 76, 166);
}
-void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex)
{
- int x, y, width, height, rowNumber, eventType;
+ p->save();
+ p->setPen(Qt::transparent);
+ int modelRowStart = 0;
+ for (int mi = 0; mi < modelIndex; mi++)
+ modelRowStart += m_profilerModelProxy->rowCount(mi);
+
for (int i = fromIndex; i <= toIndex; i++) {
- x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
+ int x, y, width, height;
+ x = (m_profilerModelProxy->getStartTime(modelIndex, i) - m_startTime) * m_spacing;
- eventType = m_profilerDataModel->getType(i);
- if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight *
- (m_profilerDataModel->eventPosInType(i) + 1);
- else
- y = m_rowStarts[eventType] + DefaultRowHeight *
- m_profilerDataModel->getNestingLevel(i);
+ int rowNumber = m_profilerModelProxy->getEventRow(modelIndex, i);
+ y = (modelRowStart + rowNumber) * DefaultRowHeight;
- width = m_profilerDataModel->getDuration(i)*m_spacing;
- if (width<1)
+ width = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing;
+ if (width < 1)
width = 1;
- rowNumber = y/DefaultRowHeight;
- if (m_rowLastX[rowNumber] > x+width)
- continue;
- m_rowLastX[rowNumber] = x+width;
-
- // special: animations
- if (eventType == 0 && m_profilerDataModel->getAnimationCount(i) >= 0) {
- double scale = m_profilerDataModel->getMaximumAnimationCount() -
- m_profilerDataModel->getMinimumAnimationCount();
- double fraction;
- if (scale > 1)
- fraction = (double)(m_profilerDataModel->getAnimationCount(i) -
- m_profilerDataModel->getMinimumAnimationCount()) / scale;
- else
- fraction = 1.0;
- height = DefaultRowHeight * (fraction * 0.85 + 0.15);
- y += DefaultRowHeight - height;
-
- double fpsFraction = m_profilerDataModel->getFramerate(i) / 60.0;
- if (fpsFraction > 1.0)
- fpsFraction = 1.0;
- p->setBrush(QColor::fromHsl((fpsFraction*96)+10, 76, 166));
- p->drawRect(x, y, width, height);
- } else {
- // normal events
- p->setBrush(colorForItem(i));
- p->drawRect(x, y, width, DefaultRowHeight);
- }
+ height = DefaultRowHeight * m_profilerModelProxy->getHeight(modelIndex, i);
+ y += DefaultRowHeight - height;
+
+ // normal events
+ p->setBrush(m_profilerModelProxy->getColor(modelIndex, i));
+ p->drawRect(x, y, width, height);
}
+ p->restore();
}
-void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex)
{
if (m_selectedItem == -1)
return;
- int id = m_profilerDataModel->getEventId(m_selectedItem);
- p->setBrush(Qt::transparent);
+ int id = m_profilerModelProxy->getEventId(modelIndex, m_selectedItem);
+
+ int modelRowStart = 0;
+ for (int mi = 0; mi < modelIndex; mi++)
+ modelRowStart += m_profilerModelProxy->rowCount(mi);
+
+ p->save();
+
QColor selectionColor = Qt::blue;
if (m_selectionLocked)
selectionColor = QColor(96,0,255);
@@ -186,25 +162,18 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toInde
QPen lightPen(QBrush(selectionColor.lighter(130)), 2);
lightPen.setJoinStyle(Qt::MiterJoin);
p->setPen(lightPen);
+ p->setBrush(Qt::transparent);
- int x, y, width, eventType;
- p->setPen(lightPen);
-
+ int x, y, width;
QRect selectedItemRect(0,0,0,0);
for (int i = fromIndex; i <= toIndex; i++) {
- if (m_profilerDataModel->getEventId(i) != id)
+ if (m_profilerModelProxy->getEventId(modelIndex, i) != id)
continue;
- x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
- eventType = m_profilerDataModel->getType(i);
- if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight *
- (m_profilerDataModel->eventPosInType(i) + 1);
- else
- y = m_rowStarts[eventType] + DefaultRowHeight *
- m_profilerDataModel->getNestingLevel(i);
+ x = (m_profilerModelProxy->getStartTime(modelIndex, i) - m_startTime) * m_spacing;
+ y = (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i)) * DefaultRowHeight;
- width = m_profilerDataModel->getDuration(i)*m_spacing;
+ width = m_profilerModelProxy->getDuration(modelIndex, i)*m_spacing;
if (width<1)
width = 1;
@@ -219,12 +188,14 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toInde
p->setPen(strongPen);
p->drawRect(selectedItemRect);
}
+
+ p->restore();
}
-void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex)
+void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex)
{
int destindex;
- int xfrom, xto, eventType;
+ int xfrom, xto;
int yfrom, yto;
int radius = DefaultRowHeight / 3;
QPen shadowPen = QPen(QColor("grey"),2);
@@ -234,38 +205,24 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int to
p->save();
for (int i = fromIndex; i <= toIndex; i++) {
- destindex = m_profilerDataModel->getBindingLoopDest(i);
+ destindex = m_profilerModelProxy->getBindingLoopDest(modelIndex, i);
if (destindex >= 0) {
// from
- xfrom = (m_profilerDataModel->getStartTime(i) +
- m_profilerDataModel->getDuration(i)/2 -
+ xfrom = (m_profilerModelProxy->getStartTime(modelIndex, i) +
+ m_profilerModelProxy->getDuration(modelIndex, i)/2 -
m_startTime) * m_spacing;
- eventType = m_profilerDataModel->getType(i);
- if (m_rowsExpanded[eventType])
- yfrom = m_rowStarts[eventType] + DefaultRowHeight*
- (m_profilerDataModel->eventPosInType(i) + 1);
- else
- yfrom = m_rowStarts[eventType] + DefaultRowHeight *
- m_profilerDataModel->getNestingLevel(i);
-
+ yfrom = getYPosition(modelIndex, i);
yfrom += DefaultRowHeight / 2;
// to
- xto = (m_profilerDataModel->getStartTime(destindex) +
- m_profilerDataModel->getDuration(destindex)/2 -
+ xto = (m_profilerModelProxy->getStartTime(modelIndex, destindex) +
+ m_profilerModelProxy->getDuration(modelIndex, destindex)/2 -
m_startTime) * m_spacing;
- eventType = m_profilerDataModel->getType(destindex);
- if (m_rowsExpanded[eventType])
- yto = m_rowStarts[eventType] + DefaultRowHeight *
- (m_profilerDataModel->eventPosInType(destindex) + 1);
- else
- yto = m_rowStarts[eventType] + DefaultRowHeight *
- m_profilerDataModel->getNestingLevel(destindex);
-
+ yto = getYPosition(modelIndex, destindex);
yto += DefaultRowHeight / 2;
// radius
- int eventWidth = m_profilerDataModel->getDuration(i) * m_spacing;
+ int eventWidth = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing;
radius = 5;
if (radius * 2 > eventWidth)
radius = eventWidth / 2;
@@ -292,6 +249,17 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int to
p->restore();
}
+int TimelineRenderer::modelFromPosition(int y)
+{
+ y = y / DefaultRowHeight;
+ for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
+ y -= m_profilerModelProxy->rowCount(modelIndex);
+ if (y < 0)
+ return modelIndex;
+ }
+ return 0;
+}
+
void TimelineRenderer::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
// special case: if there is a drag area below me, don't accept the
@@ -326,15 +294,17 @@ void TimelineRenderer::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
void TimelineRenderer::manageClicked()
{
if (m_currentSelection.eventIndex != -1) {
- if (m_currentSelection.eventIndex == m_selectedItem)
+ if (m_currentSelection.eventIndex == m_selectedItem && m_currentSelection.modelIndex == m_selectedModel)
setSelectionLocked(!m_selectionLocked);
else
setSelectionLocked(true);
- emit itemPressed(m_currentSelection.eventIndex);
+ emit itemPressed(m_currentSelection.modelIndex, m_currentSelection.eventIndex);
} else {
-// setSelectionLocked(false);
+ setSelectionLocked(false);
}
+ setSelectedModel(m_currentSelection.modelIndex);
setSelectedItem(m_currentSelection.eventIndex);
+
}
void TimelineRenderer::manageHovered(int x, int y)
@@ -344,6 +314,7 @@ void TimelineRenderer::manageHovered(int x, int y)
qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime;
int row = y / DefaultRowHeight;
+ int modelIndex = modelFromPosition(y);
// already covered? nothing to do
if (m_currentSelection.eventIndex != -1 &&
@@ -354,34 +325,37 @@ void TimelineRenderer::manageHovered(int x, int y)
}
// find if there's items in the time range
- int eventFrom = m_profilerDataModel->findFirstIndex(time);
- int eventTo = m_profilerDataModel->findLastIndex(time);
- if (eventTo < eventFrom || eventTo >= m_profilerDataModel->count()) {
+ int eventFrom = m_profilerModelProxy->findFirstIndex(modelIndex, time);
+ int eventTo = m_profilerModelProxy->findLastIndex(modelIndex, time);
+ if (eventFrom == -1 ||
+ eventTo < eventFrom || eventTo >= m_profilerModelProxy->count()) {
m_currentSelection.eventIndex = -1;
return;
}
+ int modelRowStart = 0;
+ for (int mi = 0; mi < modelIndex; mi++)
+ modelRowStart += m_profilerModelProxy->rowCount(mi);
+
// find if we are in the right column
- int itemRow, eventType;
+ int itemRow;
for (int i=eventTo; i>=eventFrom; --i) {
- if (ceil(m_profilerDataModel->getEndTime(i)*m_spacing) < floor(time*m_spacing))
+ if (ceil(m_profilerModelProxy->getEndTime(modelIndex, i)*m_spacing) < floor(time*m_spacing))
continue;
- eventType = m_profilerDataModel->getType(i);
- if (m_rowsExpanded[eventType])
- itemRow = m_rowStarts[eventType]/DefaultRowHeight +
- m_profilerDataModel->eventPosInType(i) + 1;
- else
- itemRow = m_rowStarts[eventType]/DefaultRowHeight +
- m_profilerDataModel->getNestingLevel(i);
+ itemRow = modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i);
+
if (itemRow == row) {
// match
m_currentSelection.eventIndex = i;
- m_currentSelection.startTime = m_profilerDataModel->getStartTime(i);
- m_currentSelection.endTime = m_profilerDataModel->getEndTime(i);
+ m_currentSelection.startTime = m_profilerModelProxy->getStartTime(modelIndex, i);
+ m_currentSelection.endTime = m_profilerModelProxy->getEndTime(modelIndex, i);
m_currentSelection.row = row;
- if (!m_selectionLocked)
+ m_currentSelection.modelIndex = modelIndex;
+ if (!m_selectionLocked) {
+ setSelectedModel(modelIndex);
setSelectedItem(i);
+ }
return;
}
}
@@ -400,131 +374,189 @@ void TimelineRenderer::clearData()
m_currentSelection.endTime = -1;
m_currentSelection.row = -1;
m_currentSelection.eventIndex = -1;
+ m_currentSelection.modelIndex = -1;
m_selectedItem = -1;
+ m_selectedModel = -1;
m_selectionLocked = true;
}
-qint64 TimelineRenderer::getDuration(int index) const
-{
- Q_ASSERT(m_profilerDataModel);
- return m_profilerDataModel->getEndTime(index) -
- m_profilerDataModel->getStartTime(index);
-}
-
-QString TimelineRenderer::getFilename(int index) const
-{
- Q_ASSERT(m_profilerDataModel);
- return m_profilerDataModel->getFilename(index);
-}
-
-int TimelineRenderer::getLine(int index) const
+int TimelineRenderer::getYPosition(int modelIndex, int index) const
{
- Q_ASSERT(m_profilerDataModel);
- return m_profilerDataModel->getLine(index);
-}
+ Q_ASSERT(m_profilerModelProxy);
+ if (index >= m_profilerModelProxy->count())
+ return 0;
-QString TimelineRenderer::getDetails(int index) const
-{
- Q_ASSERT(m_profilerDataModel);
- return m_profilerDataModel->getDetails(index);
-}
+ int modelRowStart = 0;
+ for (int mi = 0; mi < modelIndex; mi++)
+ modelRowStart += m_profilerModelProxy->rowCount(mi);
-int TimelineRenderer::getYPosition(int index) const
-{
- Q_ASSERT(m_profilerDataModel);
- if (index >= m_profilerDataModel->count() || m_rowStarts.isEmpty())
- return 0;
- int y, eventType = m_profilerDataModel->getType(index);
- if (m_rowsExpanded[eventType])
- y = m_rowStarts[eventType] + DefaultRowHeight *
- (m_profilerDataModel->eventPosInType(index) + 1);
- else
- y = m_rowStarts[eventType] + DefaultRowHeight *
- m_profilerDataModel->getNestingLevel(index);
+ int y = DefaultRowHeight * (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, index));
return y;
}
-void TimelineRenderer::setRowExpanded(int rowIndex, bool expanded)
-{
- m_rowsExpanded[rowIndex] = expanded;
- update();
-}
-
void TimelineRenderer::selectNext()
{
- if (m_profilerDataModel->count() == 0)
+ if (m_profilerModelProxy->count() == 0)
return;
- // select next in view or after
- int newIndex = m_selectedItem+1;
- if (newIndex >= m_profilerDataModel->count())
- newIndex = 0;
- if (m_profilerDataModel->getEndTime(newIndex) < m_startTime)
- newIndex = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
- setSelectedItem(newIndex);
+ qint64 searchTime = m_startTime;
+ if (m_selectedItem != -1)
+ searchTime = m_profilerModelProxy->getStartTime(m_selectedModel, m_selectedItem);
+
+ QVarLengthArray<int> itemIndexes(m_profilerModelProxy->modelCount());
+ for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
+ if (m_profilerModelProxy->count(i) > 0) {
+ if (m_selectedModel == i) {
+ itemIndexes[i] = (m_selectedItem + 1) % m_profilerModelProxy->count(i);
+ } else {
+ if (m_profilerModelProxy->getStartTime(i, 0) > searchTime)
+ itemIndexes[i] = 0;
+ else
+ itemIndexes[i] = (m_profilerModelProxy->findLastIndex(i, searchTime) + 1) % m_profilerModelProxy->count(i);
+ }
+ } else {
+ itemIndexes[i] = -1;
+ }
+ }
+
+ int candidateModelIndex = -1;
+ qint64 candidateStartTime = m_profilerModelProxy->traceEndTime();
+ for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
+ if (itemIndexes[i] == -1)
+ continue;
+ qint64 newStartTime = m_profilerModelProxy->getStartTime(i, itemIndexes[i]);
+ if (newStartTime > searchTime && newStartTime < candidateStartTime) {
+ candidateStartTime = newStartTime;
+ candidateModelIndex = i;
+ }
+ }
+
+ int itemIndex;
+ if (candidateModelIndex != -1) {
+ itemIndex = itemIndexes[candidateModelIndex];
+ } else {
+ // find the first index of them all (todo: the modelproxy should do this)
+ itemIndex = -1;
+ candidateStartTime = m_profilerModelProxy->traceEndTime();
+ for (int i = 0; i < m_profilerModelProxy->modelCount(); i++)
+ if (m_profilerModelProxy->count(i) > 0 &&
+ m_profilerModelProxy->getStartTime(i,0) < candidateStartTime) {
+ candidateModelIndex = i;
+ itemIndex = 0;
+ candidateStartTime = m_profilerModelProxy->getStartTime(i,0);
+ }
+ }
+
+ setSelectedModel(candidateModelIndex);
+ setSelectedItem(itemIndex);
}
void TimelineRenderer::selectPrev()
{
- if (m_profilerDataModel->count() == 0)
+ if (m_profilerModelProxy->count() == 0)
return;
- // select last in view or before
- int newIndex = m_selectedItem-1;
- if (newIndex < 0)
- newIndex = m_profilerDataModel->count()-1;
- if (m_profilerDataModel->getStartTime(newIndex) > m_endTime)
- newIndex = m_profilerDataModel->findLastIndex(m_endTime);
- setSelectedItem(newIndex);
+ qint64 searchTime = m_endTime;
+ if (m_selectedItem != -1)
+ searchTime = m_profilerModelProxy->getEndTime(m_selectedModel, m_selectedItem);
+
+ QVarLengthArray<int> itemIndexes(m_profilerModelProxy->modelCount());
+ for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
+ if (m_selectedModel == i) {
+ itemIndexes[i] = m_selectedItem - 1;
+ if (itemIndexes[i] < 0)
+ itemIndexes[i] = m_profilerModelProxy->count(m_selectedModel) -1;
+ }
+ else
+ itemIndexes[i] = m_profilerModelProxy->findLastIndex(i, searchTime);
+ }
+
+ int candidateModelIndex = -1;
+ qint64 candidateStartTime = m_profilerModelProxy->traceStartTime();
+ for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
+ if (itemIndexes[i] == -1
+ || itemIndexes[i] >= m_profilerModelProxy->count(i))
+ continue;
+ qint64 newStartTime = m_profilerModelProxy->getStartTime(i, itemIndexes[i]);
+ if (newStartTime < searchTime && newStartTime > candidateStartTime) {
+ candidateStartTime = newStartTime;
+ candidateModelIndex = i;
+ }
+ }
+
+ int itemIndex = -1;
+ if (candidateModelIndex != -1) {
+ itemIndex = itemIndexes[candidateModelIndex];
+ } else {
+ // find the last index of them all (todo: the modelproxy should do this)
+ candidateModelIndex = 0;
+ candidateStartTime = m_profilerModelProxy->traceStartTime();
+ for (int i = 0; i < m_profilerModelProxy->modelCount(); i++)
+ if (m_profilerModelProxy->count(i) > 0 &&
+ m_profilerModelProxy->getStartTime(i,m_profilerModelProxy->count(i)-1) > candidateStartTime) {
+ candidateModelIndex = i;
+ itemIndex = m_profilerModelProxy->count(candidateModelIndex) - 1;
+ candidateStartTime = m_profilerModelProxy->getStartTime(i,m_profilerModelProxy->count(i)-1);
+ }
+ }
+
+ setSelectedModel(candidateModelIndex);
+ setSelectedItem(itemIndex);
}
-int TimelineRenderer::nextItemFromId(int eventId) const
+int TimelineRenderer::nextItemFromId(int modelIndex, int eventId) const
{
int ndx = -1;
if (m_selectedItem == -1)
- ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
+ ndx = m_profilerModelProxy->findFirstIndexNoParents(modelIndex, m_startTime);
else
ndx = m_selectedItem + 1;
- if (ndx >= m_profilerDataModel->count())
+ if (ndx < 0)
+ return -1;
+ if (ndx >= m_profilerModelProxy->count(modelIndex))
ndx = 0;
int startIndex = ndx;
do {
- if (m_profilerDataModel->getEventId(ndx) == eventId)
+ if (m_profilerModelProxy->getEventId(modelIndex, ndx) == eventId)
return ndx;
- ndx = (ndx + 1) % m_profilerDataModel->count();
+ ndx = (ndx + 1) % m_profilerModelProxy->count(modelIndex);
} while (ndx != startIndex);
return -1;
}
-int TimelineRenderer::prevItemFromId(int eventId) const
+int TimelineRenderer::prevItemFromId(int modelIndex, int eventId) const
{
int ndx = -1;
if (m_selectedItem == -1)
- ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime);
+ ndx = m_profilerModelProxy->findFirstIndexNoParents(modelIndex, m_startTime);
else
ndx = m_selectedItem - 1;
if (ndx < 0)
- ndx = m_profilerDataModel->count() - 1;
+ ndx = m_profilerModelProxy->count(modelIndex) - 1;
int startIndex = ndx;
do {
- if (m_profilerDataModel->getEventId(ndx) == eventId)
+ if (m_profilerModelProxy->getEventId(modelIndex, ndx) == eventId)
return ndx;
if (--ndx < 0)
- ndx = m_profilerDataModel->count()-1;
+ ndx = m_profilerModelProxy->count(modelIndex)-1;
} while (ndx != startIndex);
return -1;
}
-void TimelineRenderer::selectNextFromId(int eventId)
+void TimelineRenderer::selectNextFromId(int modelIndex, int eventId)
{
- int eventIndex = nextItemFromId(eventId);
- if (eventIndex != -1)
+ int eventIndex = nextItemFromId(modelIndex, eventId);
+ if (eventIndex != -1) {
+ setSelectedModel(modelIndex);
setSelectedItem(eventIndex);
+ }
}
-void TimelineRenderer::selectPrevFromId(int eventId)
+void TimelineRenderer::selectPrevFromId(int modelIndex, int eventId)
{
- int eventIndex = prevItemFromId(eventId);
- if (eventIndex != -1)
+ int eventIndex = prevItemFromId(modelIndex, eventId);
+ if (eventIndex != -1) {
+ setSelectedModel(modelIndex);
setSelectedItem(eventIndex);
+ }
}
diff --git a/src/plugins/qmlprofiler/timelinerenderer.h b/src/plugins/qmlprofiler/timelinerenderer.h
index d408b5ef25..d4c70a4d9e 100644
--- a/src/plugins/qmlprofiler/timelinerenderer.h
+++ b/src/plugins/qmlprofiler/timelinerenderer.h
@@ -32,7 +32,8 @@
#include <QDeclarativeItem>
#include <QScriptValue>
-#include "qmlprofilerdatamodel.h"
+#include "qmlprofilertimelinemodelproxy.h"
+#include "timelinemodelaggregator.h"
namespace QmlProfiler {
namespace Internal {
@@ -42,9 +43,10 @@ class TimelineRenderer : public QDeclarativeItem
Q_OBJECT
Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
- Q_PROPERTY(QObject* profilerDataModel READ profilerDataModel WRITE setProfilerDataModel NOTIFY profilerDataModelChanged)
+ Q_PROPERTY(QObject *profilerModelProxy READ profilerModelProxy WRITE setProfilerModelProxy NOTIFY profilerModelProxyChanged)
Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
+ Q_PROPERTY(int selectedModel READ selectedModel WRITE setSelectedModel NOTIFY selectedModelChanged)
Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged)
Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged)
@@ -71,6 +73,11 @@ public:
return m_selectedItem;
}
+ int selectedModel() const
+ {
+ return m_selectedModel;
+ }
+
int startDragArea() const
{
return m_startDragArea;
@@ -81,37 +88,28 @@ public:
return m_endDragArea;
}
- QmlProfilerDataModel *profilerDataModel() const { return m_profilerDataModel; }
- void setProfilerDataModel(QObject *profilerDataModel)
- {
- m_profilerDataModel = qobject_cast<QmlProfilerDataModel *>(profilerDataModel);
- emit profilerDataModelChanged(m_profilerDataModel);
- }
-
- Q_INVOKABLE qint64 getDuration(int index) const;
- Q_INVOKABLE QString getFilename(int index) const;
- Q_INVOKABLE int getLine(int index) const;
- Q_INVOKABLE QString getDetails(int index) const;
- Q_INVOKABLE int getYPosition(int index) const;
+ TimelineModelAggregator *profilerModelProxy() const { return m_profilerModelProxy; }
+ void setProfilerModelProxy(QObject *profilerModelProxy);
- Q_INVOKABLE void setRowExpanded(int rowIndex, bool expanded);
+ Q_INVOKABLE int getYPosition(int modelIndex, int index) const;
Q_INVOKABLE void selectNext();
Q_INVOKABLE void selectPrev();
- Q_INVOKABLE int nextItemFromId(int eventId) const;
- Q_INVOKABLE int prevItemFromId(int eventId) const;
- Q_INVOKABLE void selectNextFromId(int eventId);
- Q_INVOKABLE void selectPrevFromId(int eventId);
+ Q_INVOKABLE int nextItemFromId(int modelIndex, int eventId) const;
+ Q_INVOKABLE int prevItemFromId(int modelIndex, int eventId) const;
+ Q_INVOKABLE void selectNextFromId(int modelIndex, int eventId);
+ Q_INVOKABLE void selectPrevFromId(int modelIndex, int eventId);
signals:
void startTimeChanged(qint64 arg);
void endTimeChanged(qint64 arg);
- void profilerDataModelChanged(QmlProfilerDataModel *list);
+ void profilerModelProxyChanged(TimelineModelAggregator *list);
void selectionLockedChanged(bool locked);
- void selectedItemChanged(int itemIndex);
+ void selectedItemChanged(int modelIndex, int itemIndex);
+ void selectedModelChanged(int modelIndex);
void startDragAreaChanged(int startDragArea);
void endDragAreaChanged(int endDragArea);
- void itemPressed(int pressedItem);
+ void itemPressed(int modelIndex, int pressedItem);
public slots:
void clearData();
@@ -148,7 +146,16 @@ public slots:
if (m_selectedItem != itemIndex) {
m_selectedItem = itemIndex;
update();
- emit selectedItemChanged(itemIndex);
+ emit selectedItemChanged(m_selectedModel, itemIndex);
+ }
+ }
+
+ void setSelectedModel(int modelIndex)
+ {
+ if (m_selectedModel != modelIndex) {
+ m_selectedModel = modelIndex;
+ update();
+ emit selectedModelChanged(modelIndex);
}
}
@@ -177,10 +184,10 @@ protected:
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
private:
- QColor colorForItem(int itemIndex);
- void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex);
- void drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex);
- void drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex);
+ void drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex);
+ void drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex);
+ void drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex);
+ int modelFromPosition(int y);
void manageClicked();
void manageHovered(int x, int y);
@@ -192,21 +199,18 @@ private:
qint64 m_lastStartTime;
qint64 m_lastEndTime;
- QmlProfilerDataModel *m_profilerDataModel;
-
- QList<int> m_rowLastX;
- QList<int> m_rowStarts;
- QList<int> m_rowWidths;
- QList<bool> m_rowsExpanded;
+ TimelineModelAggregator *m_profilerModelProxy;
struct {
qint64 startTime;
qint64 endTime;
int row;
int eventIndex;
+ int modelIndex;
} m_currentSelection;
int m_selectedItem;
+ int m_selectedModel;
bool m_selectionLocked;
int m_startDragArea;
int m_endDragArea;