Commit d2911d70 authored by Christiaan Janssen's avatar Christiaan Janssen

QmlProfiler: New event list with caching, load, save

Change-Id: I640a16649156a49f2d7e7006d6b2ea38fe218620
Reviewed-on: http://codereview.qt.nokia.com/3043Reviewed-by: default avatarQt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: default avatarKai Koehne <kai.koehne@nokia.com>
parent 8fbaa0d1
......@@ -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() +
......
......@@ -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);
......
......@@ -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;
}
......@@ -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;
......
......@@ -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
......
......@@ -164,7 +164,7 @@ TiledCanvas {
}
onMousePositionChanged: {
if (!Plotter.ranges.length)
if (!root.eventCount)
return;
if (!pressed && timeDisplayEnd.visible)
......
......@@ -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
......
This diff is collapsed.
/**************************************************************************
**
** 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
......@@ -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;
......
......@@ -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);