Commit b7304e2f authored by Christiaan Janssen's avatar Christiaan Janssen
Browse files

QmlProfiler: Refactor



The code of the qmlprofiler client has become a bit too complex,
this patch reorganizes the modules in a more sensible way,
having the modules communicate with each other through a
state machine instead of the excess of signals and slots
from before.

Change-Id: I76f7313779888a1bd07a1cdb1acbf2e47aacf42a
Reviewed-by: default avatarKai Koehne <kai.koehne@nokia.com>
parent d207165f
......@@ -7,11 +7,11 @@ contains(CONFIG, dll) {
INCLUDEPATH += $$PWD/..
HEADERS += \
$$PWD/qmlprofilereventlocation.h \
$$PWD/qdeclarativedebugclient.h \
$$PWD/qdeclarativeenginedebug.h \
$$PWD/qdeclarativeoutputparser.h \
$$PWD/qmljsdebugclient_global.h \
$$PWD/qmlprofilereventlist.h \
$$PWD/qmlprofilereventtypes.h \
$$PWD/qmlprofilertraceclient.h \
$$PWD/qpacketprotocol.h \
......@@ -23,7 +23,6 @@ SOURCES += \
$$PWD/qdeclarativedebugclient.cpp \
$$PWD/qdeclarativeenginedebug.cpp \
$$PWD/qdeclarativeoutputparser.cpp \
$$PWD/qmlprofilereventlist.cpp \
$$PWD/qmlprofilertraceclient.cpp \
$$PWD/qpacketprotocol.cpp \
$$PWD/qv8profilerclient.cpp \
......
......@@ -11,6 +11,3 @@ OTHER_FILES += \
qmljsdebugclient.pri \
qmljsdebugclient-lib.pri
HEADERS += \
qmlprofilereventlocation.h
......@@ -24,7 +24,7 @@ DynamicLibrary {
"qdeclarativeoutputparser.h",
"qmljsdebugclient_global.h",
"qmljsdebugclientconstants.h",
"qmlprofilereventlist.h",
"qmlprofilereventlocation.h",
"qmlprofilertraceclient.cpp",
"qpacketprotocol.cpp",
"qv8profilerclient.cpp",
......@@ -36,7 +36,6 @@ DynamicLibrary {
"qmlprofilertraceclient.h",
"qpacketprotocol.h",
"qdebugmessageclient.cpp",
"qmlprofilereventlist.cpp",
"qdebugmessageclient.h"
]
......
......@@ -35,6 +35,8 @@
#include "qmljsdebugclient_global.h"
#include <QString>
namespace QmlJsDebugClient {
struct QMLJSDEBUGCLIENT_EXPORT QmlEventLocation
......
......@@ -33,8 +33,6 @@
#ifndef QMLPROFILEREVENTTYPES_H
#define QMLPROFILEREVENTTYPES_H
#include <QString>
namespace QmlJsDebugClient {
enum QmlEventType {
......@@ -47,8 +45,15 @@ enum QmlEventType {
MaximumQmlEventType
};
QString qmlEventType(QmlEventType typeEnum);
QmlEventType qmlEventType(const QString &typeString);
namespace Constants {
const char TYPE_PAINTING_STR[] = "Painting";
const char TYPE_COMPILING_STR[] = "Compiling";
const char TYPE_CREATING_STR[] = "Creating";
const char TYPE_BINDING_STR[] = "Binding";
const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
const char PROFILER_FILE_VERSION[] = "1.02";
const int QML_MIN_LEVEL = 1;
}
} // namespace QmlJsDebugClient
......
......@@ -159,6 +159,12 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
int event;
stream >> event;
// stop with the first data
if (d->recording && event != StartTrace)
setRecordingFromServer(false);
else if ((!d->recording) && event == StartTrace)
setRecordingFromServer(true);
if (event == EndTrace) {
emit this->traceFinished(time);
d->maximumTime = time;
......@@ -169,9 +175,6 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
emit this->frame(time, frameRate, animationCount);
d->maximumTime = qMax(time, d->maximumTime);
} else if (event == StartTrace) {
// special: StartTrace is now asynchronous
if (!d->recording)
setRecordingFromServer(true);
emit this->traceStarted(time);
d->maximumTime = time;
} else if (event < MaximumEventType) {
......@@ -191,6 +194,9 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
d->rangeStartTimes[range].push(time);
d->inProgressRanges |= (static_cast<qint64>(1) << range);
++d->rangeCount[range];
// stop with the first data
if (d->recording)
setRecordingFromServer(false);
} else if (messageType == RangeData) {
QString data;
stream >> data;
......
......@@ -80,10 +80,9 @@ public:
bool isEnabled() const;
bool isRecording() const;
void setRecording(bool);
public slots:
void setRecording(bool);
void setRecordingFromServer(bool);
void clearData();
void sendRecordingStatus();
......@@ -106,6 +105,9 @@ protected:
virtual void statusChanged(Status);
virtual void messageReceived(const QByteArray &);
private:
void setRecordingFromServer(bool);
private:
class QmlProfilerTraceClientPrivate *d;
};
......
......@@ -118,6 +118,16 @@ void QV8ProfilerClient::setRecording(bool v)
emit recordingChanged(v);
}
void QV8ProfilerClient::setRecordingFromServer(bool v)
{
if (v == d->recording)
return;
d->recording = v;
emit recordingChanged(v);
}
void QV8ProfilerClient::statusChanged(Status /*status*/)
{
emit enabledChanged();
......@@ -133,7 +143,10 @@ void QV8ProfilerClient::messageReceived(const QByteArray &data)
stream >> messageType;
if (messageType == V8Complete) {
setRecordingFromServer(false);
emit complete();
} else if (messageType == V8ProfilingStarted) {
setRecordingFromServer(true);
} else if (messageType == V8Entry) {
QString filename;
QString function;
......
......@@ -52,6 +52,9 @@ public:
enum Message {
V8Entry,
V8Complete,
V8SnapshotChunk,
V8SnapshotComplete,
V8ProfilingStarted,
V8MaximumMessage
};
......@@ -61,9 +64,9 @@ public:
bool isEnabled() const;
bool isRecording() const;
void setRecording(bool);
public slots:
void setRecording(bool);
void clearData();
void sendRecordingStatus();
......@@ -77,6 +80,9 @@ signals:
void enabledChanged();
void cleared();
private:
void setRecordingFromServer(bool);
protected:
virtual void statusChanged(Status);
virtual void messageReceived(const QByteArray &);
......
......@@ -60,17 +60,19 @@ Item {
function updateHeight() {
height = root.singleRowHeight * (1 +
(expanded ? qmlEventList.uniqueEventsOfType(typeIndex) : qmlEventList.maxNestingForType(typeIndex)));
(expanded ? qmlProfilerDataModel.uniqueEventsOfType(typeIndex) :
qmlProfilerDataModel.maxNestingForType(typeIndex)));
}
function getDescriptions() {
var desc=[];
var ids=[];
var extdesc=[];
for (var i=0; i<qmlEventList.uniqueEventsOfType(typeIndex); i++) {
desc[i] = qmlEventList.eventTextForType(typeIndex, i);
ids[i] = qmlEventList.eventIdForType(typeIndex, i);
extdesc[i] = qmlEventList.eventDisplayNameForType(typeIndex, i) + " : " + desc[i];
for (var i=0; i<qmlProfilerDataModel.uniqueEventsOfType(typeIndex); i++) {
desc[i] = qmlProfilerDataModel.eventTextForType(typeIndex, i);
ids[i] = qmlProfilerDataModel.eventIdForType(typeIndex, i);
extdesc[i] = qmlProfilerDataModel.eventDisplayNameForType(typeIndex, i) +
" : " + desc[i];
}
descriptions = desc;
eventIds = ids;
......@@ -79,18 +81,18 @@ Item {
}
Connections {
target: qmlEventList
target: qmlProfilerDataModel
onReloadDetailLabels: getDescriptions();
onStateChanged: {
// Empty
if (qmlEventList.getCurrentStateFromQml() == 0) {
if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
descriptions = [];
eventIds = [];
extdescriptions = [];
updateHeight();
} else
// Done
if (qmlEventList.getCurrentStateFromQml() == 3) {
if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
getDescriptions();
}
}
......
......@@ -51,11 +51,16 @@ Rectangle {
property alias selectionLocked : view.selectionLocked
signal updateLockButton
property alias selectedItem: view.selectedItem
signal selectedEventIdChanged(int eventId)
signal selectedEventChanged(int eventId)
property bool lockItemSelection : false
property variant names: [ qsTr("Painting"), qsTr("Compiling"), qsTr("Creating"), qsTr("Binding"), qsTr("Handling Signal")]
property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC", "#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
property variant names: [ qsTr("Painting"),
qsTr("Compiling"),
qsTr("Creating"),
qsTr("Binding"),
qsTr("Handling Signal")]
property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC",
"#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
property variant mainviewTimePerPixel : 0
......@@ -64,9 +69,6 @@ Rectangle {
property int lineNumber: -1
property int columnNumber: 0
property real elapsedTime
signal updateTimer
signal updateRangeButton
property bool selectionRangeMode: false
......@@ -77,7 +79,11 @@ Rectangle {
signal changeToolTip(string text)
signal updateVerticalScroll(int newPosition)
property bool applicationDied : false
property bool recordingEnabled: false
property bool appKilled : false
property date recordingStartDate
property real elapsedTime
// ***** connections with external objects
Connections {
......@@ -92,7 +98,8 @@ Rectangle {
backgroundMarks.updateMarks(startTime, endTime);
view.updateFlickRange(startTime, endTime);
if (duration > 0) {
var candidateWidth = qmlEventList.traceDuration() * flick.width / duration;
var candidateWidth = qmlProfilerDataModel.traceDuration() *
flick.width / duration;
if (flick.contentWidth !== candidateWidth)
flick.contentWidth = candidateWidth;
}
......@@ -101,20 +108,21 @@ Rectangle {
}
Connections {
target: qmlEventList
target: qmlProfilerDataModel
onCountChanged: {
eventCount = qmlEventList.count();
eventCount = qmlProfilerDataModel.count();
if (eventCount === 0)
root.clearAll();
if (eventCount > 1) {
root.progress = Math.min(1.0,
(qmlEventList.lastTimeMark() - qmlEventList.traceStartTime()) / root.elapsedTime * 1e-9 );
(qmlProfilerDataModel.lastTimeMark() -
qmlProfilerDataModel.traceStartTime()) / root.elapsedTime * 1e-9 );
} else {
root.progress = 0;
}
}
onStateChanged: {
switch (qmlEventList.getCurrentStateFromQml()) {
switch (qmlProfilerDataModel.getCurrentStateFromQml()) {
case 0: {
root.clearAll();
break;
......@@ -133,7 +141,9 @@ Rectangle {
dataAvailable = true;
view.visible = true;
view.requestPaint();
zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceStartTime() + qmlEventList.traceDuration()/10);
zoomControl.setRange(qmlProfilerDataModel.traceStartTime(),
qmlProfilerDataModel.traceStartTime() +
qmlProfilerDataModel.traceDuration()/10);
break;
}
}
......@@ -151,7 +161,7 @@ Rectangle {
function clearData() {
view.clearData();
dataAvailable = false;
applicationDied = false;
appKilled = false;
eventCount = 0;
hideRangeDetails();
selectionRangeMode = false;
......@@ -166,8 +176,6 @@ Rectangle {
function clearAll() {
clearDisplay();
root.elapsedTime = 0;
root.updateTimer();
}
function nextEvent() {
......@@ -180,9 +188,10 @@ Rectangle {
function updateWindowLength(absoluteFactor) {
var windowLength = view.endTime - view.startTime;
if (qmlEventList.traceEndTime() <= qmlEventList.traceStartTime() || windowLength <= 0)
if (qmlProfilerDataModel.traceEndTime() <= qmlProfilerDataModel.traceStartTime() ||
windowLength <= 0)
return;
var currentFactor = windowLength / qmlEventList.traceDuration();
var currentFactor = windowLength / qmlProfilerDataModel.traceDuration();
updateZoom(absoluteFactor / currentFactor);
}
......@@ -193,8 +202,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
if (newWindowLength > qmlEventList.traceDuration()) {
newWindowLength = qmlEventList.traceDuration();
if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
newWindowLength = qmlProfilerDataModel.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
......@@ -205,7 +214,7 @@ Rectangle {
var fixedPoint = (view.startTime + view.endTime) / 2;
if (view.selectedItem !== -1) {
// center on selected item if it's inside the current screen
var newFixedPoint = qmlEventList.getStartTime(view.selectedItem);
var newFixedPoint = qmlProfilerDataModel.getStartTime(view.selectedItem);
if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
fixedPoint = newFixedPoint;
}
......@@ -222,8 +231,8 @@ Rectangle {
windowLength = min_length;
var newWindowLength = windowLength * relativeFactor;
if (newWindowLength > qmlEventList.traceDuration()) {
newWindowLength = qmlEventList.traceDuration();
if (newWindowLength > qmlProfilerDataModel.traceDuration()) {
newWindowLength = qmlProfilerDataModel.traceDuration();
relativeFactor = newWindowLength / windowLength;
}
if (newWindowLength < min_length) {
......@@ -241,8 +250,8 @@ Rectangle {
var newStart = Math.floor(centerPoint - windowLength/2);
if (newStart < 0)
newStart = 0;
if (newStart + windowLength > qmlEventList.traceEndTime())
newStart = qmlEventList.traceEndTime() - windowLength;
if (newStart + windowLength > qmlProfilerDataModel.traceEndTime())
newStart = qmlProfilerDataModel.traceEndTime() - windowLength;
zoomControl.setRange(newStart, newStart + windowLength);
}
......@@ -252,17 +261,16 @@ Rectangle {
return;
// if item is outside of the view, jump back to its position
if (qmlEventList.getEndTime(itemIndex) < view.startTime || qmlEventList.getStartTime(itemIndex) > view.endTime) {
recenter((qmlEventList.getStartTime(itemIndex) + qmlEventList.getEndTime(itemIndex)) / 2);
if (qmlProfilerDataModel.getEndTime(itemIndex) < view.startTime ||
qmlProfilerDataModel.getStartTime(itemIndex) > view.endTime) {
recenter((qmlProfilerDataModel.getStartTime(itemIndex) +
qmlProfilerDataModel.getEndTime(itemIndex)) / 2);
}
}
function globalZoom() {
zoomControl.setRange(qmlEventList.traceStartTime(), qmlEventList.traceEndTime());
}
function wheelZoom(wheelCenter, wheelDelta) {
if (qmlEventList.traceEndTime() > qmlEventList.traceStartTime() && wheelDelta !== 0) {
if (qmlProfilerDataModel.traceEndTime() > qmlProfilerDataModel.traceStartTime() &&
wheelDelta !== 0) {
if (wheelDelta>0)
updateZoomCentered(wheelCenter, 1/1.2);
else
......@@ -311,34 +319,22 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem != -1 && !lockItemSelection) {
lockItemSelection = true;
selectedEventIdChanged( qmlEventList.getEventId(selectedItem) );
selectedEventChanged( qmlProfilerDataModel.getEventId(selectedItem) );
lockItemSelection = false;
}
}
// ***** child items
Timer {
id: elapsedTimer
property date startDate
property bool reset: true
running: connection.recording && connection.enabled
repeat: true
onRunningChanged: {
if (running) reset = true;
}
interval: 100
triggeredOnStart: true
onTriggered: {
if (reset) {
startDate = new Date();
reset = false;
}
var time = (new Date() - startDate)/1000;
root.elapsedTime = time.toFixed(1);
root.updateTimer();
onRecordingEnabledChanged: {
if (recordingEnabled) {
recordingStartDate = new Date();
elapsedTime = 0;
} else {
elapsedTime = (new Date() - recordingStartDate)/1000.0;
}
}
// ***** child items
TimeMarks {
id: backgroundMarks
y: labels.y
......@@ -380,7 +376,8 @@ Rectangle {
selectionRange.isDragging = false;
}
onDoubleClicked: {
zoomControl.setRange(selectionRange.startTime, selectionRange.startTime + selectionRange.duration);
zoomControl.setRange(selectionRange.startTime,
selectionRange.startTime + selectionRange.duration);
root.selectionRangeMode = false;
root.updateRangeButton();
}
......@@ -394,10 +391,10 @@ Rectangle {
z: 2
}
TimelineView {
TimelineRenderer {
id: view
eventList: qmlEventList
profilerDataModel: qmlProfilerDataModel
x: flick.contentX
width: flick.width
......@@ -405,9 +402,13 @@ Rectangle {
property variant startX: 0
onStartXChanged: {
var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) + qmlEventList.traceStartTime();
var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) +
qmlProfilerDataModel.traceStartTime();
if (Math.abs(newStartTime - startTime) > 1) {
var newEndTime = Math.round((startX+flick.width)* (endTime - startTime) / flick.width) + qmlEventList.traceStartTime();
var newEndTime = Math.round((startX+flick.width) *
(endTime - startTime) /
flick.width) +
qmlProfilerDataModel.traceStartTime();
zoomControl.setRange(newStartTime, newEndTime);
}
......@@ -419,7 +420,8 @@ Rectangle {
if (start !== startTime || end !== endTime) {
startTime = start;
endTime = end;
var newStartX = (startTime - qmlEventList.traceStartTime()) * flick.width / (endTime-startTime);
var newStartX = (startTime - qmlProfilerDataModel.traceStartTime()) *
flick.width / (endTime-startTime);
if (Math.abs(newStartX - startX) >= 1)
startX = newStartX;
}
......@@ -428,24 +430,25 @@ Rectangle {
onSelectedItemChanged: {
if (selectedItem !== -1) {
// display details
rangeDetails.duration = qmlEventList.getDuration(selectedItem)/1000.0;
rangeDetails.label = qmlEventList.getDetails(selectedItem);
rangeDetails.file = qmlEventList.getFilename(selectedItem);
rangeDetails.line = qmlEventList.getLine(selectedItem);
rangeDetails.column = qmlEventList.getColumn(selectedItem);
rangeDetails.type = root.names[qmlEventList.getType(selectedItem)];
rangeDetails.isBindingLoop = qmlEventList.getBindingLoopDest(selectedItem)!==-1;
rangeDetails.duration = qmlProfilerDataModel.getDuration(selectedItem)/1000.0;
rangeDetails.label = qmlProfilerDataModel.getDetails(selectedItem);
rangeDetails.file = qmlProfilerDataModel.getFilename(selectedItem);
rangeDetails.line = qmlProfilerDataModel.getLine(selectedItem);
rangeDetails.column = qmlProfilerDataModel.getColumn(selectedItem);
rangeDetails.type = root.names[qmlProfilerDataModel.getType(selectedItem)];
rangeDetails.isBindingLoop = qmlProfilerDataModel.getBindingLoopDest(selectedItem)!==-1;
rangeDetails.visible = true;
// center view (horizontally)
var windowLength = view.endTime - view.startTime;
var eventStartTime = qmlEventList.getStartTime(selectedItem);
var eventEndTime = eventStartTime + qmlEventList.getDuration(selectedItem);
var eventStartTime = qmlProfilerDataModel.getStartTime(selectedItem);
var eventEndTime = eventStartTime +
qmlProfilerDataModel.getDuration(selectedItem);
if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
var center = (eventStartTime + eventEndTime)/2;
var from = Math.min(qmlEventList.traceEndTime()-windowLength,
var from = Math.min(qmlProfilerDataModel.traceEndTime()-windowLength,
Math.max(0, Math.floor(center - windowLength/2)));
zoomControl.setRange(from, from + windowLength);
......@@ -456,8 +459,10 @@ Rectangle {
if (itemY < root.scrollY) {
root.updateVerticalScroll(itemY);
} else
if (itemY + root.singleRowHeight > root.scrollY + root.candidateHeight) {
root.updateVerticalScroll(itemY + root.singleRowHeight - root.candidateHeight);
if (itemY + root.singleRowHeight >
root.scrollY + root.candidateHeight) {
root.updateVerticalScroll(itemY + root.singleRowHeight -
root.candidateHeight);
}
} else {
root.hideRangeDetails();
......@@ -466,14 +471,18 @@ Rectangle {
onItemPressed: {
if (pressedItem !== -1) {
root.gotoSourceLocation(qmlEventList.getFilename(pressedItem), qmlEventList.getLine(pressedItem), qmlEventList.getColumn(pressedItem));
root.gotoSourceLocation(qmlProfilerDataModel.getFilename(pressedItem),
qmlProfilerDataModel.getLine(pressedItem),
qmlProfilerDataModel.getColumn(pressedItem));
}
}
// hack to pass mouse events to the other mousearea if enabled
startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x : -flick.contentX
startDragArea: selectionRangeDrag.enabled ? selectionRangeDrag.x :
-flick.contentX
endDragArea: selectionRangeDrag.enabled ?
selectionRangeDrag.x + selectionRangeDrag.width : -flick.contentX-1
selectionRangeDrag.x + selectionRangeDrag.width :
-flick.contentX-1
}
MouseArea {
id: selectionRangeControl
......
......@@ -32,7 +32,7 @@
.pragma library
var qmlEventList = 0;
var qmlProfilerDataModel = 0;
//draw background of the graph
function drawGraph(canvas, ctxt, region)
......@@ -44,7 +44,7 @@ function drawGraph(canvas, ctxt, region)