summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristiaan Janssen <christiaan.janssen@nokia.com>2011-07-26 13:56:14 +0200
committerChristiaan Janssen <christiaan.janssen@nokia.com>2011-08-17 12:33:39 +0200
commitd2911d70f36b7fd1de4ebbd41915782669f8d721 (patch)
treedb000c070f35fe5e2224901465de1bd4cb6cc171 /src
parent8fbaa0d10a4da35b8bac7e2355d864064b0727bf (diff)
downloadqt-creator-d2911d70f36b7fd1de4ebbd41915782669f8d721.tar.gz
QmlProfiler: New event list with caching, load, save
Change-Id: I640a16649156a49f2d7e7006d6b2ea38fe218620 Reviewed-on: http://codereview.qt.nokia.com/3043 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Kai Koehne <kai.koehne@nokia.com>
Diffstat (limited to 'src')
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp13
-rw-r--r--src/libs/qmljsdebugclient/qmlprofilertraceclient.h2
-rw-r--r--src/plugins/qmlprofiler/qml/MainView.js26
-rw-r--r--src/plugins/qmlprofiler/qml/MainView.qml108
-rw-r--r--src/plugins/qmlprofiler/qml/StatusDisplay.qml9
-rw-r--r--src/plugins/qmlprofiler/qml/TimeDisplay.qml2
-rw-r--r--src/plugins/qmlprofiler/qmlprofiler.pro6
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventlist.cpp849
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventlist.h138
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.cpp213
-rw-r--r--src/plugins/qmlprofiler/qmlprofilereventview.h61
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.cpp58
-rw-r--r--src/plugins/qmlprofiler/qmlprofilertool.h6
-rw-r--r--src/plugins/qmlprofiler/timelineview.cpp320
-rw-r--r--src/plugins/qmlprofiler/timelineview.h49
-rw-r--r--src/plugins/qmlprofiler/tracewindow.cpp35
-rw-r--r--src/plugins/qmlprofiler/tracewindow.h11
17 files changed, 1417 insertions, 489 deletions
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
index 6042f38a56..62e8bd5878 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.cpp
@@ -40,10 +40,8 @@ public:
: inProgressRanges(0)
, maximumTime(0)
, recording(false)
- , nestingLevel(0)
{
::memset(rangeCount, 0, MaximumQmlEventType * sizeof(int));
- ::memset(nestingInType, 0, MaximumQmlEventType * sizeof(int));
}
qint64 inProgressRanges;
@@ -53,8 +51,6 @@ public:
int rangeCount[MaximumQmlEventType];
qint64 maximumTime;
bool recording;
- int nestingLevel;
- int nestingInType[MaximumQmlEventType];
};
} // namespace QmlJsDebugClient
@@ -77,8 +73,6 @@ QmlProfilerTraceClient::~QmlProfilerTraceClient()
void QmlProfilerTraceClient::clearData()
{
::memset(d->rangeCount, 0, MaximumQmlEventType * sizeof(int));
- ::memset(d->nestingInType, 0, MaximumQmlEventType * sizeof(int));
- d->nestingLevel = 0;
emit cleared();
}
@@ -151,8 +145,6 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
d->rangeStartTimes[range].push(time);
d->inProgressRanges |= (static_cast<qint64>(1) << range);
++d->rangeCount[range];
- ++d->nestingLevel;
- ++d->nestingInType[range];
} else if (messageType == RangeData) {
QString data;
stream >> data;
@@ -183,10 +175,7 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
Location location = d->rangeLocations[range].count() ? d->rangeLocations[range].pop() : Location();
qint64 startTime = d->rangeStartTimes[range].pop();
- emit this->range((QmlEventType)range, d->nestingLevel, d->nestingInType[range], startTime,
- time - startTime, data, location.fileName, location.line);
- --d->nestingLevel;
- --d->nestingInType[range];
+ emit this->range((QmlEventType)range, startTime, time - startTime, data, location.fileName, location.line);
if (d->rangeCount[range] == 0) {
int count = d->rangeDatas[range].count() +
d->rangeStartTimes[range].count() +
diff --git a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
index abb64afd03..ca596b706a 100644
--- a/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
+++ b/src/libs/qmljsdebugclient/qmlprofilertraceclient.h
@@ -91,7 +91,7 @@ signals:
void complete();
void gap(qint64 time);
void event(int event, qint64 time);
- void range(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length,
+ void range(int type, qint64 startTime, qint64 length,
const QStringList &data, const QString &fileName, int line);
void recordingChanged(bool arg);
diff --git a/src/plugins/qmlprofiler/qml/MainView.js b/src/plugins/qmlprofiler/qml/MainView.js
index 128ea396a3..a95df7daee 100644
--- a/src/plugins/qmlprofiler/qml/MainView.js
+++ b/src/plugins/qmlprofiler/qml/MainView.js
@@ -32,10 +32,9 @@
.pragma library
-var ranges = [ ];
var xmargin = 0;
var ymargin = 0;
-var nestingDepth = [];
+var qmlEventList = 0;
var names = [ "Painting", "Compiling", "Creating", "Binding", "Handling Signal"]
//### need better way to manipulate color from QML. In the meantime, these need to be kept in sync.
@@ -45,10 +44,8 @@ var xRayColors = [ "#6699CCB3", "#6699CCCC", "#6699B3CC", "#669999CC", "#66CC99B
function reset()
{
- ranges = [];
xmargin = 0;
ymargin = 0;
- nestingDepth = [];
}
//draw background of the graph
@@ -65,26 +62,26 @@ function drawGraph(canvas, ctxt, region)
//draw the actual data to be graphed
function drawData(canvas, ctxt, region)
{
- if (ranges.length == 0)
+ if ((!qmlEventList) || qmlEventList.count() == 0)
return;
var width = canvas.canvasSize.width - xmargin;
var height = canvas.height - ymargin;
- var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
+ var sumValue = qmlEventList.lastTimeMark() - qmlEventList.firstTimeMark();
var spacing = width / sumValue;
ctxt.fillStyle = "rgba(0,0,0,1)";
var highest = [0,0,0,0,0];
//### only draw those in range
- for (var ii = 0; ii < ranges.length; ++ii) {
+ for (var ii = 0; ii < qmlEventList.count(); ++ii) {
- var xx = (ranges[ii].start - ranges[0].start) * spacing + xmargin;
+ var xx = (qmlEventList.getStartTime(ii) - qmlEventList.firstTimeMark()) * spacing + xmargin;
if (xx > region.x + region.width)
continue;
- var size = ranges[ii].duration * spacing;
+ var size = qmlEventList.getDuration(ii) * spacing;
if (xx + size < region.x)
continue;
@@ -92,9 +89,10 @@ function drawData(canvas, ctxt, region)
size = 0.5;
xx = Math.round(xx);
- if (xx + size > highest[ranges[ii].type]) {
- ctxt.fillRect(xx, ranges[ii].type*10, size, 10);
- highest[ranges[ii].type] = xx+size;
+ var ty = qmlEventList.getType(ii);
+ if (xx + size > highest[ty]) {
+ ctxt.fillRect(xx, ty*10, size, 10);
+ highest[ty] = xx+size;
}
}
}
@@ -107,12 +105,12 @@ function plot(canvas, ctxt, region)
function xScale(canvas)
{
- if (ranges.length === 0)
+ if ((!qmlEventList) || qmlEventList.count() == 0)
return;
var width = canvas.canvasSize.width - xmargin;
- var sumValue = ranges[ranges.length - 1].start + ranges[ranges.length - 1].duration - ranges[0].start;
+ var sumValue = qmlEventList.lastTimeMark() - qmlEventList.firstTimeMark();
var spacing = sumValue / width;
return spacing;
}
diff --git a/src/plugins/qmlprofiler/qml/MainView.qml b/src/plugins/qmlprofiler/qml/MainView.qml
index 72e026b5d7..025eac39b5 100644
--- a/src/plugins/qmlprofiler/qml/MainView.qml
+++ b/src/plugins/qmlprofiler/qml/MainView.qml
@@ -38,7 +38,8 @@ Rectangle {
id: root
property bool dataAvailable: true;
- property int eventCount: Plotter.ranges.length;
+ property int eventCount: 0;
+ property real progress: 0;
// move the cursor in the editor
signal updateCursorPosition
@@ -72,34 +73,35 @@ Rectangle {
property bool mouseTracking: false;
onSelectedEventIndexChanged: {
- if ((!mouseTracking) && Plotter.ranges.length > 0
- && selectedEventIndex > -1 && selectedEventIndex < Plotter.ranges.length) {
+ if ((!mouseTracking) && eventCount > 0
+ && selectedEventIndex > -1 && selectedEventIndex < eventCount) {
// re-center flickable if necessary
- var event = Plotter.ranges[selectedEventIndex];
var xs = Plotter.xScale(canvas);
- var startTime = Plotter.ranges[0].start;
- if (rangeMover.value + startTime> event.start) {
+ var startTime = qmlEventList.firstTimeMark();
+ var eventStartTime = qmlEventList.getStartTime(selectedEventIndex);
+ var eventDuration = qmlEventList.getDuration(selectedEventIndex);
+ if (rangeMover.value + startTime > eventStartTime) {
rangeMover.x = Math.max(0,
- Math.floor((event.start - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2) );
- } else if (rangeMover.value + startTime + rangeMover.zoomWidth * xs < event.start + event.duration) {
- rangeMover.x = Math.floor((event.start + event.duration - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2);
+ Math.floor((eventStartTime - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2) );
+ } else if (rangeMover.value + startTime + rangeMover.zoomWidth * xs < eventStartTime + eventDuration) {
+ rangeMover.x = Math.floor((eventStartTime + eventDuration - startTime) / xs - canvas.canvasWindow.x - rangeMover.zoomWidth/2);
}
}
}
function nextEvent() {
- if (Plotter.ranges.length > 0) {
+ if (eventCount > 0) {
++selectedEventIndex;
- if (selectedEventIndex >= Plotter.ranges.length)
+ if (selectedEventIndex >= eventCount)
selectedEventIndex = 0;
}
}
function prevEvent() {
- if (Plotter.ranges.length > 0) {
+ if (eventCount > 0) {
--selectedEventIndex;
if (selectedEventIndex < 0)
- selectedEventIndex = Plotter.ranges.length - 1;
+ selectedEventIndex = eventCount - 1;
}
}
@@ -127,46 +129,29 @@ Rectangle {
rangeMover.updateZoomControls();
}
- //handle debug data coming from C++
Connections {
- target: connection
-
- onRange: {
- if (root.dataAvailable) {
- root.clearData();
- }
-
- // todo: consider nestingLevel
- if (!root.dataAvailable) {
- if (!Plotter.nestingDepth[type])
- Plotter.nestingDepth[type] = nestingInType;
- else
- Plotter.nestingDepth[type] = Math.max(Plotter.nestingDepth[type], nestingInType);
- Plotter.ranges.push( { type: type, start: startTime, duration: length, label: data, fileName: fileName, line: line, nestingLevel: nestingInType, nestingDepth: Plotter.nestingDepth[type] } );
- if (nestingInType == 1)
- Plotter.nestingDepth[type] = 1;
- root.eventCount = Plotter.ranges.length;
-
- }
+ target: qmlEventList
+ onCountChanged: {
+ eventCount = qmlEventList.count();
+ if (eventCount == 0)
+ root.clearAll();
+ if (eventCount > 1) {
+ root.progress = Math.min(1.0,
+ (qmlEventList.lastTimeMark() - qmlEventList.firstTimeMark()) / root.elapsedTime * 1e-9 ) * 0.5;
+ } else
+ root.progress = 0;
}
- onComplete: {
- root.dataAvailable = true;
- if (Plotter.ranges.length > 0) {
- view.visible = true;
- view.setRanges(Plotter.ranges);
- view.updateTimeline();
- canvas.requestPaint();
- rangeMover.x = 1 //### hack to get view to display things immediately
- rangeMover.x = 0
- rangeMover.opacity = 1
- }
+ onParsingStatusChanged: {
+ root.dataAvailable = false;
}
- onDataCleared: {
- root.clearAll();
+ onDataReady: {
+ if (eventCount > 0) {
+ view.clearData();
+ view.rebuildCache();
+ }
}
-
}
// Elapsed
@@ -198,8 +183,8 @@ Rectangle {
height: flick.height + labels.y
anchors.left: flick.left
anchors.right: flick.right
- startTime: rangeMover.x * Plotter.xScale(canvas);
- endTime: (rangeMover.x + rangeMover.zoomWidth) * Plotter.xScale(canvas);
+ startTime: rangeMover.x * Plotter.xScale(canvas) + qmlEventList.firstTimeMark();
+ endTime: (rangeMover.x + rangeMover.zoomWidth) * Plotter.xScale(canvas) + qmlEventList.firstTimeMark();
}
function hideRangeDetails() {
@@ -241,6 +226,9 @@ Rectangle {
TimelineView {
id: view
+ eventList: qmlEventList;
+ onEventListChanged: Plotter.qmlEventList = qmlEventList;
+
width: flick.width;
height: flick.contentHeight;
@@ -256,6 +244,20 @@ Rectangle {
endTime: startTime + (rangeMover.zoomWidth*Plotter.xScale(canvas))
onEndTimeChanged: updateTimeline()
+ onCachedProgressChanged: root.progress = 0.5 + cachedProgress * 0.5;
+ onCacheReady: {
+ root.progress = 1.0;
+ root.dataAvailable = true;
+ if (root.eventCount > 0) {
+ view.visible = true;
+ view.updateTimeline();
+ canvas.requestPaint();
+ rangeMover.x = 1 //### hack to get view to display things immediately
+ rangeMover.x = 0
+ rangeMover.opacity = 1
+ }
+ }
+
delegate: Rectangle {
id: obj
@@ -298,10 +300,10 @@ Rectangle {
function enableSelected(x,y) {
myColor = Qt.darker(baseColor, 1.2)
- rangeDetails.duration = duration
- rangeDetails.label = label
- rangeDetails.file = fileName
- rangeDetails.line = line
+ rangeDetails.duration = qmlEventList.getDuration(index)/1000.0;
+ rangeDetails.label = qmlEventList.getDetails(index);
+ rangeDetails.file = qmlEventList.getFilename(index);
+ rangeDetails.line = qmlEventList.getLine(index);
rangeDetails.type = Plotter.names[type]
var margin = 10;
diff --git a/src/plugins/qmlprofiler/qml/StatusDisplay.qml b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
index f09e886fa5..9089e42134 100644
--- a/src/plugins/qmlprofiler/qml/StatusDisplay.qml
+++ b/src/plugins/qmlprofiler/qml/StatusDisplay.qml
@@ -4,14 +4,7 @@ import "MainView.js" as Plotter
Rectangle {
id: statusDisplay
- property real percentage : 0
- property int eventCount: root.eventCount
- onEventCountChanged: {
- if (state=="loading" && eventCount > 0 && root.elapsedTime > 0) {
- percentage = Math.min(1.0,
- (Plotter.ranges[Plotter.ranges.length-1].start - Plotter.ranges[0].start) / root.elapsedTime * 1e-9 );
- }
- }
+ property real percentage : root.progress
width: Math.max(200, statusText.width+20);
height: displayColumn.height + 20
diff --git a/src/plugins/qmlprofiler/qml/TimeDisplay.qml b/src/plugins/qmlprofiler/qml/TimeDisplay.qml
index 3b5c67852c..56c816245a 100644
--- a/src/plugins/qmlprofiler/qml/TimeDisplay.qml
+++ b/src/plugins/qmlprofiler/qml/TimeDisplay.qml
@@ -164,7 +164,7 @@ TiledCanvas {
}
onMousePositionChanged: {
- if (!Plotter.ranges.length)
+ if (!root.eventCount)
return;
if (!pressed && timeDisplayEnd.visible)
diff --git a/src/plugins/qmlprofiler/qmlprofiler.pro b/src/plugins/qmlprofiler/qmlprofiler.pro
index 107acdcfc5..231d4bf36c 100644
--- a/src/plugins/qmlprofiler/qmlprofiler.pro
+++ b/src/plugins/qmlprofiler/qmlprofiler.pro
@@ -27,7 +27,8 @@ SOURCES += \
codaqmlprofilerrunner.cpp \
remotelinuxqmlprofilerrunner.cpp \
qmlprofilereventview.cpp \
- qmlprofilerruncontrolfactory.cpp
+ qmlprofilerruncontrolfactory.cpp \
+ qmlprofilereventlist.cpp
HEADERS += \
qmlprofilerconstants.h \
@@ -43,7 +44,8 @@ HEADERS += \
codaqmlprofilerrunner.h \
remotelinuxqmlprofilerrunner.h \
qmlprofilereventview.h \
- qmlprofilerruncontrolfactory.h
+ qmlprofilerruncontrolfactory.h\
+ qmlprofilereventlist.h
RESOURCES += \
qml/qml.qrc
diff --git a/src/plugins/qmlprofiler/qmlprofilereventlist.cpp b/src/plugins/qmlprofiler/qmlprofilereventlist.cpp
new file mode 100644
index 0000000000..dd05407dbc
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilereventlist.cpp
@@ -0,0 +1,849 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#include "qmlprofilereventlist.h"
+
+#include <coreplugin/icore.h>
+#include <QtCore/QUrl>
+#include <QtCore/QHash>
+#include <QtCore/QtAlgorithms>
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+#include <QtCore/QFile>
+#include <QtCore/QXmlStreamReader>
+#include <QtCore/QXmlStreamWriter>
+
+#include <QtCore/QTimer>
+#include <QtGui/QMainWindow>
+#include <QtGui/QMessageBox>
+
+#include <QDebug>
+
+namespace QmlProfiler {
+namespace Internal {
+
+#define MIN_LEVEL 1
+
+// description
+struct QmlEventDescription {
+ QmlEventDescription() : displayname(0), location(0), filename(0), details(0) {}
+ ~QmlEventDescription() {
+ delete displayname;
+ delete location;
+ delete filename;
+ delete details;
+ }
+
+ QString *displayname;
+ QString *location;
+ QString *filename;
+ QString *details;
+ int eventType;
+ int line;
+};
+
+// endtimedata
+struct QmlEventEndTimeData {
+ qint64 endTime;
+ int startTimeIndex;
+ QmlEventData *description;
+};
+
+// starttimedata
+struct QmlEventStartTimeData {
+ qint64 startTime;
+ qint64 length;
+ qint64 level;
+ int endTimeIndex;
+ qint64 nestingLevel;
+ qint64 nestingDepth;
+ QmlEventData *description;
+
+};
+
+// used by quicksort
+bool compareEndTimes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
+{
+ return t1.endTime < t2.endTime;
+}
+
+bool compareStartTimes(const QmlEventStartTimeData &t1, const QmlEventStartTimeData &t2)
+{
+ return t1.startTime < t2.startTime;
+}
+
+bool compareStartIndexes(const QmlEventEndTimeData &t1, const QmlEventEndTimeData &t2)
+{
+ return t1.startTimeIndex < t2.startTimeIndex;
+}
+
+class QmlProfilerEventList::QmlProfilerEventListPrivate
+{
+public:
+ QmlProfilerEventListPrivate(QmlProfilerEventList *qq) : q(qq) {}
+
+ QmlProfilerEventList *q;
+
+ // Stored data
+ QmlEventHash m_eventDescriptions;
+ QList<QmlEventEndTimeData> m_endTimeSortedList;
+ QList<QmlEventStartTimeData> m_startTimeSortedList;
+
+ // file to load
+ QString m_filename;
+ ParsingStatus m_parsingStatus;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+QmlProfilerEventList::QmlProfilerEventList(QObject *parent) :
+ QObject(parent), d(new QmlProfilerEventListPrivate(this))
+{
+ d->m_parsingStatus = DoneStatus;
+ setObjectName("QmlProfilerEventStatistics");
+}
+
+QmlProfilerEventList::~QmlProfilerEventList()
+{
+ clear();
+}
+
+void QmlProfilerEventList::clear()
+{
+ foreach (QmlEventData *binding, d->m_eventDescriptions.values())
+ delete binding;
+ d->m_eventDescriptions.clear();
+
+ d->m_endTimeSortedList.clear();
+ d->m_startTimeSortedList.clear();
+ emit countChanged();
+}
+
+QList <QmlEventData *> QmlProfilerEventList::getEventDescriptions() const
+{
+ return d->m_eventDescriptions.values();
+}
+
+void QmlProfilerEventList::addRangedEvent(int type, qint64 startTime, qint64 length,
+ const QStringList &data, const QString &fileName, int line)
+{
+ setParsingStatus(GettingDataStatus);
+
+ const QChar colon = QLatin1Char(':');
+ QString displayName, location, details;
+
+ if (fileName.isEmpty()) {
+ displayName = tr("<bytecode>");
+ location = QString("--:%1:%2").arg(QString::number(type), data.join(" "));
+ } else {
+ const QString filePath = QUrl(fileName).path();
+ displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(line);
+ location = fileName+colon+QString::number(line);
+ }
+
+ QmlEventData *newEvent;
+ if (d->m_eventDescriptions.contains(location)) {
+ newEvent = d->m_eventDescriptions[location];
+ } else {
+
+ // generate details string
+ if (data.isEmpty())
+ details = tr("Source code not available");
+ else {
+ details = data.join(" ").replace('\n'," ").simplified();
+ QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
+ bool match = rewrite.exactMatch(details);
+ if (match) {
+ details = rewrite.cap(1) + ": " + rewrite.cap(3);
+ }
+ if (details.startsWith(QString("file://")))
+ details = details.mid(details.lastIndexOf(QChar('/')) + 1);
+ }
+
+ newEvent = new QmlEventData;
+ newEvent->displayname = displayName;
+ newEvent->filename = fileName;
+ newEvent->location = location;
+ newEvent->line = line;
+ newEvent->eventType = (QmlJsDebugClient::QmlEventType)type;
+ newEvent->details = details;
+ d->m_eventDescriptions.insert(location, newEvent);
+ }
+
+ QmlEventEndTimeData endTimeData;
+ endTimeData.endTime = startTime + length;
+ endTimeData.description = newEvent;
+ endTimeData.startTimeIndex = d->m_startTimeSortedList.count();
+
+ QmlEventStartTimeData startTimeData;
+ startTimeData.startTime = startTime;
+ startTimeData.length = length;
+ startTimeData.description = newEvent;
+ startTimeData.endTimeIndex = d->m_endTimeSortedList.count();
+
+ d->m_endTimeSortedList << endTimeData;
+ d->m_startTimeSortedList << startTimeData;
+
+ emit countChanged();
+}
+
+void QmlProfilerEventList::complete()
+{
+ postProcess();
+}
+
+void QmlProfilerEventList::compileStatistics()
+{
+ // clear existing statistics
+ foreach (QmlEventData *eventDescription, d->m_eventDescriptions.values()) {
+ eventDescription->calls = 0;
+ // maximum possible value
+ eventDescription->minTime = d->m_endTimeSortedList.last().endTime;
+ eventDescription->maxTime = 0;
+ eventDescription->cumulatedDuration = 0;
+ eventDescription->parentList.clear();
+ eventDescription->childrenList.clear();
+ }
+
+ // compute parent-child relationship and call count
+ QHash<int, QmlEventData*> lastParent;
+ foreach (QmlEventStartTimeData eventStartData, d->m_startTimeSortedList) {
+ QmlEventData *eventDescription = eventStartData.description;
+ eventDescription->calls++;
+ eventDescription->cumulatedDuration += eventStartData.length;
+ if (eventDescription->maxTime < eventStartData.length)
+ eventDescription->maxTime = eventStartData.length;
+ if (eventDescription->minTime > eventStartData.length)
+ eventDescription->minTime = eventStartData.length;
+
+
+ if (eventStartData.level > MIN_LEVEL) {
+ if (lastParent.contains(eventStartData.level-1)) {
+ QmlEventData *parentEvent = lastParent[eventStartData.level-1];
+ if (!eventDescription->parentList.contains(parentEvent))
+ eventDescription->parentList.append(parentEvent);
+ if (!parentEvent->childrenList.contains(eventDescription))
+ parentEvent->childrenList.append(eventDescription);
+ }
+ }
+
+ lastParent[eventStartData.level] = eventDescription;
+ }
+
+ // compute percentages
+ double totalTime = 0;
+ foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
+ if (binding->filename.isEmpty())
+ continue;
+ totalTime += binding->cumulatedDuration;
+ }
+
+ foreach (QmlEventData *binding, d->m_eventDescriptions.values()) {
+ if (binding->filename.isEmpty())
+ continue;
+ binding->percentOfTime = binding->cumulatedDuration * 100.0 / totalTime;
+ binding->timePerCall = binding->calls > 0 ? double(binding->cumulatedDuration) / binding->calls : 0;
+ }
+
+ // continue postprocess
+ postProcess();
+}
+
+void QmlProfilerEventList::sortStartTimes()
+{
+ if (d->m_startTimeSortedList.count() < 2)
+ return;
+
+ // assuming startTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+ QList<QmlEventStartTimeData>::iterator itFrom = d->m_startTimeSortedList.end() - 2;
+ QList<QmlEventStartTimeData>::iterator itTo = d->m_startTimeSortedList.end() - 1;
+
+ while (itFrom != d->m_startTimeSortedList.begin() && itTo != d->m_startTimeSortedList.begin()) {
+ // find block to sort
+ while ( itFrom != d->m_startTimeSortedList.begin()
+ && itTo->startTime > itFrom->startTime ) {
+ itTo--;
+ itFrom = itTo - 1;
+ }
+
+ // if we're at the end of the list
+ if (itFrom == d->m_startTimeSortedList.begin())
+ break;
+
+ // find block length
+ while ( itFrom != d->m_startTimeSortedList.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
+ for (int i = 0; i < d->m_startTimeSortedList.length(); i++)
+ d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
+
+ // continue postprocess
+ postProcess();
+}
+
+void QmlProfilerEventList::sortEndTimes()
+{
+ // assuming endTimes is partially sorted
+ // identify blocks of events and sort them with quicksort
+
+ if (d->m_endTimeSortedList.count() < 2)
+ return;
+
+ QList<QmlEventEndTimeData>::iterator itFrom = d->m_endTimeSortedList.begin();
+ QList<QmlEventEndTimeData>::iterator itTo = d->m_endTimeSortedList.begin() + 1;
+
+ while (itTo != d->m_endTimeSortedList.end() && itFrom != d->m_endTimeSortedList.end()) {
+ // find block to sort
+ while ( itTo != d->m_endTimeSortedList.end()
+ && d->m_startTimeSortedList[itTo->startTimeIndex].startTime >
+ d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
+ d->m_startTimeSortedList[itFrom->startTimeIndex].length ) {
+ itFrom++;
+ itTo = itFrom+1;
+ }
+
+ // if we're at the end of the list
+ if (itTo == d->m_endTimeSortedList.end())
+ break;
+
+ // find block length
+ while ( itTo != d->m_endTimeSortedList.end()
+ && d->m_startTimeSortedList[itTo->startTimeIndex].startTime <=
+ d->m_startTimeSortedList[itFrom->startTimeIndex].startTime +
+ d->m_startTimeSortedList[itFrom->startTimeIndex].length )
+ itTo++;
+
+ // sort block
+ qSort(itFrom, itTo, compareEndTimes);
+
+ // move to next block
+ itFrom = itTo;
+ itTo = itFrom+1;
+
+ }
+
+ // link back the startTimes
+ for (int i = 0; i < d->m_endTimeSortedList.length(); i++)
+ d->m_startTimeSortedList[d->m_endTimeSortedList[i].startTimeIndex].endTimeIndex = i;
+
+ // continue postprocess
+ postProcess();
+}
+
+void QmlProfilerEventList::computeNestingLevels()
+{
+ // compute levels
+ QHash <int, qint64> endtimesPerLevel;
+ QList <int> nestingLevels;
+ QList < QHash <int, qint64> > endtimesPerNestingLevel;
+ int level = MIN_LEVEL;
+ endtimesPerLevel[MIN_LEVEL] = 0;
+
+ for (int i = 0; i < QmlJsDebugClient::MaximumQmlEventType; i++) {
+ nestingLevels << MIN_LEVEL;
+ QHash <int, qint64> dummyHash;
+ dummyHash[MIN_LEVEL] = 0;
+ endtimesPerNestingLevel << dummyHash;
+ }
+
+ for (int i=0; i<d->m_startTimeSortedList.count(); i++) {
+ qint64 st = d->m_startTimeSortedList[i].startTime;
+ int type = d->m_startTimeSortedList[i].description->eventType;
+
+ // general level
+ if (endtimesPerLevel[level] > st) {
+ level++;
+ } else {
+ while (level > MIN_LEVEL && endtimesPerLevel[level-1] <= st)
+ level--;
+ }
+ endtimesPerLevel[level] = st + d->m_startTimeSortedList[i].length;
+
+ // per type
+ if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
+ nestingLevels[type]++;
+ } else {
+ while (nestingLevels[type] > MIN_LEVEL &&
+ endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
+ nestingLevels[type]--;
+ }
+ endtimesPerNestingLevel[type][nestingLevels[type]] = st + d->m_startTimeSortedList[i].length;
+
+ d->m_startTimeSortedList[i].level = level;
+ d->m_startTimeSortedList[i].nestingLevel = nestingLevels[type];
+ }
+}
+
+void QmlProfilerEventList::computeNestingDepth()
+{
+ QHash <int, int> nestingDepth;
+ for (int i = 0; i < d->m_endTimeSortedList.count(); i++) {
+ int type = d->m_endTimeSortedList[i].description->eventType;
+ int nestingInType = d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingLevel;
+ if (!nestingDepth.contains(type))
+ nestingDepth[type] = nestingInType;
+ else {
+ int nd = nestingDepth[type];
+ nestingDepth[type] = nd > nestingInType ? nd : nestingInType;
+ }
+
+ d->m_startTimeSortedList[ d->m_endTimeSortedList[i].startTimeIndex ].nestingDepth = nestingDepth[type];
+ if (nestingInType == MIN_LEVEL)
+ nestingDepth[type] = MIN_LEVEL;
+ }
+}
+
+void QmlProfilerEventList::postProcess()
+{
+ switch (d->m_parsingStatus) {
+ case GettingDataStatus: {
+ setParsingStatus(SortingListsStatus);
+ QTimer::singleShot(50, this, SLOT(sortStartTimes()));
+ break;
+ }
+ case SortingEndsStatus: {
+ setParsingStatus(SortingListsStatus);
+ QTimer::singleShot(50, this, SLOT(sortEndTimes()));
+ break;
+ }
+ case SortingListsStatus: {
+ setParsingStatus(ComputingLevelsStatus);
+ QTimer::singleShot(50, this, SLOT(computeLevels()));
+ break;
+ }
+ case ComputingLevelsStatus: {
+ setParsingStatus(CompilingStatisticsStatus);
+ QTimer::singleShot(50, this, SLOT(compileStatistics()));
+ break;
+ }
+ case CompilingStatisticsStatus: {
+ linkEndsToStarts();
+ setParsingStatus(DoneStatus);
+ emit dataReady();
+ break;
+ }
+ default: break;
+ }
+
+}
+
+void QmlProfilerEventList::setParsingStatus(ParsingStatus ps)
+{
+ if (d->m_parsingStatus != ps) {
+ d->m_parsingStatus = ps;
+ emit parsingStatusChanged();
+ }
+}
+
+ParsingStatus QmlProfilerEventList::getParsingStatus() const
+{
+ return d->m_parsingStatus;
+}
+
+void QmlProfilerEventList::linkEndsToStarts()
+{
+ for (int i = 0; i < d->m_startTimeSortedList.count(); i++)
+ d->m_endTimeSortedList[d->m_startTimeSortedList[i].endTimeIndex].startTimeIndex = i;
+}
+
+void QmlProfilerEventList::computeLevels()
+{
+ computeNestingLevels();
+ computeNestingDepth();
+ // continue postprocess
+ postProcess();
+}
+
+// get list of events between A and B:
+// find fist event with endtime after A -> aa
+// find last event with starttime before B -> bb
+// list is from parent of aa with level=0 to bb, in the "sorted by starttime" list
+int QmlProfilerEventList::findFirstIndex(qint64 startTime) const
+{
+ int candidate = -1;
+ // in the "endtime" list, find the first event that ends after startTime
+ if (d->m_endTimeSortedList.isEmpty())
+ return 0; // -1
+ if (d->m_endTimeSortedList.length() == 1 || d->m_endTimeSortedList.first().endTime >= startTime)
+ candidate = 0;
+ else
+ if (d->m_endTimeSortedList.last().endTime <= startTime)
+ return 0; // -1
+
+ if (candidate == -1)
+ {
+ int fromIndex = 0;
+ int toIndex = d->m_endTimeSortedList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->m_endTimeSortedList[midIndex].endTime < startTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ candidate = toIndex;
+ }
+
+ int ndx = d->m_endTimeSortedList[candidate].startTimeIndex;
+
+ // and then go to the parent
+ while (d->m_startTimeSortedList[ndx].level != MIN_LEVEL && ndx > 0)
+ ndx--;
+
+ return ndx;
+}
+
+int QmlProfilerEventList::findLastIndex(qint64 endTime) const
+{
+ // in the "starttime" list, find the last event that starts before endtime
+ if (d->m_startTimeSortedList.isEmpty())
+ return 0; // -1
+ if (d->m_startTimeSortedList.first().startTime >= endTime)
+ return 0; // -1
+ if (d->m_startTimeSortedList.length() == 1)
+ return 0;
+ if (d->m_startTimeSortedList.last().startTime <= endTime)
+ return d->m_startTimeSortedList.count()-1;
+
+ int fromIndex = 0;
+ int toIndex = d->m_startTimeSortedList.count()-1;
+ while (toIndex - fromIndex > 1) {
+ int midIndex = (fromIndex + toIndex)/2;
+ if (d->m_startTimeSortedList[midIndex].startTime < endTime)
+ fromIndex = midIndex;
+ else
+ toIndex = midIndex;
+ }
+
+ return fromIndex;
+}
+
+qint64 QmlProfilerEventList::firstTimeMark() const
+{
+ if (d->m_startTimeSortedList.isEmpty())
+ return 0;
+ else {
+ return d->m_startTimeSortedList[0].startTime;
+ }
+}
+
+qint64 QmlProfilerEventList::lastTimeMark() const
+{
+ if (d->m_endTimeSortedList.isEmpty())
+ return 0;
+ else {
+ return d->m_endTimeSortedList.last().endTime;
+ }
+}
+
+int QmlProfilerEventList::count() const
+{
+ return d->m_startTimeSortedList.count();
+}
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+void QmlProfilerEventList::save(const QString &filename) const
+{
+ if (count() == 0) {
+ showErrorDialog(tr("No data to save"));
+ return;
+ }
+
+ QFile file(filename);
+ if (!file.open(QIODevice::WriteOnly)) {
+ showErrorDialog(tr("Could not open %1 for writing").arg(filename));
+ return;
+ }
+
+ QXmlStreamWriter stream(&file);
+ stream.setAutoFormatting(true);
+ stream.writeStartDocument();
+
+ stream.writeStartElement("trace");
+
+ stream.writeStartElement("eventData");
+ foreach (const QmlEventData *eventData, d->m_eventDescriptions.values()) {
+ stream.writeStartElement("event");
+ stream.writeAttribute("index", QString::number(d->m_eventDescriptions.keys().indexOf(eventData->location)));
+ stream.writeTextElement("displayname", eventData->displayname);
+ stream.writeTextElement("type", QString::number(eventData->eventType));
+ if (!eventData->filename.isEmpty()) {
+ stream.writeTextElement("filename", eventData->filename);
+ stream.writeTextElement("line", QString::number(eventData->line));
+ }
+ stream.writeTextElement("details", eventData->details);
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventData
+
+ stream.writeStartElement("eventList");
+ foreach (const QmlEventStartTimeData &rangedEvent, d->m_startTimeSortedList) {
+ stream.writeStartElement("range");
+ stream.writeAttribute("startTime", QString::number(rangedEvent.startTime));
+ stream.writeAttribute("duration", QString::number(rangedEvent.length));
+ stream.writeAttribute("eventIndex", QString::number(d->m_eventDescriptions.keys().indexOf(rangedEvent.description->location)));
+ stream.writeEndElement();
+ }
+ stream.writeEndElement(); // eventList
+
+ stream.writeEndElement(); // trace
+ stream.writeEndDocument();
+
+ file.close();
+}
+
+void QmlProfilerEventList::setFilename(const QString &filename)
+{
+ d->m_filename = filename;
+}
+
+void QmlProfilerEventList::load(const QString &filename)
+{
+ setFilename(filename);
+ load();
+}
+
+// "be strict in your output but tolerant in your inputs"
+void QmlProfilerEventList::load()
+{
+ QString filename = d->m_filename;
+
+ QFile file(filename);
+
+ if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ showErrorDialog(tr("Could not open %1 for reading").arg(filename));
+ return;
+ }
+
+ setParsingStatus(GettingDataStatus);
+
+ // erase current
+ clear();
+
+ QHash <int, QmlEventData *> descriptionBuffer;
+ QmlEventData *currentEvent = 0;
+ bool startTimesAreSorted = true;
+
+ QXmlStreamReader stream(&file);
+
+ while (!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 == "event") {
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("index")) {
+ int ndx = attributes.value("index").toString().toInt();
+ if (!descriptionBuffer.value(ndx))
+ descriptionBuffer[ndx] = new QmlEventData;
+ currentEvent = descriptionBuffer[ndx];
+ }
+ break;
+ }
+ if (elementName == "range") {
+ QmlEventStartTimeData rangedEvent;
+ QXmlStreamAttributes attributes = stream.attributes();
+ if (attributes.hasAttribute("startTime"))
+ rangedEvent.startTime = attributes.value("startTime").toString().toLongLong();
+ if (attributes.hasAttribute("duration"))
+ rangedEvent.length = attributes.value("duration").toString().toLongLong();
+ if (attributes.hasAttribute("eventIndex")) {
+ int ndx = attributes.value("eventIndex").toString().toInt();
+ if (!descriptionBuffer.value(ndx))
+ descriptionBuffer[ndx] = new QmlEventData();
+ rangedEvent.description = descriptionBuffer.value(ndx);
+ }
+ rangedEvent.endTimeIndex = d->m_endTimeSortedList.length();
+
+ if (!d->m_startTimeSortedList.isEmpty()
+ && rangedEvent.startTime < d->m_startTimeSortedList.last().startTime)
+ startTimesAreSorted = false;
+ d->m_startTimeSortedList << rangedEvent;
+
+ QmlEventEndTimeData endTimeEvent;
+ endTimeEvent.endTime = rangedEvent.startTime + rangedEvent.length;
+ endTimeEvent.startTimeIndex = d->m_startTimeSortedList.length()-1;
+ endTimeEvent.description = rangedEvent.description;
+ d->m_endTimeSortedList << endTimeEvent;
+ break;
+ }
+
+ // the remaining are eventdata elements
+ if (!currentEvent)
+ break;
+ stream.readNext();
+ if (stream.tokenType() != QXmlStreamReader::Characters)
+ break;
+
+ QString readData = stream.text().toString();
+
+ if (elementName == "displayname") {
+ currentEvent->displayname = readData;
+ break;
+ }
+ if (elementName == "type") {
+ currentEvent->eventType = QmlJsDebugClient::QmlEventType(readData.toInt());
+ break;
+ }
+ if (elementName == "filename") {
+ currentEvent->filename = readData;
+ break;
+ }
+ if (elementName == "line") {
+ currentEvent->line = readData.toInt();
+ break;
+ }
+ if (elementName == "details") {
+ currentEvent->details = readData;
+ break;
+ }
+ break;
+ }
+ case QXmlStreamReader::EndElement : {
+ if (elementName == "event")
+ currentEvent = 0;
+ break;
+ }
+ default: break;
+ }
+ }
+
+ file.close();
+
+ if (stream.hasError()) {
+ showErrorDialog(tr("Error while parsing %1").arg(filename));
+ clear();
+ return;
+ }
+
+ stream.clear();
+
+ // move the buffered data to the details cache
+ foreach (QmlEventData *desc, descriptionBuffer.values()) {
+ QString location = QString("%1:%2:%3").arg(QString::number(desc->eventType), desc->displayname, desc->details);
+ desc->location = location;
+ d->m_eventDescriptions[location] = desc;
+ }
+
+ // sort startTimeSortedList
+ if (!startTimesAreSorted) {
+ qSort(d->m_startTimeSortedList.begin(), d->m_startTimeSortedList.end(), compareStartTimes);
+ for (int i = 0; i< d->m_startTimeSortedList.length(); i++) {
+ QmlEventStartTimeData startTimeData = d->m_startTimeSortedList[i];
+ d->m_endTimeSortedList[startTimeData.endTimeIndex].startTimeIndex = i;
+ }
+ qSort(d->m_endTimeSortedList.begin(), d->m_endTimeSortedList.end(), compareStartIndexes);
+ }
+
+ emit countChanged();
+
+ setParsingStatus(SortingEndsStatus);
+
+ descriptionBuffer.clear();
+
+ postProcess();
+}
+
+void QmlProfilerEventList::showErrorDialog(const QString &st ) const
+{
+ Core::ICore * const core = Core::ICore::instance();
+ QMessageBox *errorDialog = new QMessageBox(core->mainWindow());
+ errorDialog->setIcon(QMessageBox::Warning);
+ errorDialog->setWindowTitle(tr("QML Profiler"));
+ errorDialog->setText( st );
+ errorDialog->setStandardButtons(QMessageBox::Ok);
+ errorDialog->setDefaultButton(QMessageBox::Ok);
+ errorDialog->setModal(false);
+ errorDialog->show();
+}
+
+///////////////////////////////////////////////
+qint64 QmlProfilerEventList::getStartTime(int index) const {
+ return d->m_startTimeSortedList[index].startTime;
+}
+
+qint64 QmlProfilerEventList::getEndTime(int index) const {
+ return d->m_startTimeSortedList[index].startTime + d->m_startTimeSortedList[index].length;
+}
+
+qint64 QmlProfilerEventList::getDuration(int index) const {
+ return d->m_startTimeSortedList[index].length;
+}
+
+int QmlProfilerEventList::getType(int index) const {
+ return d->m_startTimeSortedList[index].description->eventType;
+}
+
+int QmlProfilerEventList::getNestingLevel(int index) const {
+ return d->m_startTimeSortedList[index].nestingLevel;
+}
+
+int QmlProfilerEventList::getNestingDepth(int index) const {
+ return d->m_startTimeSortedList[index].nestingDepth;
+}
+
+QString QmlProfilerEventList::getFilename(int index) const {
+ return d->m_startTimeSortedList[index].description->filename;
+}
+
+int QmlProfilerEventList::getLine(int index) const {
+ return d->m_startTimeSortedList[index].description->line;
+}
+
+QString QmlProfilerEventList::getDetails(int index) const {
+ return d->m_startTimeSortedList[index].description->details;
+}
+
+
+} // namespace Internal
+} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilereventlist.h b/src/plugins/qmlprofiler/qmlprofilereventlist.h
new file mode 100644
index 0000000000..767da098d7
--- /dev/null
+++ b/src/plugins/qmlprofiler/qmlprofilereventlist.h
@@ -0,0 +1,138 @@
+/**************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Nokia Corporation (info@qt.nokia.com)
+**
+**
+** GNU Lesser General Public License Usage
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** Other Usage
+**
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at info@qt.nokia.com.
+**
+**************************************************************************/
+
+#ifndef QMLPROFILEREVENTLIST_H
+#define QMLPROFILEREVENTLIST_H
+
+#include <QObject>
+#include <QtCore/QHash>
+#include "qmljsdebugclient/qmlprofilereventtypes.h"
+
+namespace QmlProfiler {
+namespace Internal {
+
+struct QmlEventData
+{
+ QString displayname;
+ QString filename;
+ QString location;
+ QString details;
+ int line;
+ QmlJsDebugClient::QmlEventType eventType;
+ QList< QmlEventData *> parentList;
+ QList< QmlEventData *> childrenList;
+ qint64 cumulatedDuration;
+ qint64 calls;
+ qint64 minTime;
+ qint64 maxTime;
+ double timePerCall;
+ double percentOfTime;
+};
+
+typedef QHash<QString, QmlEventData *> QmlEventHash;
+typedef QList<QmlEventData *> QmlEventDescriptions;
+
+enum ParsingStatus {
+ GettingDataStatus = 0,
+ SortingListsStatus = 1,
+ SortingEndsStatus = 2,
+ ComputingLevelsStatus = 3,
+ CompilingStatisticsStatus = 4,
+ DoneStatus = 5
+};
+
+class QmlProfilerEventList : public QObject
+{
+ Q_OBJECT
+public:
+
+ explicit QmlProfilerEventList(QObject *parent = 0);
+ ~QmlProfilerEventList();
+
+ QmlEventDescriptions getEventDescriptions() const;
+
+ int findFirstIndex(qint64 startTime) const;
+ int findLastIndex(qint64 endTime) const;
+ Q_INVOKABLE qint64 firstTimeMark() const;
+ Q_INVOKABLE qint64 lastTimeMark() const;
+
+ Q_INVOKABLE int count() const;
+ void setParsingStatus(ParsingStatus ps);
+ Q_INVOKABLE ParsingStatus getParsingStatus() const;
+
+ // data access
+ 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 QString getDetails(int index) const;
+
+ void showErrorDialog(const QString &st ) const;
+signals:
+ void dataReady();
+ void countChanged();
+ void parsingStatusChanged();
+
+public slots:
+ void clear();
+ void addRangedEvent(int type, qint64 startTime, qint64 length,
+ const QStringList &data, const QString &fileName, int line);
+ void complete();
+ void save(const QString &filename) const;
+ void load(const QString &filename);
+ void setFilename(const QString &filename);
+ void load();
+
+private slots:
+ void postProcess();
+ void sortEndTimes();
+ void sortStartTimes();
+ void computeLevels();
+ void computeNestingLevels();
+ void computeNestingDepth();
+ void compileStatistics();
+ void linkEndsToStarts();
+
+private:
+ class QmlProfilerEventListPrivate;
+ QmlProfilerEventListPrivate *d;
+};
+
+
+} // namespace Internal
+} // namespace QmlProfiler
+
+#endif // QMLPROFILEREVENTLIST_H
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.cpp b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
index 28de7cb49e..114c696e0d 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.cpp
@@ -35,14 +35,20 @@
#include <QtCore/QUrl>
#include <QtCore/QHash>
+#include <QtGui/QStandardItem>
#include <QtGui/QHeaderView>
-#include <QtGui/QStandardItemModel>
+
+#include <QtGui/QContextMenuEvent>
+#include <QDebug>
+
using namespace QmlJsDebugClient;
namespace QmlProfiler {
namespace Internal {
+////////////////////////////////////////////////////////////////////////////////////
+
class EventsViewItem : public QStandardItem
{
public:
@@ -68,28 +74,12 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-class QmlProfilerEventStatistics::QmlProfilerEventStatisticsPrivate
-{
-public:
- QmlProfilerEventStatisticsPrivate(QmlProfilerEventStatistics *qq) : q(qq) {}
-
- void postProcess();
-
- QmlProfilerEventStatistics *q;
- QmlEventHash m_rootHash;
- QHash<int, QmlEventList> m_pendingEvents;
- int m_lastLevel;
-};
-
-
-////////////////////////////////////////////////////////////////////////////////////
-
class QmlProfilerEventsView::QmlProfilerEventsViewPrivate
{
public:
QmlProfilerEventsViewPrivate(QmlProfilerEventsView *qq) : q(qq) {}
- void buildModelFromList(const QmlEventList &list, QStandardItem *parentItem, const QmlEventList &visitedFunctionsList = QmlEventList() );
+ void buildModelFromList(const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList = QmlEventDescriptions() );
int getFieldCount();
QString displayTime(double time) const;
QString nameForType(int typeNumber) const;
@@ -97,7 +87,7 @@ public:
QmlProfilerEventsView *q;
- QmlProfilerEventStatistics *m_eventStatistics;
+ QmlProfilerEventList *m_eventStatistics;
QStandardItemModel *m_model;
QList<bool> m_fieldShown;
bool m_showAnonymous;
@@ -107,151 +97,7 @@ public:
////////////////////////////////////////////////////////////////////////////////////
-QmlProfilerEventStatistics::QmlProfilerEventStatistics(QObject *parent) :
- QObject(parent), d(new QmlProfilerEventStatisticsPrivate(this))
-{
- setObjectName("QmlProfilerEventStatistics");
- d->m_lastLevel = -1;
-}
-
-QmlProfilerEventStatistics::~QmlProfilerEventStatistics()
-{
- clear();
-}
-
-void QmlProfilerEventStatistics::clear()
-{
- foreach (int levelNumber, d->m_pendingEvents.keys())
- d->m_pendingEvents[levelNumber].clear();
-
- d->m_lastLevel = -1;
-
- foreach (QmlEventData *binding, d->m_rootHash.values())
- delete binding;
- d->m_rootHash.clear();
-}
-
-QList <QmlEventData *> QmlProfilerEventStatistics::getEventList() const
-{
- return d->m_rootHash.values();
-}
-
-int QmlProfilerEventStatistics::eventCount() const
-{
- return d->m_rootHash.size();
-}
-
-void QmlProfilerEventStatistics::addRangedEvent(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length,
- const QStringList &data, const QString &fileName, int line)
-{
- Q_UNUSED(startTime);
- Q_UNUSED(nestingInType);
-
- const QChar colon = QLatin1Char(':');
- QString displayName, location, details;
-
- if (data.isEmpty())
- details = tr("Source code not available");
- else {
- details = data.join(" ").replace('\n'," ").simplified();
- QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)");
- bool match = rewrite.exactMatch(details);
- if (match) {
- details = rewrite.cap(1) + ": " + rewrite.cap(3);
- }
- if (details.startsWith(QString("file://")))
- details = details.mid(details.lastIndexOf(QChar('/')) + 1);
- }
-
- if (fileName.isEmpty()) {
- displayName = tr("<bytecode>");
- location = QString("--:%1:%2").arg(QString::number(type), details);
- } else {
- const QString filePath = QUrl(fileName).path();
- displayName = filePath.mid(filePath.lastIndexOf(QChar('/')) + 1) + colon + QString::number(line);
- location = fileName+colon+QString::number(line);
- }
-
-
- // New Data: if it's not in the hash, put it there
- // if it's in the hash, get the reference from the hash
- QmlEventData *newBinding;
- QmlEventHash::iterator it = d->m_rootHash.find(location);
- if (it != d->m_rootHash.end()) {
- newBinding = it.value();
- newBinding->duration += length;
- newBinding->calls++;
- if (newBinding->maxTime < length)
- newBinding->maxTime = length;
- if (newBinding->minTime > length)
- newBinding->minTime = length;
- } else {
- newBinding = new QmlEventData;
- newBinding->calls = 1;
- newBinding->duration = length;
- newBinding->displayname = new QString(displayName);
- newBinding->filename = new QString(fileName);
- newBinding->location = new QString(location);
- newBinding->line = line;
- newBinding->minTime = length;
- newBinding->maxTime = length;
- newBinding->level = nestingLevel;
- newBinding->eventType = (QmlEventType)type;
- newBinding->details = new QString(details);
- newBinding->parentList = new QmlEventList();
- newBinding->childrenList = new QmlEventList();
- d->m_rootHash.insert(location, newBinding);
- }
-
- if (nestingLevel < d->m_lastLevel) {
- // I'm the parent of the former
- if (d->m_pendingEvents.contains(nestingLevel+1)) {
- foreach (QmlEventData *child, d->m_pendingEvents[nestingLevel + 1]) {
- if (!newBinding->childrenList->contains(child))
- newBinding->childrenList->append(child);
- if (!child->parentList->contains(newBinding))
- child->parentList->append(newBinding);
- }
- d->m_pendingEvents[nestingLevel + 1].clear();
- }
-
- }
-
- if (nestingLevel > 1 && !d->m_pendingEvents[nestingLevel].contains(newBinding)) {
- // I'm not root... there will come a parent later
- d->m_pendingEvents[nestingLevel].append(newBinding);
- }
-
- d->m_lastLevel = nestingLevel;
-}
-
-void QmlProfilerEventStatistics::complete()
-{
- d->postProcess();
- emit dataReady();
-}
-
-void QmlProfilerEventStatistics::QmlProfilerEventStatisticsPrivate::postProcess()
-{
- double totalTime = 0;
-
- foreach (QmlEventData *binding, m_rootHash.values()) {
- if (binding->filename->isEmpty())
- continue;
- totalTime += binding->duration;
- }
-
- foreach (QmlEventData *binding, m_rootHash.values()) {
- if (binding->filename->isEmpty())
- continue;
- binding->percentOfTime = binding->duration * 100.0 / totalTime;
- binding->timePerCall = binding->calls > 0 ? double(binding->duration) / binding->calls : 0;
- }
-}
-
-////////////////////////////////////////////////////////////////////////////////////
-
-QmlProfilerEventsView::QmlProfilerEventsView(QWidget *parent, QmlProfilerEventStatistics *model) :
+QmlProfilerEventsView::QmlProfilerEventsView(QWidget *parent, QmlProfilerEventList *model) :
QTreeView(parent), d(new QmlProfilerEventsViewPrivate(this))
{
setObjectName("QmlProfilerEventsView");
@@ -281,7 +127,7 @@ QmlProfilerEventsView::~QmlProfilerEventsView()
delete d->m_model;
}
-void QmlProfilerEventsView::setEventStatisticsModel( QmlProfilerEventStatistics *model )
+void QmlProfilerEventsView::setEventStatisticsModel( QmlProfilerEventList *model )
{
if (d->m_eventStatistics)
disconnect(d->m_eventStatistics,SIGNAL(dataReady()),this,SLOT(buildModel()));
@@ -414,7 +260,7 @@ void QmlProfilerEventsView::buildModel()
{
if (d->m_eventStatistics) {
clear();
- d->buildModelFromList( d->m_eventStatistics->getEventList(), d->m_model->invisibleRootItem() );
+ d->buildModelFromList( d->m_eventStatistics->getEventDescriptions(), d->m_model->invisibleRootItem() );
bool hasBranches = d->m_fieldShown[Parents] || d->m_fieldShown[Children];
setRootIsDecorated(hasBranches);
@@ -434,18 +280,18 @@ void QmlProfilerEventsView::buildModel()
}
}
-void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( const QmlEventList &list, QStandardItem *parentItem, const QmlEventList &visitedFunctionsList )
+void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( const QmlEventDescriptions &list, QStandardItem *parentItem, const QmlEventDescriptions &visitedFunctionsList )
{
foreach (QmlEventData *binding, list) {
if (visitedFunctionsList.contains(binding))
continue;
- if ((!m_showAnonymous) && binding->filename->isEmpty())
+ if ((!m_showAnonymous) && binding->filename.isEmpty())
continue;
QList<QStandardItem *> newRow;
if (m_fieldShown[Name]) {
- newRow << new EventsViewItem(*binding->displayname);
+ newRow << new EventsViewItem(binding->displayname);
}
if (m_fieldShown[Type]) {
@@ -459,8 +305,8 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
}
if (m_fieldShown[TotalDuration]) {
- newRow << new EventsViewItem(displayTime(binding->duration));
- newRow.last()->setData(QVariant(binding->duration));
+ newRow << new EventsViewItem(displayTime(binding->cumulatedDuration));
+ newRow.last()->setData(QVariant(binding->cumulatedDuration));
}
if (m_fieldShown[CallCount]) {
@@ -484,8 +330,8 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
}
if (m_fieldShown[Details]) {
- newRow << new EventsViewItem(*binding->details);
- newRow.last()->setData(QVariant(*binding->details));
+ newRow << new EventsViewItem(binding->details);
+ newRow.last()->setData(QVariant(binding->details));
}
if (!newRow.isEmpty()) {
@@ -494,25 +340,25 @@ void QmlProfilerEventsView::QmlProfilerEventsViewPrivate::buildModelFromList( co
item->setEditable(false);
// metadata
- newRow.at(0)->setData(QVariant(*binding->location),LocationRole);
- newRow.at(0)->setData(QVariant(*binding->filename),FilenameRole);
+ newRow.at(0)->setData(QVariant(binding->location),LocationRole);
+ newRow.at(0)->setData(QVariant(binding->filename),FilenameRole);
newRow.at(0)->setData(QVariant(binding->line),LineRole);
// append
parentItem->appendRow(newRow);
- if (m_fieldShown[Parents] && !binding->parentList->isEmpty()) {
- QmlEventList newParentList(visitedFunctionsList);
+ if (m_fieldShown[Parents] && !binding->parentList.isEmpty()) {
+ QmlEventDescriptions newParentList(visitedFunctionsList);
newParentList.append(binding);
- buildModelFromList(*binding->parentList, newRow.at(0), newParentList);
+ buildModelFromList(binding->parentList, newRow.at(0), newParentList);
}
- if (m_fieldShown[Children] && !binding->childrenList->isEmpty()) {
- QmlEventList newChildrenList(visitedFunctionsList);
+ if (m_fieldShown[Children] && !binding->childrenList.isEmpty()) {
+ QmlEventDescriptions newChildrenList(visitedFunctionsList);
newChildrenList.append(binding);
- buildModelFromList(*binding->childrenList, newRow.at(0), newChildrenList);
+ buildModelFromList(binding->childrenList, newRow.at(0), newChildrenList);
}
}
}
@@ -556,5 +402,10 @@ void QmlProfilerEventsView::jumpToItem(const QModelIndex &index)
emit gotoSourceLocation(fileName, line);
}
+void QmlProfilerEventsView::contextMenuEvent(QContextMenuEvent *ev)
+{
+ emit contextMenuRequested(ev->globalPos());
+}
+
} // namespace Internal
} // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilereventview.h b/src/plugins/qmlprofiler/qmlprofilereventview.h
index a04b6d3128..60f5b13acc 100644
--- a/src/plugins/qmlprofiler/qmlprofilereventview.h
+++ b/src/plugins/qmlprofiler/qmlprofilereventview.h
@@ -35,39 +35,11 @@
#include <QTreeView>
#include <qmljsdebugclient/qmlprofilereventtypes.h>
+#include "qmlprofilereventlist.h"
namespace QmlProfiler {
namespace Internal {
-struct QmlEventData
-{
- QmlEventData() : displayname(0) , filename(0) , location(0) , details(0),
- line(0), eventType(QmlJsDebugClient::MaximumQmlEventType), level(-1), parentList(0), childrenList(0) {}
- ~QmlEventData() {
- delete displayname;
- delete filename;
- delete location;
- delete parentList;
- delete childrenList;
- }
- QString *displayname;
- QString *filename;
- QString *location;
- QString *details;
- int line;
- QmlJsDebugClient::QmlEventType eventType;
- qint64 level;
- QList< QmlEventData *> *parentList;
- QList< QmlEventData *> *childrenList;
- qint64 duration;
- qint64 calls;
- qint64 minTime;
- qint64 maxTime;
- double timePerCall;
- double percentOfTime;
-};
-
-
typedef QHash<QString, QmlEventData *> QmlEventHash;
typedef QList<QmlEventData *> QmlEventList;
@@ -77,31 +49,6 @@ enum ItemRole {
LineRole = Qt::UserRole+3
};
-class QmlProfilerEventStatistics : public QObject
-{
- Q_OBJECT
-public:
-
- explicit QmlProfilerEventStatistics(QObject *parent = 0);
- ~QmlProfilerEventStatistics();
-
- QmlEventList getEventList() const;
- int eventCount() const;
-
-signals:
- void dataReady();
-
-public slots:
- void clear();
- void addRangedEvent(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length,
- const QStringList &data, const QString &fileName, int line);
- void complete();
-
-private:
- class QmlProfilerEventStatisticsPrivate;
- QmlProfilerEventStatisticsPrivate *d;
-};
-
class QmlProfilerEventsView : public QTreeView
{
Q_OBJECT
@@ -130,16 +77,17 @@ public:
MaxViewTypes
};
- explicit QmlProfilerEventsView(QWidget *parent, QmlProfilerEventStatistics *model);
+ explicit QmlProfilerEventsView(QWidget *parent, QmlProfilerEventList *model);
~QmlProfilerEventsView();
- void setEventStatisticsModel( QmlProfilerEventStatistics *model );
+ void setEventStatisticsModel( QmlProfilerEventList *model );
void setFieldViewable(Fields field, bool show);
void setViewType(ViewTypes type);
void setShowAnonymousEvents( bool showThem );
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber);
+ void contextMenuRequested(const QPoint &position);
public slots:
void clear();
@@ -148,6 +96,7 @@ public slots:
private:
void setHeaderLabels();
+ void contextMenuEvent(QContextMenuEvent *ev);
private:
class QmlProfilerEventsViewPrivate;
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index 855472c19d..00738542f2 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -35,6 +35,7 @@
#include "qmlprofilerplugin.h"
#include "qmlprofilerconstants.h"
#include "qmlprofilerattachdialog.h"
+#include "qmlprofilereventlist.h"
#include "qmlprofilereventview.h"
#include "tracewindow.h"
@@ -76,6 +77,8 @@
#include <QtGui/QToolButton>
#include <QtGui/QMessageBox>
#include <QtGui/QDockWidget>
+#include <QtGui/QFileDialog>
+#include <QtGui/QMenu>
using namespace Analyzer;
using namespace QmlProfiler::Internal;
@@ -93,7 +96,6 @@ public:
QTimer m_connectionTimer;
int m_connectionAttempts;
TraceWindow *m_traceWindow;
- QmlProfilerEventStatistics *m_statistics;
QmlProfilerEventsView *m_eventsView;
QmlProfilerEventsView *m_calleeView;
QmlProfilerEventsView *m_callerView;
@@ -165,6 +167,19 @@ IAnalyzerTool::ToolMode QmlProfilerTool::toolMode() const
return AnyMode;
}
+void QmlProfilerTool::showContextMenu(const QPoint &position)
+{
+ QMenu menu;
+ QAction *loadAction = menu.addAction(tr("Load QML Trace"));
+ QAction *saveAction = menu.addAction(tr("Save QML Trace"));
+
+ QAction *selectedAction = menu.exec(position);
+ if (selectedAction == loadAction)
+ showLoadDialog();
+ if (selectedAction == saveAction)
+ showSaveDialog();
+}
+
IAnalyzerEngine *QmlProfilerTool::createEngine(const AnalyzerStartParameters &sp,
ProjectExplorer::RunConfiguration *runConfiguration)
{
@@ -239,27 +254,26 @@ QWidget *QmlProfilerTool::createWidgets()
connect(d->m_traceWindow, SIGNAL(gotoSourceLocation(QString,int)),this, SLOT(gotoSourceLocation(QString,int)));
connect(d->m_traceWindow, SIGNAL(timeChanged(qreal)), this, SLOT(updateTimer(qreal)));
+ connect(d->m_traceWindow, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- d->m_statistics = new QmlProfilerEventStatistics(mw);
- d->m_eventsView = new QmlProfilerEventsView(mw, d->m_statistics);
+ d->m_eventsView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
d->m_eventsView->setViewType(QmlProfilerEventsView::EventsView);
- connect(d->m_traceWindow, SIGNAL(range(int,int,int,qint64,qint64,QStringList,QString,int)),
- d->m_statistics, SLOT(addRangedEvent(int,int,int,qint64,qint64,QStringList,QString,int)));
- connect(d->m_traceWindow, SIGNAL(viewUpdated()),
- d->m_statistics, SLOT(complete()));
connect(d->m_eventsView, SIGNAL(gotoSourceLocation(QString,int)),
this, SLOT(gotoSourceLocation(QString,int)));
+ connect(d->m_eventsView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- d->m_calleeView = new QmlProfilerEventsView(mw, d->m_statistics);
+ d->m_calleeView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
d->m_calleeView->setViewType(QmlProfilerEventsView::CalleesView);
connect(d->m_calleeView, SIGNAL(gotoSourceLocation(QString,int)),
this, SLOT(gotoSourceLocation(QString,int)));
+ connect(d->m_calleeView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
- d->m_callerView = new QmlProfilerEventsView(mw, d->m_statistics);
+ d->m_callerView = new QmlProfilerEventsView(mw, d->m_traceWindow->getEventList());
d->m_callerView->setViewType(QmlProfilerEventsView::CallersView);
connect(d->m_callerView, SIGNAL(gotoSourceLocation(QString,int)),
this, SLOT(gotoSourceLocation(QString,int)));
+ connect(d->m_callerView, SIGNAL(contextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
QDockWidget *eventsDock = AnalyzerManager::createDockWidget
(this, tr("Events"), d->m_eventsView, Qt::BottomDockWidgetArea);
@@ -403,7 +417,7 @@ void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber)
}
void QmlProfilerTool::correctTimer() {
- if (d->m_statistics->eventCount() == 0)
+ if (d->m_traceWindow->getEventList()->count() == 0)
updateTimer(0);
}
@@ -423,7 +437,6 @@ void QmlProfilerTool::updateProjectFileList()
void QmlProfilerTool::clearDisplay()
{
d->m_traceWindow->clearDisplay();
- d->m_statistics->clear();
d->m_eventsView->clear();
d->m_calleeView->clear();
d->m_callerView->clear();
@@ -546,3 +559,26 @@ void QmlProfilerTool::logError(const QString &msg)
Core::MessageManager *messageManager = Core::MessageManager::instance();
messageManager->printToOutputPane(msg, true);
}
+
+void QmlProfilerTool::showSaveDialog()
+{
+ Core::ICore *core = Core::ICore::instance();
+ QString filename = QFileDialog::getSaveFileName(core->mainWindow(), tr("Save QML Trace"), QString(), tr("QML traces (*.xml)"));
+ if (!filename.isEmpty()) {
+ if (!filename.endsWith(QLatin1String(".xml")))
+ filename += QLatin1String(".xml");
+ d->m_traceWindow->getEventList()->save(filename);
+ }
+}
+
+void QmlProfilerTool::showLoadDialog()
+{
+ Core::ICore *core = Core::ICore::instance();
+ QString filename = QFileDialog::getOpenFileName(core->mainWindow(), tr("Load QML Trace"), QString(), tr("QML traces (*.xml)"));
+
+ if (!filename.isEmpty()) {
+ // delayed load (prevent graphical artifacts due to long load time)
+ d->m_traceWindow->getEventList()->setFilename(filename);
+ QTimer::singleShot(100, d->m_traceWindow->getEventList(), SLOT(load()));
+ }
+}
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h
index 2c627e9c87..594f146585 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.h
+++ b/src/plugins/qmlprofiler/qmlprofilertool.h
@@ -36,6 +36,8 @@
#include <analyzerbase/ianalyzertool.h>
#include <analyzerbase/ianalyzerengine.h>
+#include <QtCore/QPoint>
+
namespace QmlProfiler {
namespace Internal {
@@ -74,6 +76,8 @@ public slots:
void clearDisplay();
+ void showContextMenu(const QPoint &position);
+
signals:
void setTimeLabel(const QString &);
void fetchingData(bool);
@@ -84,6 +88,8 @@ private slots:
void attach();
void tryToConnect();
void connectionStateChanged();
+ void showSaveDialog();
+ void showLoadDialog();
private:
void connectToClient();
diff --git a/src/plugins/qmlprofiler/timelineview.cpp b/src/plugins/qmlprofiler/timelineview.cpp
index 930672a847..7e141f3568 100644
--- a/src/plugins/qmlprofiler/timelineview.cpp
+++ b/src/plugins/qmlprofiler/timelineview.cpp
@@ -34,12 +34,17 @@
#include <qdeclarativecontext.h>
#include <qdeclarativeproperty.h>
+#include <QtCore/QTimer>
using namespace QmlProfiler::Internal;
+#define CACHE_ENABLED true
+#define CACHE_UPDATEDELAY 10
+#define CACHE_STEP 200
+
TimelineView::TimelineView(QDeclarativeItem *parent) :
- QDeclarativeItem(parent), m_delegate(0), m_startTime(0), m_endTime(0), m_startX(0),
- prevMin(0), prevMax(0), m_totalWidth(0)
+ QDeclarativeItem(parent), m_delegate(0), m_itemCount(0), m_startTime(0), m_endTime(0), m_startX(0), m_spacing(0),
+ prevMin(0), prevMax(0), m_eventList(0), m_totalWidth(0), m_lastCachedIndex(0), m_creatingCache(false), m_oldCacheSize(0)
{
}
@@ -50,46 +55,20 @@ void TimelineView::componentComplete()
void TimelineView::clearData()
{
- m_rangeList.clear();
- m_items.clear();
+ if (CACHE_ENABLED)
+ foreach (QDeclarativeItem *item, m_items.values())
+ item->setVisible(false);
+ else
+ foreach (QDeclarativeItem *item, m_items.values())
+ delete m_items.take(m_items.key(item));
+
m_startTime = 0;
m_endTime = 0;
m_startX = 0;
prevMin = 0;
prevMax = 0;
- m_prevLimits.clear();
m_totalWidth = 0;
-
-}
-
-void TimelineView::setRanges(const QScriptValue &value)
-{
- //TODO clear old values (always?)
- m_ranges = value;
-
- //### below code not yet used anywhere
- int length = m_ranges.property("length").toInt32();
-
- for (int i = 0; i < length; ++i) {
- int type = m_ranges.property(i).property("type").toNumber();
- Q_ASSERT(type >= 0);
- while (m_rangeList.count() <= type)
- m_rangeList.append(ValueList());
- m_rangeList[type] << m_ranges.property(i);
- }
-
- for (int i = 0; i < m_rangeList.count(); ++i)
- m_prevLimits << PrevLimits(0, 0);
-
- qreal startValue = m_ranges.property(0).property("start").toNumber();
- m_starts.clear();
- m_starts.reserve(length);
- m_ends.clear();
- m_ends.reserve(length);
- for (int i = 0; i < length; ++i) {
- m_starts.append(m_ranges.property(i).property("start").toNumber() - startValue);
- m_ends.append(m_ranges.property(i).property("start").toNumber() + m_ranges.property(i).property("duration").toNumber() - startValue);
- }
+ m_lastCachedIndex = 0;
}
void TimelineView::setStartX(qreal arg)
@@ -97,9 +76,6 @@ void TimelineView::setStartX(qreal arg)
if (arg == m_startX)
return;
- if (!m_ranges.isArray())
- return;
-
qreal window = m_endTime - m_startTime;
if (window == 0) //###
return;
@@ -118,51 +94,29 @@ void TimelineView::updateTimeline(bool updateStartX)
if (!m_delegate)
return;
- if (!m_ranges.isArray())
+ if (!m_eventList)
return;
- int length = m_ranges.property("length").toInt32();
-
- qreal startValue = m_ranges.property(0).property("start").toNumber();
- qreal endValue = m_ranges.property(length-1).property("start").toNumber() + m_ranges.property(length-1).property("duration").toNumber();
-
- qreal totalRange = endValue - startValue;
+ qreal totalRange = m_eventList->lastTimeMark() - m_eventList->firstTimeMark();
qreal window = m_endTime - m_startTime;
if (window == 0) //###
return;
- qreal spacing = width() / window;
- qreal oldtw = m_totalWidth;
- m_totalWidth = totalRange * spacing;
-
- // Find region samples
- int minsample = 0;
- int maxsample = 0;
+ qreal newSpacing = width() / window;
+ bool spacingChanged = (newSpacing != m_spacing);
+ m_spacing = newSpacing;
- for (int i = 0; i < length; ++i) {
- if (m_ends.at(i) >= m_startTime)
- break;
- minsample = i;
- }
-
- for (int i = minsample + 1; i < length; ++i) {
- maxsample = i;
- if (m_starts.at(i) > m_endTime)
- break;
- }
+ qreal oldtw = m_totalWidth;
+ m_totalWidth = totalRange * m_spacing;
- //### overkill (if we can expose whether or not data is nested)
- for (int i = maxsample + 1; i < length; ++i) {
- if (m_starts.at(i) < m_endTime)
- maxsample = i;
- }
- //qDebug() << maxsample - minsample;
+ int minsample = m_eventList->findFirstIndex(m_startTime + m_eventList->firstTimeMark());
+ int maxsample = m_eventList->findLastIndex(m_endTime + m_eventList->firstTimeMark());
if (updateStartX) {
qreal oldStartX = m_startX;
- m_startX = qRound(m_startTime * spacing);
+ m_startX = qRound(m_startTime * m_spacing);
if (m_startX != oldStartX) {
emit startXChanged(m_startX);
}
@@ -172,69 +126,181 @@ void TimelineView::updateTimeline(bool updateStartX)
if (m_totalWidth != oldtw)
emit totalWidthChanged(m_totalWidth);
- //clear items no longer in view
- while (prevMin < minsample) {
- delete m_items.take(prevMin);
- ++prevMin;
- }
- while (prevMax > maxsample) {
- delete m_items.take(prevMax);
- --prevMax;
- }
- // Show items
- int z = 0;
- for (int i = maxsample; i >= minsample; --i) {
- QDeclarativeItem *item = 0;
- item = m_items.value(i);
- bool creating = false;
- if (!item) {
- QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(this));
- item = qobject_cast<QDeclarativeItem*>(m_delegate->beginCreate(ctxt));
- m_items.insert(i, item);
- creating = true;
-
- int type = m_ranges.property(i).property("type").toNumber();
-
- ctxt->setParent(item); //### QDeclarative_setParent_noEvent(ctxt, item); instead?
- ctxt->setContextProperty("duration", qMax(qRound(m_ranges.property(i).property("duration").toNumber()/qreal(1000)),1));
- ctxt->setContextProperty("fileName", m_ranges.property(i).property("fileName").toString());
- ctxt->setContextProperty("line", m_ranges.property(i).property("line").toNumber());
- ctxt->setContextProperty("index", i);
- ctxt->setContextProperty("nestingLevel", m_ranges.property(i).property("nestingLevel").toNumber());
- ctxt->setContextProperty("nestingDepth", m_ranges.property(i).property("nestingDepth").toNumber());
- QString label;
- QVariantList list = m_ranges.property(i).property("label").toVariant().value<QVariantList>();
- for (int i = 0; i < list.size(); ++i) {
- if (i > 0)
- label += QLatin1Char('\n');
- QString sub = list.at(i).toString();
-
- //### only do rewrite for bindings...
- if (type == 3) {
- //### don't construct in loop
- QRegExp rewrite("\\(function \\$(\\w+)\\(\\) \\{ return (.+) \\}\\)");
- bool match = rewrite.exactMatch(sub);
- if (match)
- sub = rewrite.cap(1) + ": " + rewrite.cap(2);
- }
+ // the next loops have to be modified with the new implementation of the cache
- label += sub;
+ // hide items that are not visible any more
+ if (maxsample < prevMin || minsample > prevMax) {
+ for (int i = prevMin; i <= prevMax; ++i)
+ if (m_items.contains(i)) {
+ if (CACHE_ENABLED)
+ m_items.value(i)->setVisible(false);
+ else
+ delete m_items.take(i);
}
- ctxt->setContextProperty("label", label);
- ctxt->setContextProperty("type", type);
- item->setParentItem(this);
+ } else {
+ if (minsample > prevMin && minsample <= prevMax)
+ for (int i = prevMin; i < minsample; ++i)
+ if (m_items.contains(i)) {
+ if (CACHE_ENABLED)
+ m_items.value(i)->setVisible(false);
+ else
+ delete m_items.take(i);
+ }
+
+ if (maxsample >= prevMin && maxsample < prevMax)
+ for (int i = maxsample + 1; i <= prevMax; ++i)
+ if (m_items.contains(i)) {
+ if (CACHE_ENABLED)
+ m_items.value(i)->setVisible(false);
+ else
+ delete m_items.take(i);
+ }
+ }
+
+ // Update visible items
+ for (int i = minsample; i <= maxsample; ++i) {
+ if (!m_items.contains(i)) {
+ createItem(i);
+ m_items.value(i)->setVisible(true);
}
- if (item) {
- item->setX(m_starts.at(i)*spacing);
- qreal width = (m_ends.at(i)-m_starts.at(i)) * spacing;
- item->setWidth(width > 1 ? width : 1);
- item->setZValue(++z);
+ else
+ if (spacingChanged || !m_items.value(i)->isVisible()) {
+ m_items.value(i)->setVisible(true);
+ updateItemPosition(i);
}
- if (creating)
- m_delegate->completeCreate();
}
prevMin = minsample;
prevMax = maxsample;
+
+}
+
+void TimelineView::createItem(int itemIndex)
+{
+ QDeclarativeContext *ctxt = new QDeclarativeContext(qmlContext(this));
+ QDeclarativeItem *item = qobject_cast<QDeclarativeItem*>(m_delegate->beginCreate(ctxt));
+ m_items.insert(itemIndex, item);
+
+ ctxt->setParent(item); //### QDeclarative_setParent_noEvent(ctxt, item); instead?
+ ctxt->setContextProperty("index", itemIndex);
+ ctxt->setContextProperty("type", m_eventList->getType(itemIndex));
+ ctxt->setContextProperty("nestingLevel", m_eventList->getNestingLevel(itemIndex));
+ ctxt->setContextProperty("nestingDepth", m_eventList->getNestingDepth(itemIndex));
+
+ updateItemPosition(itemIndex);
+
+ item->setVisible(false);
+
+ item->setParentItem(this);
+ m_delegate->completeCreate();
+ m_itemCount++;
+}
+
+void TimelineView::updateItemPosition(int itemIndex)
+{
+ QDeclarativeItem *item = m_items.value(itemIndex);
+ if (item) {
+ qreal itemStartPos = (m_eventList->getStartTime(itemIndex) - m_eventList->firstTimeMark()) * m_spacing;
+ item->setX(itemStartPos);
+ qreal width = (m_eventList->getEndTime(itemIndex) - m_eventList->getStartTime(itemIndex)) * m_spacing;
+ item->setWidth(width > 1 ? width : 1);
+ }
+}
+
+void TimelineView::rebuildCache()
+{
+ if (CACHE_ENABLED) {
+ m_lastCachedIndex = 0;
+ m_creatingCache = false;
+ m_oldCacheSize = m_items.count();
+ emit cachedProgressChanged();
+ QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache()));
+ } else {
+ m_creatingCache = true;
+ m_lastCachedIndex = m_eventList->count();
+ emit cacheReady();
+ }
+}
+
+qreal TimelineView::cachedProgress() const
+{
+ qreal progress;
+ if (!m_creatingCache) {
+ if (m_oldCacheSize == 0)
+ progress = 0.5;
+ else
+ progress = (m_lastCachedIndex * 0.5) / m_oldCacheSize;
+ }
+ else
+ progress = 0.5 + (m_lastCachedIndex * 0.5) / m_eventList->count();
+
+ return progress;
+}
+
+void TimelineView::increaseCache()
+{
+ int totalCount = m_eventList->count();
+ if (m_lastCachedIndex >= totalCount) {
+ emit cacheReady();
+ return;
+ }
+
+ for (int i = 0; i < CACHE_STEP; i++) {
+ createItem(m_lastCachedIndex);
+ m_lastCachedIndex++;
+ if (m_lastCachedIndex >= totalCount)
+ break;
+ }
+
+ emit cachedProgressChanged();
+
+ QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache()));
+}
+
+void TimelineView::purgeCache()
+{
+ if (m_items.isEmpty()) {
+ m_creatingCache = true;
+ m_lastCachedIndex = 0;
+ QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(increaseCache()));
+ return;
+ }
+
+ for (int i=0; i < CACHE_STEP; i++)
+ {
+ if (m_items.contains(m_lastCachedIndex))
+ delete m_items.take(m_lastCachedIndex);
+
+ m_lastCachedIndex++;
+ if (m_items.isEmpty())
+ break;
+ }
+
+ emit cachedProgressChanged();
+ QTimer::singleShot(CACHE_UPDATEDELAY, this, SLOT(purgeCache()));
+}
+
+qint64 TimelineView::getDuration(int index) const
+{
+ Q_ASSERT(m_eventList);
+ return m_eventList->getEndTime(index) - m_eventList->getStartTime(index);
+}
+
+QString TimelineView::getFilename(int index) const
+{
+ Q_ASSERT(m_eventList);
+ return m_eventList->getFilename(index);
+}
+
+int TimelineView::getLine(int index) const
+{
+ Q_ASSERT(m_eventList);
+ return m_eventList->getLine(index);
+}
+
+QString TimelineView::getDetails(int index) const
+{
+ Q_ASSERT(m_eventList);
+ return m_eventList->getDetails(index);
}
diff --git a/src/plugins/qmlprofiler/timelineview.h b/src/plugins/qmlprofiler/timelineview.h
index ca0bffc792..8bf35ee696 100644
--- a/src/plugins/qmlprofiler/timelineview.h
+++ b/src/plugins/qmlprofiler/timelineview.h
@@ -35,6 +35,7 @@
#include <QtDeclarative/QDeclarativeItem>
#include <QtScript/QScriptValue>
+#include <qmlprofilereventlist.h>
namespace QmlProfiler {
namespace Internal {
@@ -47,6 +48,8 @@ class TimelineView : public QDeclarativeItem
Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged)
Q_PROPERTY(qreal totalWidth READ totalWidth NOTIFY totalWidthChanged)
+ Q_PROPERTY(QObject* eventList READ eventList WRITE setEventList NOTIFY eventListChanged)
+ Q_PROPERTY(qreal cachedProgress READ cachedProgress NOTIFY cachedProgressChanged)
public:
explicit TimelineView(QDeclarativeItem *parent = 0);
@@ -76,16 +79,34 @@ public:
return m_totalWidth;
}
+ qreal cachedProgress() const;
+
+ QmlProfilerEventList *eventList() const { return m_eventList; }
+ void setEventList(QObject *eventList)
+ {
+ m_eventList = qobject_cast<QmlProfilerEventList *>(eventList);
+ emit eventListChanged(m_eventList);
+ }
+
+ 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 void rebuildCache();
+
signals:
void delegateChanged(QDeclarativeComponent * arg);
void startTimeChanged(qint64 arg);
void endTimeChanged(qint64 arg);
void startXChanged(qreal arg);
void totalWidthChanged(qreal arg);
+ void eventListChanged(QmlProfilerEventList *list);
+
+ void cachedProgressChanged();
+ void cacheReady();
public slots:
void clearData();
- void setRanges(const QScriptValue &value);
void updateTimeline(bool updateStartX = true);
void setDelegate(QDeclarativeComponent * arg)
@@ -118,27 +139,31 @@ protected:
void componentComplete();
private:
+ void createItem(int itemIndex);
+ void updateItemPosition(int itemIndex);
+
+public slots:
+ void increaseCache();
+ void purgeCache();
+
+private:
QDeclarativeComponent * m_delegate;
- QScriptValue m_ranges;
- typedef QList<QScriptValue> ValueList;
- QList<ValueList> m_rangeList;
QHash<int,QDeclarativeItem*> m_items;
+ qint64 m_itemCount;
qint64 m_startTime;
qint64 m_endTime;
qreal m_startX;
+ qreal m_spacing;
int prevMin;
int prevMax;
- QList<qreal> m_starts;
- QList<qreal> m_ends;
- struct PrevLimits {
- PrevLimits(int _min, int _max) : min(_min), max(_max) {}
- int min;
- int max;
- };
+ QmlProfilerEventList *m_eventList;
- QList<PrevLimits> m_prevLimits;
qreal m_totalWidth;
+ int m_lastCachedIndex;
+ bool m_creatingCache;
+ int m_oldCacheSize;
+
};
} // namespace Internal
diff --git a/src/plugins/qmlprofiler/tracewindow.cpp b/src/plugins/qmlprofiler/tracewindow.cpp
index b54115e0f9..48403cf649 100644
--- a/src/plugins/qmlprofiler/tracewindow.cpp
+++ b/src/plugins/qmlprofiler/tracewindow.cpp
@@ -33,6 +33,7 @@
#include "tracewindow.h"
#include "qmlprofilerplugin.h"
+#include "qmlprofilereventlist.h"
#include <qmljsdebugclient/qdeclarativedebugclient.h>
#include <qmljsdebugclient/qmlprofilertraceclient.h>
@@ -43,6 +44,7 @@
#include <QtGui/QVBoxLayout>
#include <QtGui/QToolButton>
#include <QtGui/QGraphicsObject>
+#include <QtGui/QContextMenuEvent>
using namespace QmlJsDebugClient;
@@ -97,14 +99,17 @@ TraceWindow::TraceWindow(QWidget *parent)
toolBarLayout->addWidget(buttonZoomIn);
toolBarLayout->addWidget(buttonZoomOut);
-
-
m_view->setResizeMode(QDeclarativeView::SizeRootObjectToView);
m_view->setFocus();
groupLayout->addWidget(m_view);
setLayout(groupLayout);
+ m_eventList = new QmlProfilerEventList(this);
+ connect(this,SIGNAL(range(int,qint64,qint64,QStringList,QString,int)), m_eventList, SLOT(addRangedEvent(int,qint64,qint64,QStringList,QString,int)));
+ connect(this,SIGNAL(viewUpdated()), m_eventList, SLOT(complete()));
+ m_view->rootContext()->setContextProperty("qmlEventList", m_eventList);
+
// Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
setMinimumHeight(170);
}
@@ -121,8 +126,8 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
delete m_plugin.data();
m_plugin = new QmlProfilerTraceClient(conn);
connect(m_plugin.data(), SIGNAL(complete()), this, SIGNAL(viewUpdated()));
- connect(m_plugin.data(), SIGNAL(range(int,int,int,qint64,qint64,QStringList,QString,int)),
- this, SIGNAL(range(int,int,int,qint64,qint64,QStringList,QString,int)));
+ connect(m_plugin.data(), SIGNAL(range(int,qint64,qint64,QStringList,QString,int)),
+ this, SIGNAL(range(int,qint64,qint64,QStringList,QString,int)));
m_view->rootContext()->setContextProperty("connection", m_plugin.data());
m_view->setSource(QUrl("qrc:/qmlprofiler/MainView.qml"));
@@ -131,7 +136,7 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
connect(m_view->rootObject(), SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
connect(m_view->rootObject(), SIGNAL(updateTimer()), this, SLOT(updateTimer()));
- connect(m_view->rootObject(), SIGNAL(dataAvailableChanged()), this, SLOT(updateToolbar()));
+ connect(m_eventList, SIGNAL(countChanged()), this, SLOT(updateToolbar()));
connect(this, SIGNAL(jumpToPrev()), m_view->rootObject(), SLOT(prevEvent()));
connect(this, SIGNAL(jumpToNext()), m_view->rootObject(), SLOT(nextEvent()));
connect(this, SIGNAL(zoomIn()), m_view->rootObject(), SLOT(zoomIn()));
@@ -140,6 +145,16 @@ void TraceWindow::reset(QDeclarativeDebugConnection *conn)
connect(this, SIGNAL(internalClearDisplay()), m_view->rootObject(), SLOT(clearAll()));
}
+QmlProfilerEventList *TraceWindow::getEventList() const
+{
+ return m_eventList;
+}
+
+void TraceWindow::contextMenuEvent(QContextMenuEvent *ev)
+{
+ emit contextMenuRequested(ev->globalPos());
+}
+
void TraceWindow::updateCursorPosition()
{
emit gotoSourceLocation(m_view->rootObject()->property("fileName").toString(),
@@ -153,17 +168,17 @@ void TraceWindow::updateTimer()
void TraceWindow::clearDisplay()
{
+ m_eventList->clear();
+
if (m_plugin)
m_plugin.data()->clearData();
- else
- emit internalClearDisplay();
+
+ emit internalClearDisplay();
}
void TraceWindow::updateToolbar()
{
- bool dataAvailable = m_view->rootObject()->property("dataAvailable").toBool() &&
- m_view->rootObject()->property("eventCount").toInt() > 0;
- emit enableToolbar(dataAvailable);
+ emit enableToolbar(m_eventList && m_eventList->count()>0);
}
void TraceWindow::setRecording(bool recording)
diff --git a/src/plugins/qmlprofiler/tracewindow.h b/src/plugins/qmlprofiler/tracewindow.h
index 5629f977f0..04bbcab261 100644
--- a/src/plugins/qmlprofiler/tracewindow.h
+++ b/src/plugins/qmlprofiler/tracewindow.h
@@ -34,6 +34,7 @@
#define TRACEWINDOW_H
#include <qmljsdebugclient/qmlprofilertraceclient.h>
+#include "qmlprofilereventlist.h"
#include <QtCore/QPointer>
#include <QtGui/QWidget>
@@ -55,6 +56,8 @@ public:
void reset(QmlJsDebugClient::QDeclarativeDebugConnection *conn);
+ QmlProfilerEventList *getEventList() const;
+
void setRecording(bool recording);
bool isRecording() const;
@@ -69,7 +72,7 @@ signals:
void viewUpdated();
void gotoSourceLocation(const QString &fileUrl, int lineNumber);
void timeChanged(qreal newTime);
- void range(int type, int nestingLevel, int nestingInType, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line);
+ void range(int type, qint64 startTime, qint64 length, const QStringList &data, const QString &fileName, int line);
void internalClearDisplay();
void jumpToPrev();
@@ -78,11 +81,17 @@ signals:
void zoomOut();
void enableToolbar(bool);
+ void contextMenuRequested(const QPoint& position);
+
+private:
+ void contextMenuEvent(QContextMenuEvent *);
+
private:
QWeakPointer<QmlJsDebugClient::QmlProfilerTraceClient> m_plugin;
QSize m_sizeHint;
QDeclarativeView *m_view;
+ QmlProfilerEventList *m_eventList;
};
} // namespace Internal