QmlProfiler: reworked

Change-Id: I66a236a024d76e7bef6edfb91ae30b5dd098b76b
Reviewed-by: Kai Koehne <kai.koehne@digia.com>
This commit is contained in:
Christiaan Janssen
2013-08-08 13:28:08 +02:00
parent 7764f35107
commit 0a3b20f5f9
62 changed files with 7925 additions and 3138 deletions

View File

@@ -6,6 +6,7 @@ QtcLibrary {
cpp.defines: base.concat("QMLDEBUG_LIB") cpp.defines: base.concat("QMLDEBUG_LIB")
Depends { name: "cpp" }
Depends { name: "Qt"; submodules: ["gui", "network"] } Depends { name: "Qt"; submodules: ["gui", "network"] }
files: [ files: [

View File

@@ -38,6 +38,8 @@ enum QmlEventType {
Creating, Creating,
Binding, Binding,
HandlingSignal, HandlingSignal,
PixmapCacheEvent,
SceneGraphFrameEvent,
MaximumQmlEventType MaximumQmlEventType
}; };
@@ -46,6 +48,8 @@ enum BindingType {
QmlBinding, QmlBinding,
V8Binding, V8Binding,
OptimizedBinding, OptimizedBinding,
QPainterEvent,
AnimationFrame,
MaximumBindingType MaximumBindingType
}; };

View File

@@ -170,7 +170,8 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
} else if (event == AnimationFrame) { } else if (event == AnimationFrame) {
int frameRate, animationCount; int frameRate, animationCount;
stream >> frameRate >> animationCount; stream >> frameRate >> animationCount;
emit this->frame(time, frameRate, animationCount); emit rangedEvent(QmlDebug::Painting, QmlDebug::AnimationFrame, time, 0,
QStringList(), QmlDebug::QmlEventLocation(), frameRate, animationCount, 0,0,0);
d->maximumTime = qMax(time, d->maximumTime); d->maximumTime = qMax(time, d->maximumTime);
} else if (event == StartTrace) { } else if (event == StartTrace) {
emit this->traceStarted(time); emit this->traceStarted(time);
@@ -181,6 +182,32 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
} }
} else if (messageType == Complete) { } else if (messageType == Complete) {
emit complete(); emit complete();
} else if (messageType == SceneGraphFrame) {
int sgEventType;
int count = 0;
qint64 params[5];
stream >> sgEventType;
while (!stream.atEnd()) {
stream >> params[count++];
}
while (count<5)
params[count++] = 0;
emit rangedEvent(SceneGraphFrameEvent, sgEventType,time, 0, QStringList(),
QmlDebug::QmlEventLocation(), params[0], params[1], params[2], params[3], params[4]);
} else if (messageType == PixmapCacheEvent) {
int pixEvTy, width = 0, height = 0, refcount = 0;
QString pixUrl;
stream >> pixEvTy >> pixUrl;
if (pixEvTy == (int)PixmapReferenceCountChanged || pixEvTy == (int)PixmapCacheCountChanged) {
stream >> refcount;
} else if (pixEvTy == (int)PixmapSizeKnown) {
stream >> width >> height;
refcount = 1;
}
emit rangedEvent(QmlDebug::PixmapCacheEvent, pixEvTy, time, 0, QStringList(),
QmlDebug::QmlEventLocation(pixUrl,0,0), width, height, refcount, 0, 0);
d->maximumTime = qMax(time, d->maximumTime);
} else { } else {
int range; int range;
stream >> range; stream >> range;
@@ -240,7 +267,10 @@ void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
BindingType bindingType = QmlBinding; BindingType bindingType = QmlBinding;
if ((QmlEventType)range == Binding) if ((QmlEventType)range == Binding)
bindingType = d->bindingTypes.pop(); bindingType = d->bindingTypes.pop();
emit this->range((QmlEventType)range, bindingType, startTime, time - startTime, data, location); if ((QmlEventType)range == Painting)
bindingType = QPainterEvent;
emit rangedEvent((QmlEventType)range, bindingType, startTime, time - startTime, data,
location, 0, 0, 0, 0, 0);
if (d->rangeCount[range] == 0) { if (d->rangeCount[range] == 0) {
int count = d->rangeDatas[range].count() + int count = d->rangeDatas[range].count() +
d->rangeStartTimes[range].count() + d->rangeStartTimes[range].count() +

View File

@@ -71,10 +71,23 @@ public:
RangeLocation, RangeLocation,
RangeEnd, RangeEnd,
Complete, Complete,
PixmapCacheEvent,
SceneGraphFrame,
MaximumMessage MaximumMessage
}; };
enum PixmapEventType {
PixmapSizeKnown,
PixmapReferenceCountChanged,
PixmapCacheCountChanged,
PixmapLoadingStarted,
PixmapLoadingFinished,
PixmapLoadingError,
MaximumPixmapEventType
};
bool isEnabled() const; bool isEnabled() const;
bool isRecording() const; bool isRecording() const;
void setRecording(bool); void setRecording(bool);
@@ -89,10 +102,9 @@ signals:
void event(int event, qint64 time); void event(int event, qint64 time);
void traceFinished( qint64 time ); void traceFinished( qint64 time );
void traceStarted( qint64 time ); void traceStarted( qint64 time );
void range(int type, int bindingType, qint64 startTime, qint64 length, void rangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
const QStringList &data, const QmlDebug::QmlEventLocation &location); const QStringList &data, const QmlDebug::QmlEventLocation &location,
void frame(qint64 time, int frameRate, int animationCount); qint64 param1, qint64 param2, qint64 param3, qint64 param4, qint64 param5);
void recordingChanged(bool arg); void recordingChanged(bool arg);
void enabledChanged(); void enabledChanged();

View File

@@ -0,0 +1,82 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "abstracttimelinemodel.h"
namespace QmlProfiler {
AbstractTimelineModel::AbstractTimelineModel(QObject *parent) : QObject(parent), m_modelManager(0)
{}
AbstractTimelineModel::~AbstractTimelineModel()
{}
void AbstractTimelineModel::setModelManager(QmlProfilerModelManager *modelManager)
{
m_modelManager = modelManager;
connect(modelManager->simpleModel(),SIGNAL(changed()),this,SLOT(dataChanged()));
m_modelId = modelManager->registerModelProxy();
}
qint64 AbstractTimelineModel::traceStartTime() const
{
return m_modelManager->traceTime()->startTime();
}
qint64 AbstractTimelineModel::traceEndTime() const
{
return m_modelManager->traceTime()->endTime();
}
qint64 AbstractTimelineModel::traceDuration() const
{
return m_modelManager->traceTime()->duration();
}
int AbstractTimelineModel::getState() const
{
return (int)m_modelManager->state();
}
int AbstractTimelineModel::rowCount() const
{
int count = 0;
for (int i=0; i<categoryCount(); i++)
count += categoryDepth(i);
return count;
}
int AbstractTimelineModel::getBindingLoopDest(int index) const
{
Q_UNUSED(index);
return -1;
}
}

View File

@@ -0,0 +1,113 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef ABSTRACTTIMELINEMODEL_H
#define ABSTRACTTIMELINEMODEL_H
#include "qmlprofiler_global.h"
#include "qmlprofilermodelmanager.h"
#include "qmlprofilersimplemodel.h"
#include <QObject>
#include <QVariant>
#include <QColor>
namespace QmlProfiler {
class QMLPROFILER_EXPORT AbstractTimelineModel : public QObject
{
Q_OBJECT
public:
explicit AbstractTimelineModel(QObject *parent = 0);
~AbstractTimelineModel();
void setModelManager(QmlProfilerModelManager *modelManager);
virtual int categories() const = 0;
virtual QStringList categoryTitles() const = 0;
virtual QString name() const = 0;
virtual int count() const = 0;
virtual bool isEmpty() const = 0;
virtual bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const = 0;
Q_INVOKABLE virtual qint64 lastTimeMark() const = 0;
Q_INVOKABLE qint64 traceStartTime() const;
Q_INVOKABLE qint64 traceEndTime() const;
Q_INVOKABLE qint64 traceDuration() const;
Q_INVOKABLE int getState() const;
Q_INVOKABLE virtual bool expanded(int category) const = 0;
Q_INVOKABLE virtual void setExpanded(int category, bool expanded) = 0;
Q_INVOKABLE virtual int categoryDepth(int categoryIndex) const = 0;
Q_INVOKABLE virtual int categoryCount() const = 0;
Q_INVOKABLE virtual int rowCount() const;
Q_INVOKABLE virtual const QString categoryLabel(int categoryIndex) const = 0;
virtual int findFirstIndex(qint64 startTime) const = 0;
virtual int findFirstIndexNoParents(qint64 startTime) const = 0;
virtual int findLastIndex(qint64 endTime) const = 0;
virtual int getEventType(int index) const = 0;
virtual int getEventCategory(int index) const = 0;
virtual int getEventRow(int index) const = 0;
Q_INVOKABLE virtual qint64 getDuration(int index) const = 0;
Q_INVOKABLE virtual qint64 getStartTime(int index) const = 0;
Q_INVOKABLE virtual qint64 getEndTime(int index) const = 0;
Q_INVOKABLE virtual int getEventId(int index) const = 0;
Q_INVOKABLE virtual int getBindingLoopDest(int index) const;
Q_INVOKABLE virtual QColor getColor(int index) const = 0;
Q_INVOKABLE virtual float getHeight(int index) const = 0;
Q_INVOKABLE virtual const QVariantList getLabelsForCategory(int category) const = 0;
Q_INVOKABLE virtual const QVariantList getEventDetails(int index) const = 0;
// returned map should contain "file", "line", "column" properties, or be empty
Q_INVOKABLE virtual const QVariantMap getEventLocation(int index) const = 0;
Q_INVOKABLE virtual int getEventIdForHash(const QString &eventHash) const = 0;
Q_INVOKABLE virtual int getEventIdForLocation(const QString &filename, int line, int column) const = 0;
signals:
void countChanged();
void dataAvailable();
void stateChanged();
void emptyChanged();
void expandedChanged();
protected:
QmlProfilerModelManager *m_modelManager;
int m_modelId;
};
}
#endif // ABSTRACTTIMELINEMODEL_H

View File

@@ -34,7 +34,6 @@ Item {
id: detail id: detail
property string label property string label
property string content property string content
signal linkActivated(string url)
height: childrenRect.height+2 height: childrenRect.height+2
width: childrenRect.width width: childrenRect.width
@@ -55,7 +54,6 @@ Item {
font.pixelSize: 12 font.pixelSize: 12
anchors.baseline: lbl.baseline anchors.baseline: lbl.baseline
anchors.left: guideline.right anchors.left: guideline.right
onLinkActivated: detail.linkActivated(link)
textFormat: Text.PlainText textFormat: Text.PlainText
} }
} }

View File

@@ -31,23 +31,24 @@ import QtQuick 1.0
Item { Item {
id: labelContainer id: labelContainer
property alias text: txt.text property string text: qmlProfilerModelProxy.categoryLabel(modelIndex, categoryIndex)
property bool expanded: false property bool expanded: false
property int typeIndex: index property int categoryIndex: qmlProfilerModelProxy.correctedCategoryIndexForModel(modelIndex, index)
property int modelIndex: qmlProfilerModelProxy.modelIndexForCategory(index);
property variant descriptions: [] property variant descriptions: []
property variant extdescriptions: [] property variant extdescriptions: []
property variant eventIds: [] property variant eventIds: []
visible: qmlProfilerModelProxy.categoryDepth(modelIndex, categoryIndex) > 0;
height: root.singleRowHeight height: root.singleRowHeight
width: 150 width: 150
onExpandedChanged: { onExpandedChanged: {
var rE = labels.rowExpanded; qmlProfilerModelProxy.setExpanded(modelIndex, categoryIndex, expanded);
rE[typeIndex] = expanded;
labels.rowExpanded = rE;
backgroundMarks.requestRedraw(); backgroundMarks.requestRedraw();
view.setRowExpanded(typeIndex, expanded); getDescriptions();
updateHeight(); updateHeight();
} }
@@ -56,20 +57,24 @@ Item {
} }
function updateHeight() { function updateHeight() {
height = root.singleRowHeight * (1 + if (expanded != qmlProfilerModelProxy.expanded(modelIndex, categoryIndex))
(expanded ? qmlProfilerDataModel.uniqueEventsOfType(typeIndex) : expanded = qmlProfilerModelProxy.expanded(modelIndex, categoryIndex);
qmlProfilerDataModel.maxNestingForType(typeIndex))); height = root.singleRowHeight * qmlProfilerModelProxy.categoryDepth(modelIndex, categoryIndex);
} }
function getDescriptions() { function getDescriptions() {
visible = qmlProfilerModelProxy.categoryDepth(modelIndex, categoryIndex) > 0;
if (!visible)
return;
var desc=[]; var desc=[];
var ids=[]; var ids=[];
var extdesc=[]; var extdesc=[];
for (var i=0; i<qmlProfilerDataModel.uniqueEventsOfType(typeIndex); i++) { var labelList = qmlProfilerModelProxy.getLabelsForCategory(modelIndex, categoryIndex);
desc[i] = qmlProfilerDataModel.eventTextForType(typeIndex, i); for (var i = 0; i < labelList.length; i++ ) {
ids[i] = qmlProfilerDataModel.eventIdForType(typeIndex, i); desc[i] = labelList[i].description;
extdesc[i] = qmlProfilerDataModel.eventDisplayNameForType(typeIndex, i) + ids[i] = labelList[i].id;
" : " + desc[i]; extdesc[i] = labelList[i].displayName + ":" + labelList[i].description;
} }
descriptions = desc; descriptions = desc;
eventIds = ids; eventIds = ids;
@@ -78,20 +83,13 @@ Item {
} }
Connections { Connections {
target: qmlProfilerDataModel target: qmlProfilerModelProxy
onReloadDetailLabels: getDescriptions(); onExpandedChanged: {
updateHeight();
}
onStateChanged: { onStateChanged: {
// Empty getDescriptions();
if (qmlProfilerDataModel.getCurrentStateFromQml() == 0) {
descriptions = [];
eventIds = [];
extdescriptions = [];
updateHeight();
} else
// Done
if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
getDescriptions();
}
} }
} }
@@ -99,6 +97,7 @@ Item {
id: txt id: txt
x: 5 x: 5
font.pixelSize: 12 font.pixelSize: 12
text: labelContainer.text
color: "#232323" color: "#232323"
height: root.singleRowHeight height: root.singleRowHeight
width: 140 width: 140
@@ -140,9 +139,9 @@ Item {
onExited: changeToolTip(""); onExited: changeToolTip("");
onClicked: { onClicked: {
if (mouse.modifiers & Qt.ShiftModifier) if (mouse.modifiers & Qt.ShiftModifier)
view.selectPrevFromId(eventIds[index]); view.selectPrevFromId(modelIndex,eventIds[index]);
else else
view.selectNextFromId(eventIds[index]); view.selectNextFromId(modelIndex,eventIds[index]);
} }
} }
} }
@@ -150,7 +149,6 @@ Item {
} }
Image { Image {
visible: descriptions.length > 0
source: expanded ? "arrow_down.png" : "arrow_right.png" source: expanded ? "arrow_down.png" : "arrow_right.png"
x: parent.width - 12 x: parent.width - 12
y: root.singleRowHeight / 2 - height / 2 y: root.singleRowHeight / 2 - height / 2

View File

@@ -51,14 +51,6 @@ Rectangle {
signal selectedEventChanged(int eventId) signal selectedEventChanged(int eventId)
property bool lockItemSelection : false property bool lockItemSelection : false
property variant names: [ qsTr("Painting"),
qsTr("Compiling"),
qsTr("Creating"),
qsTr("Binding"),
qsTr("Handling Signal")]
property variant colors : [ "#99CCB3", "#99CCCC", "#99B3CC",
"#9999CC", "#CC99B3", "#CC99CC", "#CCCC99", "#CCB399" ]
property variant mainviewTimePerPixel : 0 property variant mainviewTimePerPixel : 0
signal updateCursorPosition signal updateCursorPosition
@@ -95,7 +87,7 @@ Rectangle {
backgroundMarks.updateMarks(startTime, endTime); backgroundMarks.updateMarks(startTime, endTime);
view.updateFlickRange(startTime, endTime); view.updateFlickRange(startTime, endTime);
if (duration > 0) { if (duration > 0) {
var candidateWidth = qmlProfilerDataModel.traceDuration() * var candidateWidth = qmlProfilerModelProxy.traceDuration() *
flick.width / duration; flick.width / duration;
if (flick.contentWidth !== candidateWidth) if (flick.contentWidth !== candidateWidth)
flick.contentWidth = candidateWidth; flick.contentWidth = candidateWidth;
@@ -104,22 +96,23 @@ Rectangle {
} }
} }
Connections { Connections {
target: qmlProfilerDataModel target: qmlProfilerModelProxy
onCountChanged: { onCountChanged: {
eventCount = qmlProfilerDataModel.count(); eventCount = qmlProfilerModelProxy.count();
if (eventCount === 0) if (eventCount === 0)
root.clearAll(); root.clearAll();
if (eventCount > 1) { if (eventCount > 1) {
root.progress = Math.min(1.0, root.progress = Math.min(1.0,
(qmlProfilerDataModel.lastTimeMark() - (qmlProfilerModelProxy.lastTimeMark() -
qmlProfilerDataModel.traceStartTime()) / root.elapsedTime * 1e-9 ); qmlProfilerModelProxy.traceStartTime()) / root.elapsedTime * 1e-9 );
} else { } else {
root.progress = 0; root.progress = 0;
} }
} }
onStateChanged: { onStateChanged: {
switch (qmlProfilerDataModel.getCurrentStateFromQml()) { switch (qmlProfilerModelProxy.getState()) {
case 0: { case 0: {
root.clearAll(); root.clearAll();
break; break;
@@ -132,27 +125,30 @@ Rectangle {
root.progress = 0.9; // jump to 90% root.progress = 0.9; // jump to 90%
break; break;
} }
case 3: {
view.clearData();
progress = 1.0;
dataAvailable = true;
view.visible = true;
view.requestPaint();
zoomControl.setRange(qmlProfilerDataModel.traceStartTime(),
qmlProfilerDataModel.traceStartTime() +
qmlProfilerDataModel.traceDuration()/10);
break;
}
} }
} }
onDataAvailable: {
view.clearData();
zoomControl.setRange(0,0);
progress = 1.0;
dataAvailable = true;
view.visible = true;
view.requestPaint();
zoomControl.setRange(qmlProfilerModelProxy.traceStartTime(),
qmlProfilerModelProxy.traceStartTime() +
qmlProfilerModelProxy.traceDuration()/10);
}
} }
// ***** functions // ***** functions
function gotoSourceLocation(file,line,column) { function gotoSourceLocation(file,line,column) {
root.fileName = file; if (file !== undefined) {
root.lineNumber = line; root.fileName = file;
root.columnNumber = column; root.lineNumber = line;
root.updateCursorPosition(); root.columnNumber = column;
root.updateCursorPosition();
}
} }
function clearData() { function clearData() {
@@ -186,10 +182,10 @@ Rectangle {
function updateWindowLength(absoluteFactor) { function updateWindowLength(absoluteFactor) {
var windowLength = view.endTime - view.startTime; var windowLength = view.endTime - view.startTime;
if (qmlProfilerDataModel.traceEndTime() <= qmlProfilerDataModel.traceStartTime() || if (qmlProfilerModelProxy.traceEndTime() <= qmlProfilerModelProxy.traceStartTime() ||
windowLength <= 0) windowLength <= 0)
return; return;
var currentFactor = windowLength / qmlProfilerDataModel.traceDuration(); var currentFactor = windowLength / qmlProfilerModelProxy.traceDuration();
updateZoom(absoluteFactor / currentFactor); updateZoom(absoluteFactor / currentFactor);
} }
@@ -200,8 +196,8 @@ Rectangle {
windowLength = min_length; windowLength = min_length;
var newWindowLength = windowLength * relativeFactor; var newWindowLength = windowLength * relativeFactor;
if (newWindowLength > qmlProfilerDataModel.traceDuration()) { if (newWindowLength > qmlProfilerModelProxy.traceDuration()) {
newWindowLength = qmlProfilerDataModel.traceDuration(); newWindowLength = qmlProfilerModelProxy.traceDuration();
relativeFactor = newWindowLength / windowLength; relativeFactor = newWindowLength / windowLength;
} }
if (newWindowLength < min_length) { if (newWindowLength < min_length) {
@@ -210,13 +206,15 @@ Rectangle {
} }
var fixedPoint = (view.startTime + view.endTime) / 2; var fixedPoint = (view.startTime + view.endTime) / 2;
if (view.selectedItem !== -1) { if (view.selectedItem !== -1) {
// center on selected item if it's inside the current screen // center on selected item if it's inside the current screen
var newFixedPoint = qmlProfilerDataModel.getStartTime(view.selectedItem); var newFixedPoint = qmlProfilerModelProxy.getStartTime(view.selectedModel, view.selectedItem);
if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime) if (newFixedPoint >= view.startTime && newFixedPoint < view.endTime)
fixedPoint = newFixedPoint; fixedPoint = newFixedPoint;
} }
var startTime = fixedPoint - relativeFactor*(fixedPoint - view.startTime); var startTime = fixedPoint - relativeFactor*(fixedPoint - view.startTime);
zoomControl.setRange(startTime, startTime + newWindowLength); zoomControl.setRange(startTime, startTime + newWindowLength);
} }
@@ -229,8 +227,8 @@ Rectangle {
windowLength = min_length; windowLength = min_length;
var newWindowLength = windowLength * relativeFactor; var newWindowLength = windowLength * relativeFactor;
if (newWindowLength > qmlProfilerDataModel.traceDuration()) { if (newWindowLength > qmlProfilerModelProxy.traceDuration()) {
newWindowLength = qmlProfilerDataModel.traceDuration(); newWindowLength = qmlProfilerModelProxy.traceDuration();
relativeFactor = newWindowLength / windowLength; relativeFactor = newWindowLength / windowLength;
} }
if (newWindowLength < min_length) { if (newWindowLength < min_length) {
@@ -248,26 +246,27 @@ Rectangle {
var newStart = Math.floor(centerPoint - windowLength/2); var newStart = Math.floor(centerPoint - windowLength/2);
if (newStart < 0) if (newStart < 0)
newStart = 0; newStart = 0;
if (newStart + windowLength > qmlProfilerDataModel.traceEndTime()) if (newStart + windowLength > qmlProfilerModelProxy.traceEndTime())
newStart = qmlProfilerDataModel.traceEndTime() - windowLength; newStart = qmlProfilerModelProxy.traceEndTime() - windowLength;
zoomControl.setRange(newStart, newStart + windowLength); zoomControl.setRange(newStart, newStart + windowLength);
} }
function recenterOnItem( itemIndex ) function recenterOnItem( modelIndex, itemIndex )
{ {
if (itemIndex === -1) if (itemIndex === -1)
return; return;
// if item is outside of the view, jump back to its position // if item is outside of the view, jump back to its position
if (qmlProfilerDataModel.getEndTime(itemIndex) < view.startTime || if (qmlProfilerModelProxy.getEndTime(modelIndex, itemIndex) < view.startTime ||
qmlProfilerDataModel.getStartTime(itemIndex) > view.endTime) { qmlProfilerModelProxy.getStartTime(modelIndex, itemIndex) > view.endTime) {
recenter((qmlProfilerDataModel.getStartTime(itemIndex) + recenter((qmlProfilerModelProxy.getStartTime(modelIndex, itemIndex) +
qmlProfilerDataModel.getEndTime(itemIndex)) / 2); qmlProfilerModelProxy.getEndTime(modelIndex, itemIndex)) / 2);
} }
} }
function wheelZoom(wheelCenter, wheelDelta) { function wheelZoom(wheelCenter, wheelDelta) {
if (qmlProfilerDataModel.traceEndTime() > qmlProfilerDataModel.traceStartTime() && if (qmlProfilerModelProxy.traceEndTime() > qmlProfilerModelProxy.traceStartTime() &&
wheelDelta !== 0) { wheelDelta !== 0) {
if (wheelDelta>0) if (wheelDelta>0)
updateZoomCentered(wheelCenter, 1/1.2); updateZoomCentered(wheelCenter, 1/1.2);
@@ -280,24 +279,35 @@ Rectangle {
rangeDetails.visible = false; rangeDetails.visible = false;
rangeDetails.duration = ""; rangeDetails.duration = "";
rangeDetails.label = ""; rangeDetails.label = "";
rangeDetails.type = ""; //rangeDetails.type = "";
rangeDetails.file = ""; rangeDetails.file = "";
rangeDetails.line = -1; rangeDetails.line = -1;
rangeDetails.column = 0; rangeDetails.column = 0;
rangeDetails.isBindingLoop = false; rangeDetails.isBindingLoop = false;
} }
function selectNextWithId( eventId ) function selectNextByHash(hash) {
var eventId = qmlProfilerModelProxy.getEventIdForHash(hash);
if (eventId !== -1) {
selectNextById(eventId);
}
}
function selectNextById(eventId)
{ {
// this is a slot responding to events from the other pane
// which tracks only events from the basic model
if (!lockItemSelection) { if (!lockItemSelection) {
lockItemSelection = true; lockItemSelection = true;
var itemIndex = view.nextItemFromId( eventId ); var modelIndex = qmlProfilerModelProxy.basicModelIndex();
var itemIndex = view.nextItemFromId( modelIndex, eventId );
// select an item, lock to it, and recenter if necessary // select an item, lock to it, and recenter if necessary
if (view.selectedItem != itemIndex) { if (view.selectedItem != itemIndex || view.selectedModel != modelIndex) {
view.selectedModel = modelIndex;
view.selectedItem = itemIndex; view.selectedItem = itemIndex;
if (itemIndex !== -1) { if (itemIndex !== -1) {
view.selectionLocked = true; view.selectionLocked = true;
recenterOnItem(itemIndex); recenterOnItem(modelIndex, itemIndex);
} }
} }
lockItemSelection = false; lockItemSelection = false;
@@ -317,7 +327,9 @@ Rectangle {
onSelectedItemChanged: { onSelectedItemChanged: {
if (selectedItem != -1 && !lockItemSelection) { if (selectedItem != -1 && !lockItemSelection) {
lockItemSelection = true; lockItemSelection = true;
selectedEventChanged( qmlProfilerDataModel.getEventId(selectedItem) ); // update in other views
var eventLocation = qmlProfilerModelProxy.getEventLocation(view.selectedModel, view.selectedItem);
gotoSourceLocation(eventLocation.file, eventLocation.line, eventLocation.column);
lockItemSelection = false; lockItemSelection = false;
} }
} }
@@ -392,7 +404,7 @@ Rectangle {
TimelineRenderer { TimelineRenderer {
id: view id: view
profilerDataModel: qmlProfilerDataModel profilerModelProxy: qmlProfilerModelProxy
x: flick.contentX x: flick.contentX
width: flick.width width: flick.width
@@ -401,12 +413,12 @@ Rectangle {
property variant startX: 0 property variant startX: 0
onStartXChanged: { onStartXChanged: {
var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) + var newStartTime = Math.round(startX * (endTime - startTime) / flick.width) +
qmlProfilerDataModel.traceStartTime(); qmlProfilerModelProxy.traceStartTime();
if (Math.abs(newStartTime - startTime) > 1) { if (Math.abs(newStartTime - startTime) > 1) {
var newEndTime = Math.round((startX+flick.width) * var newEndTime = Math.round((startX+flick.width) *
(endTime - startTime) / (endTime - startTime) /
flick.width) + flick.width) +
qmlProfilerDataModel.traceStartTime(); qmlProfilerModelProxy.traceStartTime();
zoomControl.setRange(newStartTime, newEndTime); zoomControl.setRange(newStartTime, newEndTime);
} }
@@ -418,7 +430,7 @@ Rectangle {
if (start !== startTime || end !== endTime) { if (start !== startTime || end !== endTime) {
startTime = start; startTime = start;
endTime = end; endTime = end;
var newStartX = (startTime - qmlProfilerDataModel.traceStartTime()) * var newStartX = (startTime - qmlProfilerModelProxy.traceStartTime()) *
flick.width / (endTime-startTime); flick.width / (endTime-startTime);
if (Math.abs(newStartX - startX) >= 1) if (Math.abs(newStartX - startX) >= 1)
startX = newStartX; startX = newStartX;
@@ -428,32 +440,26 @@ Rectangle {
onSelectedItemChanged: { onSelectedItemChanged: {
if (selectedItem !== -1) { if (selectedItem !== -1) {
// display details // display details
rangeDetails.duration = qmlProfilerDataModel.getDuration(selectedItem)/1000.0; rangeDetails.showInfo(qmlProfilerModelProxy.getEventDetails(selectedModel, selectedItem));
rangeDetails.label = qmlProfilerDataModel.getDetails(selectedItem); rangeDetails.setLocation(qmlProfilerModelProxy.getEventLocation(selectedModel, 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) // center view (horizontally)
var windowLength = view.endTime - view.startTime; var windowLength = view.endTime - view.startTime;
var eventStartTime = qmlProfilerDataModel.getStartTime(selectedItem); var eventStartTime = qmlProfilerModelProxy.getStartTime(selectedModel, selectedItem);
var eventEndTime = eventStartTime + var eventEndTime = eventStartTime +
qmlProfilerDataModel.getDuration(selectedItem); qmlProfilerModelProxy.getDuration(selectedModel, selectedItem);
if (eventEndTime < view.startTime || eventStartTime > view.endTime) { if (eventEndTime < view.startTime || eventStartTime > view.endTime) {
var center = (eventStartTime + eventEndTime)/2; var center = (eventStartTime + eventEndTime)/2;
var from = Math.min(qmlProfilerDataModel.traceEndTime()-windowLength, var from = Math.min(qmlProfilerModelProxy.traceEndTime()-windowLength,
Math.max(0, Math.floor(center - windowLength/2))); Math.max(0, Math.floor(center - windowLength/2)));
zoomControl.setRange(from, from + windowLength); zoomControl.setRange(from, from + windowLength);
} }
// center view (vertically) // center view (vertically)
var itemY = view.getYPosition(selectedItem); var itemY = view.getYPosition(selectedModel, selectedItem);
if (itemY < root.scrollY) { if (itemY < root.scrollY) {
root.updateVerticalScroll(itemY); root.updateVerticalScroll(itemY);
} else } else
@@ -462,17 +468,16 @@ Rectangle {
root.updateVerticalScroll(itemY + root.singleRowHeight - root.updateVerticalScroll(itemY + root.singleRowHeight -
root.candidateHeight); root.candidateHeight);
} }
} else { } else {
root.hideRangeDetails(); root.hideRangeDetails();
} }
} }
onItemPressed: { onItemPressed: {
if (pressedItem !== -1) { var location = qmlProfilerModelProxy.getEventLocation(modelIndex, pressedItem);
root.gotoSourceLocation(qmlProfilerDataModel.getFilename(pressedItem), if (location.hasOwnProperty("file")) // not empty
qmlProfilerDataModel.getLine(pressedItem), root.gotoSourceLocation(location.file, location.line, location.column);
qmlProfilerDataModel.getColumn(pressedItem));
}
} }
// hack to pass mouse events to the other mousearea if enabled // hack to pass mouse events to the other mousearea if enabled
@@ -522,17 +527,13 @@ Rectangle {
color: "#dcdcdc" color: "#dcdcdc"
height: col.height height: col.height
property int rowCount: 5 property int rowCount: qmlProfilerModelProxy.categories();
property variant rowExpanded: [false,false,false,false,false];
Column { Column {
id: col id: col
Repeater { Repeater {
model: labels.rowCount model: labels.rowCount
delegate: Label { delegate: Label { }
text: root.names[index]
height: labels.height/labels.rowCount
}
} }
} }
} }

View File

@@ -29,7 +29,7 @@
.pragma library .pragma library
var qmlProfilerDataModel = 0; var qmlProfilerModelProxy = 0;
//draw background of the graph //draw background of the graph
function drawGraph(canvas, ctxt, region) function drawGraph(canvas, ctxt, region)
@@ -41,89 +41,82 @@ function drawGraph(canvas, ctxt, region)
//draw the actual data to be graphed //draw the actual data to be graphed
function drawData(canvas, ctxt, region) function drawData(canvas, ctxt, region)
{ {
if ((!qmlProfilerDataModel) || qmlProfilerDataModel.count() == 0) if ((!qmlProfilerModelProxy) || qmlProfilerModelProxy.count() == 0)
return; return;
var typeCount = 5;
var width = canvas.width; var width = canvas.width;
var bump = 10; var bump = 10;
var height = canvas.height - bump; var height = canvas.height - bump;
var typeCount = qmlProfilerModelProxy.visibleCategories();
var blockHeight = height / typeCount; var blockHeight = height / typeCount;
var spacing = width / qmlProfilerDataModel.traceDuration(); var spacing = width / qmlProfilerModelProxy.traceDuration();
var highest = [0,0,0,0,0]; // note: change if typeCount changes var modelRowStart = 0;
for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); modelIndex++) {
for (var ii = 0; ii < qmlProfilerModelProxy.count(modelIndex); ++ii) {
for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) { var xx = (qmlProfilerModelProxy.getStartTime(modelIndex,ii) -
qmlProfilerModelProxy.traceStartTime()) * spacing;
if (xx > region.x + region.width)
continue;
var xx = (qmlProfilerDataModel.getStartTime(ii) - var eventWidth = qmlProfilerModelProxy.getDuration(modelIndex,ii) * spacing;
qmlProfilerDataModel.traceStartTime()) * spacing; if (xx + eventWidth < region.x)
if (xx > region.x + region.width) continue;
continue;
var eventWidth = qmlProfilerDataModel.getDuration(ii) * spacing; if (eventWidth < 1)
if (xx + eventWidth < region.x) eventWidth = 1;
continue;
if (eventWidth < 1) xx = Math.round(xx);
eventWidth = 1;
xx = Math.round(xx); var rowNumber = modelRowStart + qmlProfilerModelProxy.getEventCategoryInModel(modelIndex, ii);
var ty = qmlProfilerDataModel.getType(ii);
if (xx + eventWidth > highest[ty]) {
// special: animations
if (ty === 0 && qmlProfilerDataModel.getAnimationCount(ii) >= 0) {
var vertScale = qmlProfilerDataModel.getMaximumAnimationCount() -
qmlProfilerDataModel.getMinimumAnimationCount();
if (vertScale < 1)
vertScale = 1;
var fraction = (qmlProfilerDataModel.getAnimationCount(ii) -
qmlProfilerDataModel.getMinimumAnimationCount()) / vertScale;
var eventHeight = blockHeight * (fraction * 0.85 + 0.15);
var yy = bump + ty*blockHeight + blockHeight - eventHeight;
var fpsFraction = qmlProfilerDataModel.getFramerate(ii) / 60.0; var itemHeight = qmlProfilerModelProxy.getHeight(modelIndex,ii) * blockHeight;
if (fpsFraction > 1.0) var yy = (rowNumber + 1) * blockHeight - itemHeight ;
fpsFraction = 1.0;
ctxt.fillStyle = "hsl("+(fpsFraction*0.27+0.028)+",0.3,0.65)"; var itemColor = qmlProfilerModelProxy.getColorRGB(modelIndex, ii);
ctxt.fillRect(xx, yy, eventWidth, eventHeight); ctxt.fillStyle = "rgb("+itemColor[0]+","+itemColor[1]+","+itemColor[2]+")";
} else { ctxt.fillRect(xx, bump + yy, eventWidth, itemHeight);
var hue = ( qmlProfilerDataModel.getEventId(ii) * 25 ) % 360;
ctxt.fillStyle = "hsl("+(hue/360.0+0.001)+",0.3,0.65)";
ctxt.fillRect(xx, bump + ty*blockHeight, eventWidth, blockHeight);
}
highest[ty] = xx+eventWidth;
} }
modelRowStart += qmlProfilerModelProxy.categoryCount(modelIndex);
} }
// binding loops // binding loops
ctxt.strokeStyle = "orange"; ctxt.strokeStyle = "orange";
ctxt.lineWidth = 2; ctxt.lineWidth = 2;
var radius = 1; var radius = 1;
for (var ii = 0; ii < qmlProfilerDataModel.count(); ++ii) { modelRowStart = 0;
if (qmlProfilerDataModel.getBindingLoopDest(ii) >= 0) { for (modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); modelIndex++) {
var xcenter = Math.round(qmlProfilerDataModel.getStartTime(ii) + for (ii = 0; ii < qmlProfilerModelProxy.count(modelIndex); ++ii) {
qmlProfilerDataModel.getDuration(ii) - if (qmlProfilerModelProxy.getBindingLoopDest(modelIndex,ii) >= 0) {
qmlProfilerDataModel.traceStartTime()) * spacing; var xcenter = Math.round(qmlProfilerModelProxy.getStartTime(modelIndex,ii) +
var ycenter = Math.round(bump + qmlProfilerDataModel.getType(ii) * qmlProfilerModelProxy.getDuration(modelIndex,ii) -
blockHeight + blockHeight/2); qmlProfilerModelProxy.traceStartTime()) * spacing;
ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true); var ycenter = Math.round(bump + (modelRowStart +
ctxt.stroke(); qmlProfilerModelProxy.getEventCategoryInModel(modelIndex, ii)) *
blockHeight + blockHeight/2);
ctxt.arc(xcenter, ycenter, radius, 0, 2*Math.PI, true);
ctxt.stroke();
}
} }
modelRowStart += qmlProfilerModelProxy.categoryCount(modelIndex);
} }
} }
function drawTimeBar(canvas, ctxt, region) function drawTimeBar(canvas, ctxt, region)
{ {
if (!qmlProfilerDataModel) if (!qmlProfilerModelProxy)
return; return;
var width = canvas.width; var width = canvas.width;
var height = 10; var height = 10;
var startTime = qmlProfilerDataModel.traceStartTime(); var startTime = qmlProfilerModelProxy.traceStartTime();
var endTime = qmlProfilerDataModel.traceEndTime(); var endTime = qmlProfilerModelProxy.traceEndTime();
var totalTime = qmlProfilerDataModel.traceDuration(); var totalTime = qmlProfilerModelProxy.traceDuration();
var spacing = width / totalTime; var spacing = width / totalTime;
var initialBlockLength = 120; var initialBlockLength = 120;

View File

@@ -36,36 +36,37 @@ Canvas2D {
// ***** properties // ***** properties
height: 50 height: 50
property bool dataAvailable: false property bool dataReady: false
property variant startTime : 0 property variant startTime : 0
property variant endTime : 0 property variant endTime : 0
// ***** functions // ***** functions
function clearDisplay() function clearDisplay()
{ {
dataAvailable = false; dataReady = false;
requestRedraw(); requestRedraw();
} }
function updateRange() { function updateRange() {
var newStartTime = Math.round(rangeMover.x * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime(); var newStartTime = Math.round(rangeMover.x * qmlProfilerModelProxy.traceDuration() / width) + qmlProfilerModelProxy.traceStartTime();
var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerDataModel.traceDuration() / width) + qmlProfilerDataModel.traceStartTime(); var newEndTime = Math.round((rangeMover.x + rangeMover.width) * qmlProfilerModelProxy.traceDuration() / width) + qmlProfilerModelProxy.traceStartTime();
if (startTime !== newStartTime || endTime !== newEndTime) { if (startTime !== newStartTime || endTime !== newEndTime) {
zoomControl.setRange(newStartTime, newEndTime); zoomControl.setRange(newStartTime, newEndTime);
} }
} }
// ***** connections to external objects // ***** connections to external objects
Connections { Connections {
target: zoomControl target: zoomControl
onRangeChanged: { onRangeChanged: {
if (qmlProfilerDataModel) { if (qmlProfilerModelProxy) {
startTime = zoomControl.startTime(); startTime = zoomControl.startTime();
endTime = zoomControl.endTime(); endTime = zoomControl.endTime();
var newRangeX = (startTime - qmlProfilerDataModel.traceStartTime()) * width / qmlProfilerDataModel.traceDuration(); var newRangeX = (startTime - qmlProfilerModelProxy.traceStartTime()) * width / qmlProfilerModelProxy.traceDuration();
if (rangeMover.x !== newRangeX) if (rangeMover.x !== newRangeX)
rangeMover.x = newRangeX; rangeMover.x = newRangeX;
var newWidth = (endTime-startTime) * width / qmlProfilerDataModel.traceDuration(); var newWidth = (endTime-startTime) * width / qmlProfilerModelProxy.traceDuration();
if (rangeMover.width !== newWidth) if (rangeMover.width !== newWidth)
rangeMover.width = newWidth; rangeMover.width = newWidth;
} }
@@ -73,20 +74,18 @@ Canvas2D {
} }
Connections { Connections {
target: qmlProfilerDataModel target: qmlProfilerModelProxy
onStateChanged: { onDataAvailable: {
// State is "done" dataReady = true;
if (qmlProfilerDataModel.getCurrentStateFromQml() == 3) {
dataAvailable = true;
requestRedraw(); requestRedraw();
}
} }
} }
// ***** slots // ***** slots
onDrawRegion: { onDrawRegion: {
Plotter.qmlProfilerDataModel = qmlProfilerDataModel; Plotter.qmlProfilerModelProxy = qmlProfilerModelProxy;
if (dataAvailable) { if (dataReady) {
Plotter.plot(canvas, ctxt, region); Plotter.plot(canvas, ctxt, region);
} else { } else {
Plotter.drawGraph(canvas, ctxt, region) //just draw the background Plotter.drawGraph(canvas, ctxt, region) //just draw the background
@@ -116,7 +115,7 @@ Canvas2D {
RangeMover { RangeMover {
id: rangeMover id: rangeMover
visible: dataAvailable visible: dataReady
} }
Rectangle { Rectangle {

View File

@@ -35,7 +35,7 @@ Item {
property string duration property string duration
property string label property string label
property string type property string dialogTitle
property string file property string file
property int line property int line
property int column property int column
@@ -65,6 +65,36 @@ Item {
onCandidateHeightChanged: fitInView(); onCandidateHeightChanged: fitInView();
} }
//property variant eventInfo
ListModel {
id: eventInfo
}
function showInfo(eventData) {
eventInfo.clear();
rangeDetails.dialogTitle = eventData[0]["title"];
for (var i = 1; i < eventData.length; i++) {
for (var k in eventData[i]) {
eventInfo.append({"key": k, "value":eventData[i][k]});
}
}
rangeDetails.visible = true;
}
function setLocation(location) {
if (location.hasOwnProperty("file")) { // not empty
file = location.file;
line = location.line;
column = location.column;
} else {
// reset to default values
file = "";
line = 0;
column = -1;
}
}
function fitInView() { function fitInView() {
// don't reposition if it does not fit // don't reposition if it does not fit
if (root.width < width || root.candidateHeight < height) if (root.width < width || root.candidateHeight < height)
@@ -122,7 +152,7 @@ Item {
//title //title
Text { Text {
id: typeTitle id: typeTitle
text: " "+rangeDetails.type text: " "+rangeDetails.dialogTitle
font.bold: true font.bold: true
height: 18 height: 18
y: 2 y: 2
@@ -145,42 +175,14 @@ Item {
id: col id: col
x: 10 x: 10
y: 5 y: 5
Detail {
label: qsTr("Duration:")
content: rangeDetails.duration < 1000 ? rangeDetails.duration + "μs" : Math.floor(rangeDetails.duration/10)/100 + "ms"
}
Detail {
opacity: content.length !== 0 ? 1 : 0
label: qsTr("Details:")
content: {
var inputString = rangeDetails.label;
if (inputString.length > 7 && inputString.substring(0,7) == "file://") {
var pos = inputString.lastIndexOf("/");
return inputString.substr(pos+1);
}
var maxLen = 40;
if (inputString.length > maxLen)
inputString = inputString.substring(0,maxLen)+"...";
return inputString; Repeater {
model: eventInfo
Detail {
label: key
content: value
} }
} }
Detail {
opacity: content.length !== 0 ? 1 : 0
label: qsTr("Location:")
content: {
var file = rangeDetails.file;
var pos = file.lastIndexOf("/");
if (pos != -1)
file = file.substr(pos+1);
return (file.length !== 0) ? (file + ":" + rangeDetails.line) : "";
}
}
Detail {
visible: isBindingLoop
opacity: content.length != 0 ? 1 : 0
label: qsTr("Binding loop detected")
}
} }
} }
@@ -194,7 +196,7 @@ Item {
drag.maximumY: root.candidateHeight - parent.height + yoffset drag.maximumY: root.candidateHeight - parent.height + yoffset
onClicked: { onClicked: {
root.gotoSourceLocation(file, line, column); root.gotoSourceLocation(file, line, column);
root.recenterOnItem(view.selectedItem); root.recenterOnItem(view.selectedModel, view.selectedItem);
} }
} }

View File

@@ -47,7 +47,7 @@ Rectangle {
property string endTimeString: detailedPrintTime(startTime+duration) property string endTimeString: detailedPrintTime(startTime+duration)
property string durationString: detailedPrintTime(duration) property string durationString: detailedPrintTime(duration)
property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerDataModel.traceStartTime() property variant startTime: x * selectionRange.viewTimePerPixel + qmlProfilerModelProxy.traceStartTime()
property variant duration: width * selectionRange.viewTimePerPixel property variant duration: width * selectionRange.viewTimePerPixel
property variant viewTimePerPixel: 1 property variant viewTimePerPixel: 1
property variant creationState : 0 property variant creationState : 0

View File

@@ -48,6 +48,11 @@ Canvas2D {
requestRedraw(); requestRedraw();
} }
Connections {
target: labels
onHeightChanged: { requestRedraw(); }
}
onDrawRegion: { onDrawRegion: {
drawBackgroundBars( ctxt, region ); drawBackgroundBars( ctxt, region );
@@ -86,19 +91,21 @@ Canvas2D {
} }
} }
// gray off out-of-bounds areas // gray off out-of-bounds areas
var rectWidth; var rectWidth;
if (startTime < qmlProfilerDataModel.traceStartTime()) { if (startTime < qmlProfilerModelProxy.traceStartTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)"; ctxt.fillStyle = "rgba(127,127,127,0.2)";
rectWidth = (qmlProfilerDataModel.traceStartTime() - startTime) * spacing; rectWidth = (qmlProfilerModelProxy.traceStartTime() - startTime) * spacing;
ctxt.fillRect(0, 0, rectWidth, height); ctxt.fillRect(0, 0, rectWidth, height);
} }
if (endTime > qmlProfilerDataModel.traceEndTime()) { if (endTime > qmlProfilerModelProxy.traceEndTime()) {
ctxt.fillStyle = "rgba(127,127,127,0.2)"; ctxt.fillStyle = "rgba(127,127,127,0.2)";
var rectX = (qmlProfilerDataModel.traceEndTime() - startTime) * spacing; var rectX = (qmlProfilerModelProxy.traceEndTime() - startTime) * spacing;
rectWidth = (endTime - qmlProfilerDataModel.traceEndTime()) * spacing; rectWidth = (endTime - qmlProfilerModelProxy.traceEndTime()) * spacing;
ctxt.fillRect(rectX, 0, rectWidth, height); ctxt.fillRect(rectX, 0, rectWidth, height);
} }
} }
function updateMarks(start, end) { function updateMarks(start, end) {
@@ -121,16 +128,16 @@ Canvas2D {
// separators // separators
var cumulatedHeight = 0; var cumulatedHeight = 0;
for (var i=0; i<labels.rowCount; i++) { for (var modelIndex = 0; modelIndex < qmlProfilerModelProxy.modelCount(); modelIndex++) {
cumulatedHeight += root.singleRowHeight + (labels.rowExpanded[i] ? for (var i=0; i<qmlProfilerModelProxy.categoryCount(modelIndex); i++) {
qmlProfilerDataModel.uniqueEventsOfType(i) * root.singleRowHeight : cumulatedHeight += root.singleRowHeight * qmlProfilerModelProxy.categoryDepth(modelIndex, i);
qmlProfilerDataModel.maxNestingForType(i) * root.singleRowHeight);
ctxt.strokeStyle = "#B0B0B0"; ctxt.strokeStyle = "#B0B0B0";
ctxt.beginPath(); ctxt.beginPath();
ctxt.moveTo(0, cumulatedHeight); ctxt.moveTo(0, cumulatedHeight);
ctxt.lineTo(width, cumulatedHeight); ctxt.lineTo(width, cumulatedHeight);
ctxt.stroke(); ctxt.stroke();
}
} }
// bottom // bottom

View File

@@ -12,16 +12,26 @@ SOURCES += \
qmlprofilerattachdialog.cpp \ qmlprofilerattachdialog.cpp \
localqmlprofilerrunner.cpp \ localqmlprofilerrunner.cpp \
qmlprofilereventview.cpp \ qmlprofilereventview.cpp \
qv8profilereventview.cpp \
qmlprofilerdetailsrewriter.cpp \ qmlprofilerdetailsrewriter.cpp \
qmlprofilertraceview.cpp \ qmlprofilertraceview.cpp \
timelinerenderer.cpp \ timelinerenderer.cpp \
qmlprofilerstatemanager.cpp \ qmlprofilerstatemanager.cpp \
qv8profilerdatamodel.cpp \ qv8profilerdatamodel.cpp \
qmlprofilerdatamodel.cpp \
qmlprofilerclientmanager.cpp \ qmlprofilerclientmanager.cpp \
qmlprofilerviewmanager.cpp \ qmlprofilerviewmanager.cpp \
qmlprofilerstatewidget.cpp \ qmlprofilerstatewidget.cpp \
qmlprofilerruncontrolfactory.cpp qmlprofilerruncontrolfactory.cpp \
qmlprofilermodelmanager.cpp \
qmlprofilersimplemodel.cpp \
qmlprofilerprocessedmodel.cpp \
qmlprofilereventsmodelproxy.cpp \
qmlprofilertimelinemodelproxy.cpp \
qmlprofilertreeview.cpp \
qmlprofilertracefile.cpp \
abstracttimelinemodel.cpp \
timelinemodelaggregator.cpp \
qmlprofilerpainteventsmodelproxy.cpp
HEADERS += \ HEADERS += \
qmlprofilerconstants.h \ qmlprofilerconstants.h \
@@ -33,16 +43,26 @@ HEADERS += \
abstractqmlprofilerrunner.h \ abstractqmlprofilerrunner.h \
localqmlprofilerrunner.h \ localqmlprofilerrunner.h \
qmlprofilereventview.h \ qmlprofilereventview.h \
qv8profilereventview.h \
qmlprofilerdetailsrewriter.h \ qmlprofilerdetailsrewriter.h \
qmlprofilertraceview.h \ qmlprofilertraceview.h \
timelinerenderer.h \ timelinerenderer.h \
qmlprofilerstatemanager.h \ qmlprofilerstatemanager.h \
qv8profilerdatamodel.h \ qv8profilerdatamodel.h \
qmlprofilerdatamodel.h \
qmlprofilerclientmanager.h \ qmlprofilerclientmanager.h \
qmlprofilerviewmanager.h \ qmlprofilerviewmanager.h \
qmlprofilerstatewidget.h \ qmlprofilerstatewidget.h \
qmlprofilerruncontrolfactory.h qmlprofilerruncontrolfactory.h \
qmlprofilermodelmanager.h \
qmlprofilersimplemodel.h \
qmlprofilerprocessedmodel.h \
qmlprofilereventsmodelproxy.h \
qmlprofilertimelinemodelproxy.h \
qmlprofilertreeview.h \
qmlprofilertracefile.h \
abstracttimelinemodel.h \
timelinemodelaggregator.h \
qmlprofilerpainteventsmodelproxy.h
RESOURCES += \ RESOURCES += \
qml/qmlprofiler.qrc qml/qmlprofiler.qrc

View File

@@ -22,6 +22,8 @@ QtcPlugin {
cpp.includePaths: base.concat("canvas") cpp.includePaths: base.concat("canvas")
files: [ files: [
"abstracttimelinemodel.h",
"abstracttimelinemodel.cpp",
"abstractqmlprofilerrunner.h", "abstractqmlprofilerrunner.h",
"localqmlprofilerrunner.cpp", "localqmlprofilerrunner.cpp",
"localqmlprofilerrunner.h", "localqmlprofilerrunner.h",
@@ -31,32 +33,50 @@ QtcPlugin {
"qmlprofilerclientmanager.cpp", "qmlprofilerclientmanager.cpp",
"qmlprofilerclientmanager.h", "qmlprofilerclientmanager.h",
"qmlprofilerconstants.h", "qmlprofilerconstants.h",
"qmlprofilerdatamodel.cpp",
"qmlprofilerdatamodel.h",
"qmlprofilerdetailsrewriter.cpp", "qmlprofilerdetailsrewriter.cpp",
"qmlprofilerdetailsrewriter.h", "qmlprofilerdetailsrewriter.h",
"qmlprofilerengine.cpp", "qmlprofilerengine.cpp",
"qmlprofilerengine.h", "qmlprofilerengine.h",
"qmlprofilereventsmodelproxy.cpp",
"qmlprofilereventsmodelproxy.h",
"qmlprofilereventview.cpp", "qmlprofilereventview.cpp",
"qmlprofilereventview.h", "qmlprofilereventview.h",
"qmlprofilermodelmanager.cpp",
"qmlprofilermodelmanager.h",
"qmlprofilerplugin.cpp", "qmlprofilerplugin.cpp",
"qmlprofilerplugin.h", "qmlprofilerplugin.h",
"qmlprofilerruncontrolfactory.cpp", "qmlprofilerruncontrolfactory.cpp",
"qmlprofilerruncontrolfactory.h", "qmlprofilerruncontrolfactory.h",
"qmlprofilerprocessedmodel.cpp",
"qmlprofilerprocessedmodel.h",
"qmlprofilersimplemodel.cpp",
"qmlprofilersimplemodel.h",
"qmlprofilerstatemanager.cpp", "qmlprofilerstatemanager.cpp",
"qmlprofilerstatemanager.h", "qmlprofilerstatemanager.h",
"qmlprofilerstatewidget.cpp", "qmlprofilerstatewidget.cpp",
"qmlprofilerstatewidget.h", "qmlprofilerstatewidget.h",
"qmlprofilertimelinemodelproxy.cpp",
"qmlprofilertimelinemodelproxy.h",
"qmlprofilertool.cpp", "qmlprofilertool.cpp",
"qmlprofilertool.h", "qmlprofilertool.h",
"qmlprofilertreeview.cpp",
"qmlprofilertreeview.h",
"qmlprofilertracefile.cpp",
"qmlprofilertracefile.h",
"qmlprofilertraceview.cpp", "qmlprofilertraceview.cpp",
"qmlprofilertraceview.h", "qmlprofilertraceview.h",
"qmlprofilerviewmanager.cpp", "qmlprofilerviewmanager.cpp",
"qmlprofilerviewmanager.h", "qmlprofilerviewmanager.h",
"qv8profilereventview.h",
"qv8profilereventview.cpp",
"qv8profilerdatamodel.cpp", "qv8profilerdatamodel.cpp",
"qv8profilerdatamodel.h", "qv8profilerdatamodel.h",
"timelinemodelaggregator.cpp",
"timelinemodelaggregator.h",
"timelinerenderer.cpp", "timelinerenderer.cpp",
"timelinerenderer.h", "timelinerenderer.h",
"qmlprofilerpainteventsmodelproxy.h",
"qmlprofilerpainteventsmodelproxy.cpp",
"canvas/qdeclarativecanvas.cpp", "canvas/qdeclarativecanvas.cpp",
"canvas/qdeclarativecanvas_p.h", "canvas/qdeclarativecanvas_p.h",
"canvas/qdeclarativecanvastimer.cpp", "canvas/qdeclarativecanvastimer.cpp",

View File

@@ -40,16 +40,19 @@
#include <QTimer> #include <QTimer>
#include <QMessageBox> #include <QMessageBox>
#include "qmlprofilermodelmanager.h"
using namespace QmlDebug; using namespace QmlDebug;
using namespace Core; using namespace Core;
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
class QmlProfilerClientManager::QmlProfilerClientManagerPrivate class QmlProfilerClientManager::QmlProfilerClientManagerPrivate {
{
public: public:
QmlProfilerStateManager *profilerState; QmlProfilerClientManagerPrivate(QmlProfilerClientManager *qq) { Q_UNUSED(qq); }
QmlProfilerStateManager* profilerState;
QmlDebugConnection *connection; QmlDebugConnection *connection;
QPointer<QmlProfilerTraceClient> qmlclientplugin; QPointer<QmlProfilerTraceClient> qmlclientplugin;
@@ -58,7 +61,9 @@ public:
QTimer connectionTimer; QTimer connectionTimer;
int connectionAttempts; int connectionAttempts;
enum ConnectMode { TcpConnection, OstConnection }; enum ConnectMode {
TcpConnection, OstConnection
};
ConnectMode connectMode; ConnectMode connectMode;
QString tcpHost; QString tcpHost;
quint64 tcpPort; quint64 tcpPort;
@@ -67,10 +72,12 @@ public:
bool v8DataReady; bool v8DataReady;
bool qmlDataReady; bool qmlDataReady;
QmlProfilerModelManager *modelManager;
}; };
QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) : QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
QObject(parent), d(new QmlProfilerClientManagerPrivate) QObject(parent), d(new QmlProfilerClientManagerPrivate(this))
{ {
setObjectName(QLatin1String("QML Profiler Connections")); setObjectName(QLatin1String("QML Profiler Connections"));
@@ -81,6 +88,8 @@ QmlProfilerClientManager::QmlProfilerClientManager(QObject *parent) :
d->v8DataReady = false; d->v8DataReady = false;
d->qmlDataReady = false; d->qmlDataReady = false;
d->modelManager = 0;
d->connectionTimer.setInterval(200); d->connectionTimer.setInterval(200);
connect(&d->connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect())); connect(&d->connectionTimer, SIGNAL(timeout()), SLOT(tryToConnect()));
} }
@@ -95,6 +104,17 @@ QmlProfilerClientManager::~QmlProfilerClientManager()
delete d; delete d;
} }
void QmlProfilerClientManager::setModelManager(QmlProfilerModelManager *m)
{
if (d->modelManager) {
disconnect(this,SIGNAL(dataReadyForProcessing()), d->modelManager, SLOT(complete()));
}
d->modelManager = m;
if (d->modelManager) {
connect(this,SIGNAL(dataReadyForProcessing()), d->modelManager, SLOT(complete()));
}
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// Interface // Interface
void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port) void QmlProfilerClientManager::setTcpConnection(QString host, quint64 port)
@@ -159,15 +179,15 @@ void QmlProfilerClientManager::connectClientSignals()
connect(d->qmlclientplugin.data(), SIGNAL(complete()), connect(d->qmlclientplugin.data(), SIGNAL(complete()),
this, SLOT(qmlComplete())); this, SLOT(qmlComplete()));
connect(d->qmlclientplugin.data(), connect(d->qmlclientplugin.data(),
SIGNAL(range(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)), SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
this, qint64,qint64,qint64,qint64,qint64)),
SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation))); d->modelManager,
SLOT(addQmlEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
qint64,qint64,qint64,qint64,qint64)));
connect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)), connect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
this, SIGNAL(traceFinished(qint64))); d->modelManager->traceTime(), SLOT(setEndTime(qint64)));
connect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)), connect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
this, SIGNAL(traceStarted(qint64))); d->modelManager->traceTime(), SLOT(setStartTime(qint64)));
connect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
this, SIGNAL(addFrameEvent(qint64,int,int)));
connect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()), connect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
d->qmlclientplugin.data(), SLOT(sendRecordingStatus())); d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
// fixme: this should be unified for both clients // fixme: this should be unified for both clients
@@ -178,8 +198,8 @@ void QmlProfilerClientManager::connectClientSignals()
connect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete())); connect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
connect(d->v8clientplugin.data(), connect(d->v8clientplugin.data(),
SIGNAL(v8range(int,QString,QString,int,double,double)), SIGNAL(v8range(int,QString,QString,int,double,double)),
this, d->modelManager,
SIGNAL(addV8Event(int,QString,QString,int,double,double))); SLOT(addV8Event(int,QString,QString,int,double,double)));
connect(d->v8clientplugin.data(), SIGNAL(enabledChanged()), connect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
d->v8clientplugin.data(), SLOT(sendRecordingStatus())); d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
} }
@@ -191,15 +211,17 @@ void QmlProfilerClientManager::disconnectClientSignals()
disconnect(d->qmlclientplugin.data(), SIGNAL(complete()), disconnect(d->qmlclientplugin.data(), SIGNAL(complete()),
this, SLOT(qmlComplete())); this, SLOT(qmlComplete()));
disconnect(d->qmlclientplugin.data(), disconnect(d->qmlclientplugin.data(),
SIGNAL(range(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)), SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
this, qint64,qint64,qint64,qint64,qint64)),
SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation))); d->modelManager,
SLOT(addQmlEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
qint64,qint64,qint64,qint64,qint64)));
disconnect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)), disconnect(d->qmlclientplugin.data(), SIGNAL(traceFinished(qint64)),
this, SIGNAL(traceFinished(qint64))); d->modelManager->traceTime(), SLOT(setEndTime(qint64)));
disconnect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)), disconnect(d->qmlclientplugin.data(), SIGNAL(traceStarted(qint64)),
this, SIGNAL(traceStarted(qint64))); d->modelManager->traceTime(), SLOT(setStartTime(qint64)));
disconnect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)), disconnect(d->qmlclientplugin.data(), SIGNAL(frame(qint64,int,int)),
this, SIGNAL(addFrameEvent(qint64,int,int))); d->modelManager, SLOT(addFrameEvent(qint64,int,int)));
disconnect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()), disconnect(d->qmlclientplugin.data(), SIGNAL(enabledChanged()),
d->qmlclientplugin.data(), SLOT(sendRecordingStatus())); d->qmlclientplugin.data(), SLOT(sendRecordingStatus()));
// fixme: this should be unified for both clients // fixme: this should be unified for both clients
@@ -210,8 +232,8 @@ void QmlProfilerClientManager::disconnectClientSignals()
disconnect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete())); disconnect(d->v8clientplugin.data(), SIGNAL(complete()), this, SLOT(v8Complete()));
disconnect(d->v8clientplugin.data(), disconnect(d->v8clientplugin.data(),
SIGNAL(v8range(int,QString,QString,int,double,double)), SIGNAL(v8range(int,QString,QString,int,double,double)),
this, d->modelManager,
SIGNAL(addV8Event(int,QString,QString,int,double,double))); SLOT(addV8Event(int,QString,QString,int,double,double)));
disconnect(d->v8clientplugin.data(), SIGNAL(enabledChanged()), disconnect(d->v8clientplugin.data(), SIGNAL(enabledChanged()),
d->v8clientplugin.data(), SLOT(sendRecordingStatus())); d->v8clientplugin.data(), SLOT(sendRecordingStatus()));
} }

View File

@@ -37,12 +37,13 @@
#include <QStringList> #include <QStringList>
namespace QmlProfiler { namespace QmlProfiler {
class QmlProfilerModelManager;
namespace Internal { namespace Internal {
class QmlProfilerClientManager : public QObject class QmlProfilerClientManager : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlProfilerClientManager(QObject *parent = 0); explicit QmlProfilerClientManager(QObject *parent = 0);
~QmlProfilerClientManager(); ~QmlProfilerClientManager();
@@ -56,16 +57,10 @@ public:
void discardPendingData(); void discardPendingData();
bool isConnected() const; bool isConnected() const;
void setModelManager(QmlProfilerModelManager *m);
signals: signals:
void connectionFailed(); void connectionFailed();
void connectionClosed(); void connectionClosed();
// data
void addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation);
void addV8Event(int,QString,QString,int,double,double);
void addFrameEvent(qint64,int,int);
void traceStarted(qint64);
void traceFinished(qint64);
void dataReadyForProcessing(); void dataReadyForProcessing();
public slots: public slots:
@@ -85,6 +80,9 @@ private slots:
void serverRecordingChanged(); void serverRecordingChanged();
private: private:
class QmlProfilerClientManagerPrivate;
QmlProfilerClientManagerPrivate *d;
void connectToClient(); void connectToClient();
void enableServices(); void enableServices();
@@ -92,12 +90,9 @@ private:
void disconnectClientSignals(); void disconnectClientSignals();
void stopClientsRecording(); void stopClientsRecording();
class QmlProfilerClientManagerPrivate;
QmlProfilerClientManagerPrivate *d;
}; };
} // namespace Internal }
} // namespace QmlProfiler }
#endif // QMLPROFILERCLIENTMANAGER_H #endif // QMLPROFILERCLIENTMANAGER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,201 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERDATAMODEL_H
#define QMLPROFILERDATAMODEL_H
#include <qmldebug/qmlprofilereventtypes.h>
#include <qmldebug/qmlprofilereventlocation.h>
#include "qv8profilerdatamodel.h"
#include <QHash>
#include <QObject>
namespace QmlProfiler {
namespace Internal {
// used for parents and children
struct QmlRangeEventRelative;
struct QmlRangeEventData
{
QmlRangeEventData();
~QmlRangeEventData();
int eventId;
int bindingType;
QString displayName;
QString eventHashStr;
QString details;
QmlDebug::QmlEventLocation location;
QmlDebug::QmlEventType eventType;
bool isBindingLoop;
QHash <QString, QmlRangeEventRelative *> parentHash;
QHash <QString, QmlRangeEventRelative *> childrenHash;
qint64 duration;
qint64 calls;
qint64 minTime;
qint64 maxTime;
double timePerCall;
double percentOfTime;
qint64 medianTime;
QmlRangeEventData &operator=(const QmlRangeEventData &ref);
};
struct QmlRangeEventRelative {
QmlRangeEventRelative(QmlRangeEventData *from) : reference(from), duration(0), calls(0), inLoopPath(false) {}
QmlRangeEventRelative(QmlRangeEventRelative *from) : reference(from->reference), duration(from->duration), calls(from->calls), inLoopPath(from->inLoopPath) {}
QmlRangeEventData *reference;
qint64 duration;
qint64 calls;
bool inLoopPath;
};
class QmlProfilerDataModel : public QObject
{
Q_OBJECT
public:
enum State {
Empty,
AcquiringData,
ProcessingData,
Done
};
explicit QmlProfilerDataModel(QObject *parent = 0);
~QmlProfilerDataModel();
QList<QmlRangeEventData *> getEventDescriptions() const;
QmlRangeEventData *eventDescription(int eventId) const;
QList<QV8EventData *> getV8Events() const;
QV8EventData *v8EventDescription(int eventId) const;
static QString getHashStringForQmlEvent(const QmlDebug::QmlEventLocation &location, int eventType);
static QString getHashStringForV8Event(const QString &displayName, const QString &function);
static QString rootEventName();
static QString rootEventDescription();
static QString qmlEventTypeAsString(QmlDebug::QmlEventType typeEnum);
static QmlDebug::QmlEventType qmlEventTypeAsEnum(const QString &typeString);
int findFirstIndex(qint64 startTime) const;
int findFirstIndexNoParents(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
Q_INVOKABLE qint64 firstTimeMark() const;
Q_INVOKABLE qint64 lastTimeMark() const;
// data access
Q_INVOKABLE int count() const;
Q_INVOKABLE bool isEmpty() const;
Q_INVOKABLE qint64 getStartTime(int index) const;
Q_INVOKABLE qint64 getEndTime(int index) const;
Q_INVOKABLE qint64 getDuration(int index) const;
Q_INVOKABLE int getType(int index) const;
Q_INVOKABLE int getNestingLevel(int index) const;
Q_INVOKABLE int getNestingDepth(int index) const;
Q_INVOKABLE QString getFilename(int index) const;
Q_INVOKABLE int getLine(int index) const;
Q_INVOKABLE int getColumn(int index) const;
Q_INVOKABLE QString getDetails(int index) const;
Q_INVOKABLE int getEventId(int index) const;
Q_INVOKABLE int getBindingLoopDest(int index) const;
Q_INVOKABLE int getFramerate(int index) const;
Q_INVOKABLE int getAnimationCount(int index) const;
Q_INVOKABLE int getMaximumAnimationCount() const;
Q_INVOKABLE int getMinimumAnimationCount() const;
// per-type data
Q_INVOKABLE int uniqueEventsOfType(int type) const;
Q_INVOKABLE int maxNestingForType(int type) const;
Q_INVOKABLE QString eventTextForType(int type, int index) const;
Q_INVOKABLE QString eventDisplayNameForType(int type, int index) const;
Q_INVOKABLE int eventIdForType(int type, int index) const;
Q_INVOKABLE int eventPosInType(int index) const;
Q_INVOKABLE qint64 traceStartTime() const;
Q_INVOKABLE qint64 traceEndTime() const;
Q_INVOKABLE qint64 traceDuration() const;
Q_INVOKABLE qint64 qmlMeasuredTime() const;
Q_INVOKABLE qint64 v8MeasuredTime() const;
void compileStatistics(qint64 startTime, qint64 endTime);
State currentState() const;
Q_INVOKABLE int getCurrentStateFromQml() const;
signals:
void stateChanged();
void countChanged();
void error(const QString &error);
void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
void detailsChanged(int eventId, const QString &newString);
void reloadDetailLabels();
void reloadDocumentsForDetails();
public slots:
void clear();
void prepareForWriting();
void addRangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
const QStringList &data, const QmlDebug::QmlEventLocation &location);
void addV8Event(int depth,const QString &function,const QString &filename, int lineNumber, double totalTime, double selfTime);
void addFrameEvent(qint64 time, int framerate, int animationcount);
void setTraceStartTime(qint64 time);
void setTraceEndTime(qint64 time);
void complete();
bool save(const QString &filename);
void load(const QString &filename);
void setFilename(const QString &filename);
void load();
void rewriteDetailsString(int eventType, const QmlDebug::QmlEventLocation &location, const QString &newString);
void finishedRewritingDetails();
private:
void setState(State state);
void reloadDetails();
private:
class QmlProfilerDataModelPrivate;
QmlProfilerDataModelPrivate *d;
friend class QV8ProfilerDataModel;
};
} // namespace Internal
} // namespace QmlProfiler
#endif // QMLPROFILERDATAMODEL_H

View File

@@ -41,7 +41,7 @@ namespace Internal {
struct PendingEvent { struct PendingEvent {
QmlDebug::QmlEventLocation location; QmlDebug::QmlEventLocation location;
QString localFile; QString localFile;
int eventType; int requestId;
}; };
class PropertyVisitor: protected QmlJS::AST::Visitor class PropertyVisitor: protected QmlJS::AST::Visitor
@@ -122,7 +122,7 @@ QmlProfilerDetailsRewriter::~QmlProfilerDetailsRewriter()
delete d; delete d;
} }
void QmlProfilerDetailsRewriter::requestDetailsForLocation(int type, void QmlProfilerDetailsRewriter::requestDetailsForLocation(int requestId,
const QmlDebug::QmlEventLocation &location) const QmlDebug::QmlEventLocation &location)
{ {
const QString localFile = d->m_projectFinder->findFile(location.filename); const QString localFile = d->m_projectFinder->findFile(location.filename);
@@ -132,7 +132,7 @@ void QmlProfilerDetailsRewriter::requestDetailsForLocation(int type,
if (!QmlJS::Document::isQmlLikeLanguage(QmlJSTools::languageOfFile(localFile))) if (!QmlJS::Document::isQmlLikeLanguage(QmlJSTools::languageOfFile(localFile)))
return; return;
PendingEvent ev = {location, localFile, type}; PendingEvent ev = {location, localFile, requestId};
d->m_pendingEvents << ev; d->m_pendingEvents << ev;
if (!d->m_pendingDocs.contains(localFile)) { if (!d->m_pendingDocs.contains(localFile)) {
if (d->m_pendingDocs.isEmpty()) if (d->m_pendingDocs.isEmpty())
@@ -154,7 +154,7 @@ void QmlProfilerDetailsRewriter::reloadDocuments()
} }
void QmlProfilerDetailsRewriter::rewriteDetailsForLocation(QTextStream &textDoc, void QmlProfilerDetailsRewriter::rewriteDetailsForLocation(QTextStream &textDoc,
QmlJS::Document::Ptr doc, int type, const QmlDebug::QmlEventLocation &location) QmlJS::Document::Ptr doc, int requestId, const QmlDebug::QmlEventLocation &location)
{ {
PropertyVisitor propertyVisitor; PropertyVisitor propertyVisitor;
QmlJS::AST::Node *node = propertyVisitor(doc->ast(), location.line, location.column); QmlJS::AST::Node *node = propertyVisitor(doc->ast(), location.line, location.column);
@@ -168,7 +168,12 @@ void QmlProfilerDetailsRewriter::rewriteDetailsForLocation(QTextStream &textDoc,
textDoc.seek(startPos); textDoc.seek(startPos);
QString details = textDoc.read(len).replace(QLatin1Char('\n'), QLatin1Char(' ')).simplified(); QString details = textDoc.read(len).replace(QLatin1Char('\n'), QLatin1Char(' ')).simplified();
emit rewriteDetailsString(type, location, details); emit rewriteDetailsString(requestId, details);
}
void QmlProfilerDetailsRewriter::clearRequests()
{
d->m_pendingDocs.clear();
} }
void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc) void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc)
@@ -186,7 +191,7 @@ void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc)
PendingEvent ev = d->m_pendingEvents[i]; PendingEvent ev = d->m_pendingEvents[i];
if (ev.localFile == doc->fileName()) { if (ev.localFile == doc->fileName()) {
d->m_pendingEvents.removeAt(i); d->m_pendingEvents.removeAt(i);
rewriteDetailsForLocation(st, doc, ev.eventType, ev.location); rewriteDetailsForLocation(st, doc, ev.requestId, ev.location);
} }
} }
} }

View File

@@ -46,16 +46,18 @@ public:
explicit QmlProfilerDetailsRewriter(QObject *parent, Utils::FileInProjectFinder *fileFinder); explicit QmlProfilerDetailsRewriter(QObject *parent, Utils::FileInProjectFinder *fileFinder);
~QmlProfilerDetailsRewriter(); ~QmlProfilerDetailsRewriter();
void clearRequests();
private: private:
void rewriteDetailsForLocation(QTextStream &textDoc,QmlJS::Document::Ptr doc, int type, void rewriteDetailsForLocation(QTextStream &textDoc, QmlJS::Document::Ptr doc, int requestId,
const QmlDebug::QmlEventLocation &location); const QmlDebug::QmlEventLocation &location);
public slots: public slots:
void requestDetailsForLocation(int type, const QmlDebug::QmlEventLocation &location); void requestDetailsForLocation(int requestId, const QmlDebug::QmlEventLocation &location);
void reloadDocuments(); void reloadDocuments();
void documentReady(QmlJS::Document::Ptr doc); void documentReady(QmlJS::Document::Ptr doc);
signals: signals:
void rewriteDetailsString(int type, const QmlDebug::QmlEventLocation &location, void rewriteDetailsString(int requestId, const QString &details);
const QString &details);
void eventDetailsChanged(); void eventDetailsChanged();
private: private:
class QmlProfilerDetailsRewriterPrivate; class QmlProfilerDetailsRewriterPrivate;

View File

@@ -82,7 +82,7 @@ QmlProfilerRunControl::QmlProfilerRunControl(const Analyzer::AnalyzerStartParame
{ {
d->m_profilerState = 0; d->m_profilerState = 0;
// Only wait 4 seconds for the 'Waiting for connection' on application ouput, then just try to connect // Only wait 4 seconds for the 'Waiting for connection' on application output, then just try to connect
// (application output might be redirected / blocked) // (application output might be redirected / blocked)
d->m_noDebugOutputTimer.setSingleShot(true); d->m_noDebugOutputTimer.setSingleShot(true);
d->m_noDebugOutputTimer.setInterval(4000); d->m_noDebugOutputTimer.setInterval(4000);

View File

@@ -0,0 +1,479 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilereventsmodelproxy.h"
#include "qmlprofilermodelmanager.h"
#include "qmlprofilersimplemodel.h"
#include <utils/qtcassert.h>
#include <QVector>
#include <QHash>
#include <QUrl>
#include <QString>
#include <QStack>
#include <QElapsedTimer>
#include <QDebug>
namespace QmlProfiler {
namespace Internal {
class QmlProfilerEventsModelProxy::QmlProfilerEventsModelProxyPrivate
{
public:
QmlProfilerEventsModelProxyPrivate(QmlProfilerEventsModelProxy *qq) : q(qq) {}
~QmlProfilerEventsModelProxyPrivate() {}
QHash<QString, QmlProfilerEventsModelProxy::QmlEventStats> data;
QmlProfilerModelManager *modelManager;
QmlProfilerEventsModelProxy *q;
int modelId;
QVector<int> acceptedTypes;
QSet<QString> eventsInBindingLoop;
};
QmlProfilerEventsModelProxy::QmlProfilerEventsModelProxy(QmlProfilerModelManager *modelManager, QObject *parent)
: QObject(parent), d(new QmlProfilerEventsModelProxyPrivate(this))
{
d->modelManager = modelManager;
connect(modelManager->simpleModel(), SIGNAL(changed()), this, SLOT(dataChanged()));
d->modelId = modelManager->registerModelProxy();
d->acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
}
QmlProfilerEventsModelProxy::~QmlProfilerEventsModelProxy()
{
delete d;
}
const QList<QmlProfilerEventsModelProxy::QmlEventStats> QmlProfilerEventsModelProxy::getData() const
{
return d->data.values();
}
void QmlProfilerEventsModelProxy::clear()
{
d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1);
d->data.clear();
d->eventsInBindingLoop.clear();
}
void QmlProfilerEventsModelProxy::limitToRange(qint64 rangeStart, qint64 rangeEnd)
{
loadData(rangeStart, rangeEnd);
}
void QmlProfilerEventsModelProxy::dataChanged()
{
if (d->modelManager->state() == QmlProfilerDataState::ProcessingData)
loadData();
if (d->modelManager->state() == QmlProfilerDataState::Empty)
clear();
}
QSet<QString> QmlProfilerEventsModelProxy::eventsInBindingLoop() const
{
return d->eventsInBindingLoop;
}
void QmlProfilerEventsModelProxy::loadData(qint64 rangeStart, qint64 rangeEnd)
{
clear();
qint64 qmlTime = 0;
qint64 lastEndTime = 0;
QHash <QString, QVector<qint64> > durations;
const bool checkRanges = (rangeStart != -1) && (rangeEnd != -1);
const QVector<QmlProfilerSimpleModel::QmlEventData> eventList
= d->modelManager->simpleModel()->getEvents();
// used by binding loop detection
typedef QPair<QString, const QmlProfilerSimpleModel::QmlEventData*> CallStackEntry;
QStack<CallStackEntry> callStack;
callStack.push(CallStackEntry(QString(), 0)); // artificial root
for (int i = 0; i < eventList.size(); ++i) {
const QmlProfilerSimpleModel::QmlEventData *event = &eventList[i];
if (!d->acceptedTypes.contains(event->eventType))
continue;
if (checkRanges) {
if ((event->startTime + event->duration < rangeStart)
|| (event->startTime > rangeEnd))
continue;
}
// put event in hash
QString hash = QmlProfilerSimpleModel::getHashString(*event);
if (!d->data.contains(hash)) {
QmlEventStats stats = {
event->displayName,
hash,
event->data.join(QLatin1String(" ")),
event->location,
event->eventType,
event->bindingType,
event->duration,
1, //calls
event->duration, //minTime
event->duration, // maxTime
0, //timePerCall
0, //percentOfTime
0, //medianTime
false //isBindingLoop
};
d->data.insert(hash, stats);
// for median computing
durations.insert(hash, QVector<qint64>());
durations[hash].append(event->duration);
} else {
// update stats
QmlEventStats *stats = &d->data[hash];
stats->duration += event->duration;
if (event->duration < stats->minTime)
stats->minTime = event->duration;
if (event->duration > stats->maxTime)
stats->maxTime = event->duration;
stats->calls++;
// for median computing
durations[hash].append(event->duration);
}
// qml time computation
if (event->startTime > lastEndTime) { // assume parent event if starts before last end
qmlTime += event->duration;
lastEndTime = event->startTime + event->duration;
}
//
// binding loop detection
//
const QmlProfilerSimpleModel::QmlEventData *potentialParent = callStack.top().second;
while (potentialParent
&& !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
callStack.pop();
potentialParent = callStack.top().second;
}
// check whether event is already in stack
bool inLoop = false;
for (int ii = 1; ii < callStack.size(); ++ii) {
if (callStack.at(ii).first == hash)
inLoop = true;
if (inLoop)
d->eventsInBindingLoop.insert(hash);
}
CallStackEntry newEntry(hash, event);
callStack.push(newEntry);
d->modelManager->modelProxyCountUpdated(d->modelId, i, eventList.count()*2);
}
// post-process: calc mean time, median time, percentoftime
foreach (const QString &hash, d->data.keys()) {
QmlEventStats* stats = &d->data[hash];
if (stats->calls > 0)
stats->timePerCall = stats->duration / (double)stats->calls;
QVector<qint64> eventDurations = durations.value(hash);
if (!eventDurations.isEmpty()) {
qSort(eventDurations);
stats->medianTime = eventDurations.at(eventDurations.count()/2);
}
stats->percentOfTime = stats->duration * 100.0 / qmlTime;
}
// set binding loop flag
foreach (const QString &eventHash, d->eventsInBindingLoop)
d->data[eventHash].isBindingLoop = true;
QString rootEventName = tr("<program>");
QmlDebug::QmlEventLocation rootEventLocation(rootEventName, 1, 1);
// insert root event
QmlEventStats rootEvent = {
rootEventName, //event.displayName,
rootEventName, // hash
tr("Main Program"), //event.details,
rootEventLocation, // location
(int)QmlDebug::Binding, // event type
0, // binding type
qmlTime + 1,
1, //calls
qmlTime + 1, //minTime
qmlTime + 1, // maxTime
qmlTime + 1, //timePerCall
100.0, //percentOfTime
qmlTime + 1, //medianTime;
false
};
d->data.insert(rootEventName, rootEvent);
d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1);
emit dataAvailable();
}
int QmlProfilerEventsModelProxy::count() const
{
return d->data.count();
}
//////////////////////////////////////////////////////////////////////////////////
QmlProfilerEventRelativesModelProxy::QmlProfilerEventRelativesModelProxy(QmlProfilerModelManager *modelManager,
QmlProfilerEventsModelProxy *eventsModel,
QObject *parent)
: QObject(parent)
{
QTC_CHECK(modelManager);
m_modelManager = modelManager;
connect(modelManager->simpleModel(), SIGNAL(changed()), this, SLOT(dataChanged()));
QTC_CHECK(eventsModel);
m_eventsModel = eventsModel;
m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
}
QmlProfilerEventRelativesModelProxy::~QmlProfilerEventRelativesModelProxy()
{
}
const QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap QmlProfilerEventRelativesModelProxy::getData(const QString &hash) const
{
if (m_data.contains(hash))
return m_data[hash];
return QmlEventRelativesMap();
}
int QmlProfilerEventRelativesModelProxy::count() const
{
return m_data.count();
}
void QmlProfilerEventRelativesModelProxy::clear()
{
m_data.clear();
}
void QmlProfilerEventRelativesModelProxy::dataChanged()
{
loadData();
emit dataAvailable();
}
//////////////////////////////////////////////////////////////////////////////////
QmlProfilerEventParentsModelProxy::QmlProfilerEventParentsModelProxy(QmlProfilerModelManager *modelManager,
QmlProfilerEventsModelProxy *eventsModel,
QObject *parent)
: QmlProfilerEventRelativesModelProxy(modelManager, eventsModel, parent)
{}
QmlProfilerEventParentsModelProxy::~QmlProfilerEventParentsModelProxy()
{}
void QmlProfilerEventParentsModelProxy::loadData()
{
clear();
QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
if (simpleModel->isEmpty())
return;
QHash<QString, QmlProfilerSimpleModel::QmlEventData> cachedEvents;
QString rootEventName = tr("<program>");
QmlProfilerSimpleModel::QmlEventData rootEvent = {
rootEventName,
QmlDebug::Binding,
0,
0,
0,
QStringList() << tr("Main Program"),
QmlDebug::QmlEventLocation(rootEventName, 0, 0),
0,0,0,0,0 // numericData fields
};
cachedEvents.insert(rootEventName, rootEvent);
// for level computation
QHash<int, qint64> endtimesPerLevel;
int level = QmlDebug::Constants::QML_MIN_LEVEL;
endtimesPerLevel[0] = 0;
const QSet<QString> eventsInBindingLoop = m_eventsModel->eventsInBindingLoop();
// compute parent-child relationship and call count
QHash<int, QString> lastParent;
//for (int index = fromIndex; index <= toIndex; index++) {
const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
// whitelist
if (!m_acceptedTypes.contains(event.eventType))
continue;
// level computation
if (endtimesPerLevel[level] > event.startTime) {
level++;
} else {
while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
level--;
}
endtimesPerLevel[level] = event.startTime + event.duration;
QString parentHash = rootEventName;
QString eventHash = QmlProfilerSimpleModel::getHashString(event);
// save in cache
if (!cachedEvents.contains(eventHash))
cachedEvents.insert(eventHash, event);
if (level > QmlDebug::Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
parentHash = lastParent[level-1];
QmlProfilerSimpleModel::QmlEventData *parentEvent = &(cachedEvents[parentHash]);
// generate placeholder if needed
if (!m_data.contains(eventHash))
m_data.insert(eventHash, QmlEventRelativesMap());
if (m_data[eventHash].contains(parentHash)) {
QmlEventRelativesData *parent = &(m_data[eventHash][parentHash]);
parent->calls++;
parent->duration += event.duration;
} else {
m_data[eventHash].insert(parentHash, QmlEventRelativesData());
QmlEventRelativesData *parent = &(m_data[eventHash][parentHash]);
parent->displayName = parentEvent->displayName;
parent->eventType = parentEvent->eventType;
parent->duration = event.duration;
parent->calls = 1;
parent->details = parentEvent->data.join(QLatin1String(""));
parent->isBindingLoop = eventsInBindingLoop.contains(parentHash);
}
// now lastparent is a string with the hash
lastParent[level] = eventHash;
}
}
//////////////////////////////////////////////////////////////////////////////////
QmlProfilerEventChildrenModelProxy::QmlProfilerEventChildrenModelProxy(QmlProfilerModelManager *modelManager,
QmlProfilerEventsModelProxy *eventsModel,
QObject *parent)
: QmlProfilerEventRelativesModelProxy(modelManager, eventsModel, parent)
{}
QmlProfilerEventChildrenModelProxy::~QmlProfilerEventChildrenModelProxy()
{}
void QmlProfilerEventChildrenModelProxy::loadData()
{
clear();
QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
if (simpleModel->isEmpty())
return;
QString rootEventName = tr("<program>");
// for level computation
QHash<int, qint64> endtimesPerLevel;
int level = QmlDebug::Constants::QML_MIN_LEVEL;
endtimesPerLevel[0] = 0;
const QSet<QString> eventsInBindingLoop = m_eventsModel->eventsInBindingLoop();
// compute parent-child relationship and call count
QHash<int, QString> lastParent;
const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
// whitelist
if (!m_acceptedTypes.contains(event.eventType))
continue;
// level computation
if (endtimesPerLevel[level] > event.startTime) {
level++;
} else {
while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
level--;
}
endtimesPerLevel[level] = event.startTime + event.duration;
QString parentHash = rootEventName;
QString eventHash = QmlProfilerSimpleModel::getHashString(event);
if (level > QmlDebug::Constants::QML_MIN_LEVEL && lastParent.contains(level-1))
parentHash = lastParent[level-1];
// generate placeholder if needed
if (!m_data.contains(parentHash))
m_data.insert(parentHash, QmlEventRelativesMap());
if (m_data[parentHash].contains(eventHash)) {
QmlEventRelativesData *child = &(m_data[parentHash][eventHash]);
child->calls++;
child->duration += event.duration;
} else {
m_data[parentHash].insert(eventHash, QmlEventRelativesData());
QmlEventRelativesData *child = &(m_data[parentHash][eventHash]);
child->displayName = event.displayName;
child->eventType = event.eventType;
child->duration = event.duration;
child->calls = 1;
child->details = event.data.join(QLatin1String(""));
child->isBindingLoop = eventsInBindingLoop.contains(parentHash);
}
// now lastparent is a string with the hash
lastParent[level] = eventHash;
}
}
}
}

View File

@@ -0,0 +1,172 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILEREVENTSMODELPROXY_H
#define QMLPROFILEREVENTSMODELPROXY_H
#include "qmlprofilersimplemodel.h"
#include <QObject>
#include <qmldebug/qmlprofilereventtypes.h>
#include <qmldebug/qmlprofilereventlocation.h>
#include <QHash>
#include <QVector>
namespace QmlProfiler {
class QmlProfilerModelManager;
namespace Internal {
class QmlProfilerEventsModelProxy : public QObject
{
Q_OBJECT
public:
struct QmlEventStats {
QString displayName;
QString eventHashStr;
QString details;
QmlDebug::QmlEventLocation location;
int eventType;
int bindingType;
qint64 duration;
qint64 calls;
qint64 minTime;
qint64 maxTime;
qint64 timePerCall;
double percentOfTime;
qint64 medianTime;
bool isBindingLoop;
};
QmlProfilerEventsModelProxy(QmlProfilerModelManager *modelManager, QObject *parent = 0);
~QmlProfilerEventsModelProxy();
const QList<QmlEventStats> getData() const;
int count() const;
void clear();
void limitToRange(qint64 rangeStart, qint64 rangeEnd);
signals:
void dataAvailable();
private:
void loadData(qint64 rangeStart = -1, qint64 rangeEnd = -1);
QSet<QString> eventsInBindingLoop() const;
private slots:
void dataChanged();
private:
class QmlProfilerEventsModelProxyPrivate;
QmlProfilerEventsModelProxyPrivate *d;
friend class QmlProfilerEventParentsModelProxy;
friend class QmlProfilerEventChildrenModelProxy;
};
class QmlProfilerEventRelativesModelProxy : public QObject
{
Q_OBJECT
public:
struct QmlEventRelativesData {
QString displayName;
int eventType;
qint64 duration;
qint64 calls;
QString details;
bool isBindingLoop;
};
typedef QHash <QString, QmlEventRelativesData> QmlEventRelativesMap;
QmlProfilerEventRelativesModelProxy(QmlProfilerModelManager *modelManager,
QmlProfilerEventsModelProxy *eventsModel,
QObject *parent = 0);
~QmlProfilerEventRelativesModelProxy();
int count() const;
void clear();
const QmlEventRelativesMap getData(const QString &hash) const;
protected:
virtual void loadData() = 0;
signals:
void dataAvailable();
protected slots:
void dataChanged();
protected:
QHash <QString, QmlEventRelativesMap> m_data;
QmlProfilerModelManager *m_modelManager;
QmlProfilerEventsModelProxy *m_eventsModel;
QVector <int> m_acceptedTypes;
};
class QmlProfilerEventParentsModelProxy : public QmlProfilerEventRelativesModelProxy
{
Q_OBJECT
public:
QmlProfilerEventParentsModelProxy(QmlProfilerModelManager *modelManager,
QmlProfilerEventsModelProxy *eventsModel,
QObject *parent = 0);
~QmlProfilerEventParentsModelProxy();
protected:
virtual void loadData();
signals:
void dataAvailable();
};
class QmlProfilerEventChildrenModelProxy : public QmlProfilerEventRelativesModelProxy
{
Q_OBJECT
public:
QmlProfilerEventChildrenModelProxy(QmlProfilerModelManager *modelManager,
QmlProfilerEventsModelProxy *eventsModel,
QObject *parent = 0);
~QmlProfilerEventChildrenModelProxy();
protected:
virtual void loadData();
signals:
void dataAvailable();
};
}
}
#endif

View File

@@ -66,6 +66,9 @@ struct Colors {
Q_GLOBAL_STATIC(Colors, colors) Q_GLOBAL_STATIC(Colors, colors)
////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
class EventsViewItem : public QStandardItem class EventsViewItem : public QStandardItem
@@ -104,46 +107,48 @@ public:
QmlProfilerViewManager *m_viewContainer; QmlProfilerViewManager *m_viewContainer;
QmlProfilerEventsMainView *m_eventTree; QmlProfilerEventsMainView *m_eventTree;
QmlProfilerEventsParentsAndChildrenView *m_eventChildren; QmlProfilerEventRelativesView *m_eventChildren;
QmlProfilerEventsParentsAndChildrenView *m_eventParents; QmlProfilerEventRelativesView *m_eventParents;
QmlProfilerDataModel *m_profilerDataModel;
bool m_globalStatsEnabled; QmlProfilerEventsModelProxy *modelProxy;
bool globalStats;
}; };
QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent, QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
QmlProfilerTool *profilerTool, QmlProfilerTool *profilerTool,
QmlProfilerViewManager *container, QmlProfilerViewManager *container,
QmlProfilerDataModel *profilerDataModel ) QmlProfilerModelManager *profilerModelManager )
: QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this)) : QWidget(parent), d(new QmlProfilerEventsWidgetPrivate(this))
{ {
setObjectName(QLatin1String("QmlProfilerEventsView")); setObjectName(QLatin1String("QmlProfilerEventsView"));
d->m_profilerDataModel = profilerDataModel; d->modelProxy = new QmlProfilerEventsModelProxy(profilerModelManager, this);
connect(d->m_profilerDataModel, SIGNAL(stateChanged()), connect(profilerModelManager, SIGNAL(stateChanged()),
this, SLOT(profilerDataModelStateChanged())); this, SLOT(profilerDataModelStateChanged()));
d->m_eventTree = new QmlProfilerEventsMainView(QmlProfilerEventsMainView::EventsView, this, d->m_profilerDataModel); d->m_eventTree = new QmlProfilerEventsMainView(this, d->modelProxy);
connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int))); connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
connect(d->m_eventTree, SIGNAL(showEventInTimeline(int)), this, SIGNAL(showEventInTimeline(int))); connect(d->m_eventTree, SIGNAL(eventSelected(QString)), this, SIGNAL(eventSelectedByHash(QString)));
d->m_eventChildren = new QmlProfilerEventsParentsAndChildrenView( d->m_eventChildren = new QmlProfilerEventRelativesView(
QmlProfilerEventsParentsAndChildrenView::ChildrenView, profilerModelManager,
this, new QmlProfilerEventChildrenModelProxy(profilerModelManager, d->modelProxy, this),
d->m_profilerDataModel); this);
d->m_eventParents = new QmlProfilerEventsParentsAndChildrenView( d->m_eventParents = new QmlProfilerEventRelativesView(
QmlProfilerEventsParentsAndChildrenView::ParentsView, profilerModelManager,
this, new QmlProfilerEventParentsModelProxy(profilerModelManager, d->modelProxy, this),
d->m_profilerDataModel); this);
connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int))); connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventChildren, SLOT(displayEvent(QString)));
connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int))); connect(d->m_eventTree, SIGNAL(eventSelected(QString)), d->m_eventParents, SLOT(displayEvent(QString)));
connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int))); connect(d->m_eventChildren, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int))); connect(d->m_eventParents, SIGNAL(eventClicked(QString)), d->m_eventTree, SLOT(selectEvent(QString)));
// widget arrangement // widget arrangement
QVBoxLayout *groupLayout = new QVBoxLayout; QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->setContentsMargins(0,0,0,0); groupLayout->setContentsMargins(0,0,0,0);
groupLayout->setSpacing(0); groupLayout->setSpacing(0);
Core::MiniSplitter *splitterVertical = new Core::MiniSplitter; Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
splitterVertical->addWidget(d->m_eventTree); splitterVertical->addWidget(d->m_eventTree);
Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter; Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
@@ -159,30 +164,17 @@ QmlProfilerEventsWidget::QmlProfilerEventsWidget(QWidget *parent,
d->m_profilerTool = profilerTool; d->m_profilerTool = profilerTool;
d->m_viewContainer = container; d->m_viewContainer = container;
d->m_globalStatsEnabled = true; d->globalStats = true;
} }
QmlProfilerEventsWidget::~QmlProfilerEventsWidget() QmlProfilerEventsWidget::~QmlProfilerEventsWidget()
{ {
delete d->modelProxy;
delete d; delete d;
} }
void QmlProfilerEventsWidget::profilerDataModelStateChanged() void QmlProfilerEventsWidget::profilerDataModelStateChanged()
{ {
if (d->m_profilerDataModel) {
QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
if (newState == QmlProfilerDataModel::Empty)
clear();
}
}
void QmlProfilerEventsWidget::switchToV8View()
{
setObjectName(QLatin1String("QmlProfilerV8ProfileView"));
d->m_eventTree->setViewType(QmlProfilerEventsMainView::V8ProfileView);
d->m_eventParents->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ParentsView);
d->m_eventChildren->setViewType(QmlProfilerEventsParentsAndChildrenView::V8ChildrenView);
setToolTip(tr("Trace information from the v8 JavaScript engine. Available only in Qt5 based applications."));
} }
void QmlProfilerEventsWidget::clear() void QmlProfilerEventsWidget::clear()
@@ -194,9 +186,8 @@ void QmlProfilerEventsWidget::clear()
void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd) void QmlProfilerEventsWidget::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{ {
clear(); d->modelProxy->limitToRange(rangeStart, rangeEnd);
d->m_eventTree->getStatisticsInRange(rangeStart, rangeEnd); d->globalStats = (rangeStart == -1) && (rangeEnd == -1);
d->m_globalStatsEnabled = d->m_eventTree->isRangeGlobal(rangeStart, rangeEnd);
} }
QModelIndex QmlProfilerEventsWidget::selectedItem() const QModelIndex QmlProfilerEventsWidget::selectedItem() const
@@ -230,23 +221,18 @@ void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
copyRowAction = menu.addAction(tr("Copy Row")); copyRowAction = menu.addAction(tr("Copy Row"));
copyTableAction = menu.addAction(tr("Copy Table")); copyTableAction = menu.addAction(tr("Copy Table"));
if (isQml()) { showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics"));
// only for qml events view, not for v8 showExtendedStatsAction->setCheckable(true);
showExtendedStatsAction = menu.addAction(tr("Extended Event Statistics")); showExtendedStatsAction->setChecked(showExtendedStatistics());
showExtendedStatsAction->setCheckable(true);
showExtendedStatsAction->setChecked(showExtendedStatistics());
}
} }
if (isQml()) { menu.addSeparator();
menu.addSeparator(); getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range"));
getLocalStatsAction = menu.addAction(tr("Limit Events Pane to Current Range")); if (!d->m_viewContainer->hasValidSelection())
if (!d->m_viewContainer->hasValidSelection()) getLocalStatsAction->setEnabled(false);
getLocalStatsAction->setEnabled(false); getGlobalStatsAction = menu.addAction(tr("Reset Events Pane"));
getGlobalStatsAction = menu.addAction(tr("Reset Events Pane")); if (hasGlobalStats())
if (hasGlobalStats()) getGlobalStatsAction->setEnabled(false);
getGlobalStatsAction->setEnabled(false);
}
QAction *selectedAction = menu.exec(position); QAction *selectedAction = menu.exec(position);
@@ -259,12 +245,8 @@ void QmlProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
getStatisticsInRange(d->m_viewContainer->selectionStart(), getStatisticsInRange(d->m_viewContainer->selectionStart(),
d->m_viewContainer->selectionEnd()); d->m_viewContainer->selectionEnd());
} }
if (selectedAction == getGlobalStatsAction) { if (selectedAction == getGlobalStatsAction)
if (d->m_profilerDataModel) { getStatisticsInRange(-1, -1);
getStatisticsInRange(d->m_profilerDataModel->traceStartTime(),
d->m_profilerDataModel->traceEndTime());
}
}
if (selectedAction == showExtendedStatsAction) if (selectedAction == showExtendedStatsAction)
setShowExtendedStatistics(!showExtendedStatistics()); setShowExtendedStatistics(!showExtendedStatistics());
} }
@@ -293,24 +275,20 @@ void QmlProfilerEventsWidget::copyRowToClipboard() const
d->m_eventTree->copyRowToClipboard(); d->m_eventTree->copyRowToClipboard();
} }
void QmlProfilerEventsWidget::updateSelectedEvent(int eventId) const void QmlProfilerEventsWidget::updateSelectedEvent(const QString &eventHash) const
{ {
if (d->m_eventTree->selectedEventId() != eventId) if (d->m_eventTree->selectedEventHash() != eventHash)
d->m_eventTree->selectEvent(eventId); d->m_eventTree->selectEvent(eventHash);
} }
void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column) void QmlProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
{ {
// This slot is used to connect the javascript pane with the qml events pane d->m_eventTree->selectEventByLocation(filename, line, column);
// Our javascript trace data does not store column information
// thus we ignore it here
Q_UNUSED(column);
d->m_eventTree->selectEventByLocation(filename, line);
} }
bool QmlProfilerEventsWidget::hasGlobalStats() const bool QmlProfilerEventsWidget::hasGlobalStats() const
{ {
return d->m_globalStatsEnabled; return d->globalStats;
} }
void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show) void QmlProfilerEventsWidget::setShowExtendedStatistics(bool show)
@@ -323,15 +301,6 @@ bool QmlProfilerEventsWidget::showExtendedStatistics() const
return d->m_eventTree->showExtendedStatistics(); return d->m_eventTree->showExtendedStatistics();
} }
bool QmlProfilerEventsWidget::isQml() const
{
return d->m_eventTree->viewType() == QmlProfilerEventsMainView::EventsView;
}
bool QmlProfilerEventsWidget::isV8() const
{
return d->m_eventTree->viewType() == QmlProfilerEventsMainView::V8ProfileView;
}
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
@@ -339,17 +308,14 @@ class QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate
public: public:
QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {} QmlProfilerEventsMainViewPrivate(QmlProfilerEventsMainView *qq) : q(qq) {}
void buildModelFromList(const QList<QmlRangeEventData *> &list, QStandardItem *parentItem );
void buildV8ModelFromList( const QList<QV8EventData *> &list );
int getFieldCount(); int getFieldCount();
QString textForItem(QStandardItem *item, bool recursive) const; QString textForItem(QStandardItem *item, bool recursive = false) const;
QmlProfilerEventsMainView *q; QmlProfilerEventsMainView *q;
QmlProfilerEventsMainView::ViewTypes m_viewType; QmlProfilerEventsModelProxy *modelProxy;
QmlProfilerDataModel *m_profilerDataModel;
QStandardItemModel *m_model; QStandardItemModel *m_model;
QList<bool> m_fieldShown; QList<bool> m_fieldShown;
QHash<int, int> m_columnIndex; // maps field enum to column index QHash<int, int> m_columnIndex; // maps field enum to column index
@@ -361,49 +327,52 @@ public:
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
QmlProfilerEventsMainView::QmlProfilerEventsMainView(ViewTypes viewType, QmlProfilerEventsMainView::QmlProfilerEventsMainView(QWidget *parent,
QWidget *parent, QmlProfilerEventsModelProxy *modelProxy)
QmlProfilerDataModel *dataModel) : QmlProfilerTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
: QTreeView(parent), d(new QmlProfilerEventsMainViewPrivate(this))
{ {
setObjectName(QLatin1String("QmlProfilerEventsTable")); setObjectName(QLatin1String("QmlProfilerEventsTable"));
header()->setResizeMode(QHeaderView::Interactive);
header()->setDefaultSectionSize(100);
header()->setMinimumSectionSize(50);
setSortingEnabled(false); setSortingEnabled(false);
setFrameStyle(QFrame::NoFrame);
d->m_model = new QStandardItemModel(this); d->m_model = new QStandardItemModel(this);
setModel(d->m_model); setModel(d->m_model);
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex))); connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
d->m_profilerDataModel = dataModel; d->modelProxy = modelProxy;
connect(d->m_profilerDataModel,SIGNAL(stateChanged()), connect(d->modelProxy,SIGNAL(dataAvailable()), this, SLOT(buildModel()));
this,SLOT(profilerDataModelStateChanged())); // connect(d->modelProxy,SIGNAL(stateChanged()),
connect(d->m_profilerDataModel,SIGNAL(detailsChanged(int,QString)), // this,SLOT(profilerDataModelStateChanged()));
this,SLOT(changeDetailsForEvent(int,QString)));
d->m_firstNumericColumn = 0; d->m_firstNumericColumn = 0;
d->m_preventSelectBounce = false; d->m_preventSelectBounce = false;
d->m_showExtendedStatistics = false; d->m_showExtendedStatistics = false;
setViewType(viewType); setFieldViewable(Name, true);
setFieldViewable(Type, true);
setFieldViewable(TimeInPercent, true);
setFieldViewable(TotalTime, true);
setFieldViewable(SelfTimeInPercent, false);
setFieldViewable(SelfTime, false);
setFieldViewable(CallCount, true);
setFieldViewable(TimePerCall, true);
setFieldViewable(MaxTime, true);
setFieldViewable(MinTime, true);
setFieldViewable(MedianTime, true);
setFieldViewable(Details, true);
buildModel();
} }
QmlProfilerEventsMainView::~QmlProfilerEventsMainView() QmlProfilerEventsMainView::~QmlProfilerEventsMainView()
{ {
clear(); clear();
//delete d->modelProxy;
delete d->m_model; delete d->m_model;
delete d; delete d;
} }
void QmlProfilerEventsMainView::profilerDataModelStateChanged() void QmlProfilerEventsMainView::profilerDataModelStateChanged()
{ {
if (d->m_profilerDataModel) {
QmlProfilerDataModel::State newState = d->m_profilerDataModel->currentState();
if (newState == QmlProfilerDataModel::Done)
buildModel();
}
} }
void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show) void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
@@ -418,52 +387,6 @@ void QmlProfilerEventsMainView::setFieldViewable(Fields field, bool show)
} }
} }
QmlProfilerEventsMainView::ViewTypes QmlProfilerEventsMainView::viewType() const
{
return d->m_viewType;
}
void QmlProfilerEventsMainView::setViewType(ViewTypes type)
{
d->m_viewType = type;
switch (type) {
case EventsView: {
setObjectName(QLatin1String("QmlProfilerEventsTable"));
setFieldViewable(Name, true);
setFieldViewable(Type, true);
setFieldViewable(Percent, true);
setFieldViewable(TotalDuration, true);
setFieldViewable(SelfPercent, false);
setFieldViewable(SelfDuration, false);
setFieldViewable(CallCount, true);
setFieldViewable(TimePerCall, true);
setFieldViewable(MaxTime, true);
setFieldViewable(MinTime, true);
setFieldViewable(MedianTime, true);
setFieldViewable(Details, true);
break;
}
case V8ProfileView: {
setObjectName(QLatin1String("QmlProfilerV8ProfileTable"));
setFieldViewable(Name, true);
setFieldViewable(Type, false);
setFieldViewable(Percent, true);
setFieldViewable(TotalDuration, true);
setFieldViewable(SelfPercent, true);
setFieldViewable(SelfDuration, true);
setFieldViewable(CallCount, false);
setFieldViewable(TimePerCall, false);
setFieldViewable(MaxTime, false);
setFieldViewable(MinTime, false);
setFieldViewable(MedianTime, false);
setFieldViewable(Details, true);
break;
}
default: break;
}
buildModel();
}
void QmlProfilerEventsMainView::setHeaderLabels() void QmlProfilerEventsMainView::setHeaderLabels()
{ {
@@ -473,53 +396,53 @@ void QmlProfilerEventsMainView::setHeaderLabels()
d->m_columnIndex.clear(); d->m_columnIndex.clear();
if (d->m_fieldShown[Name]) { if (d->m_fieldShown[Name]) {
d->m_columnIndex[Name] = fieldIndex; d->m_columnIndex[Name] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Location"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Location)));
d->m_firstNumericColumn++; d->m_firstNumericColumn++;
} }
if (d->m_fieldShown[Type]) { if (d->m_fieldShown[Type]) {
d->m_columnIndex[Type] = fieldIndex; d->m_columnIndex[Type] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Type"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
d->m_firstNumericColumn++; d->m_firstNumericColumn++;
} }
if (d->m_fieldShown[Percent]) { if (d->m_fieldShown[TimeInPercent]) {
d->m_columnIndex[Percent] = fieldIndex; d->m_columnIndex[TimeInPercent] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Time in Percent"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimeInPercent)));
} }
if (d->m_fieldShown[TotalDuration]) { if (d->m_fieldShown[TotalTime]) {
d->m_columnIndex[TotalDuration] = fieldIndex; d->m_columnIndex[TotalTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Total Time"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
} }
if (d->m_fieldShown[SelfPercent]) { if (d->m_fieldShown[SelfTimeInPercent]) {
d->m_columnIndex[Type] = fieldIndex; d->m_columnIndex[Type] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time in Percent"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTimeInPercent)));
} }
if (d->m_fieldShown[SelfDuration]) { if (d->m_fieldShown[SelfTime]) {
d->m_columnIndex[SelfDuration] = fieldIndex; d->m_columnIndex[SelfTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Self Time"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTime)));
} }
if (d->m_fieldShown[CallCount]) { if (d->m_fieldShown[CallCount]) {
d->m_columnIndex[CallCount] = fieldIndex; d->m_columnIndex[CallCount] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Calls"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
} }
if (d->m_fieldShown[TimePerCall]) { if (d->m_fieldShown[TimePerCall]) {
d->m_columnIndex[TimePerCall] = fieldIndex; d->m_columnIndex[TimePerCall] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Mean Time"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimePerCall)));
} }
if (d->m_fieldShown[MedianTime]) { if (d->m_fieldShown[MedianTime]) {
d->m_columnIndex[MedianTime] = fieldIndex; d->m_columnIndex[MedianTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Median Time"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MedianTime)));
} }
if (d->m_fieldShown[MaxTime]) { if (d->m_fieldShown[MaxTime]) {
d->m_columnIndex[MaxTime] = fieldIndex; d->m_columnIndex[MaxTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Longest Time"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MaxTime)));
} }
if (d->m_fieldShown[MinTime]) { if (d->m_fieldShown[MinTime]) {
d->m_columnIndex[MinTime] = fieldIndex; d->m_columnIndex[MinTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Shortest Time"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MinTime)));
} }
if (d->m_fieldShown[Details]) { if (d->m_fieldShown[Details]) {
d->m_columnIndex[Details] = fieldIndex; d->m_columnIndex[Details] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(tr("Details"))); d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Details)));
} }
} }
@@ -569,47 +492,41 @@ int QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::getFieldCount()
void QmlProfilerEventsMainView::buildModel() void QmlProfilerEventsMainView::buildModel()
{ {
if (d->m_profilerDataModel) { clear();
clear(); parseModelProxy();
if (d->m_viewType == V8ProfileView) setShowExtendedStatistics(d->m_showExtendedStatistics);
d->buildV8ModelFromList( d->m_profilerDataModel->getV8Events() );
else
d->buildModelFromList( d->m_profilerDataModel->getEventDescriptions(), d->m_model->invisibleRootItem() );
setShowExtendedStatistics(d->m_showExtendedStatistics); setRootIsDecorated(false);
setSortingEnabled(true);
sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
setRootIsDecorated(false); expandAll();
setSortingEnabled(true); if (d->m_fieldShown[Name])
sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder); resizeColumnToContents(0);
expandAll(); if (d->m_fieldShown[Type])
if (d->m_fieldShown[Name]) resizeColumnToContents(d->m_fieldShown[Name]?1:0);
resizeColumnToContents(0); collapseAll();
if (d->m_fieldShown[Type])
resizeColumnToContents(d->m_fieldShown[Name]?1:0);
collapseAll();
}
} }
void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFromList( const QList<QmlRangeEventData *> &list, QStandardItem *parentItem) void QmlProfilerEventsMainView::parseModelProxy()
{ {
foreach (QmlRangeEventData *binding, list) { const QList <QmlProfilerEventsModelProxy::QmlEventStats> eventList = d->modelProxy->getData();
if (binding->calls == 0) foreach (const QmlProfilerEventsModelProxy::QmlEventStats &event, eventList) {
continue; QStandardItem *parentItem = d->m_model->invisibleRootItem();
QList<QStandardItem *> newRow; QList<QStandardItem *> newRow;
if (m_fieldShown[Name])
newRow << new EventsViewItem(binding->displayName);
if (m_fieldShown[Type]) { if (d->m_fieldShown[Name])
QString typeString = QmlProfilerEventsMainView::nameForType(binding->eventType); newRow << new EventsViewItem(event.displayName);
if (d->m_fieldShown[Type]) {
QString typeString = QmlProfilerEventsMainView::nameForType(event.eventType);
QString toolTipText; QString toolTipText;
if (binding->eventType == Binding) { if (event.eventType == Binding) {
if (binding->bindingType == (int)OptimizedBinding) { if (event.bindingType == (int)OptimizedBinding) {
typeString = typeString + tr(" (Opt)"); typeString = typeString + tr(" (Opt)");
toolTipText = tr("Binding is evaluated by the optimized engine."); toolTipText = tr("Binding is evaluated by the optimized engine.");
} else if (binding->bindingType == (int)V8Binding) { } else if (event.bindingType == (int)V8Binding) {
toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n" toolTipText = tr("Binding not optimized (e.g. has side effects or assignments,\n"
"references to elements in other files, loops, etc.)"); "references to elements in other files, loops, etc.)");
@@ -621,44 +538,44 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
newRow.last()->setToolTip(toolTipText); newRow.last()->setToolTip(toolTipText);
} }
if (m_fieldShown[Percent]) { if (d->m_fieldShown[TimeInPercent]) {
newRow << new EventsViewItem(QString::number(binding->percentOfTime,'f',2)+QLatin1String(" %")); newRow << new EventsViewItem(QString::number(event.percentOfTime,'f',2)+QLatin1String(" %"));
newRow.last()->setData(QVariant(binding->percentOfTime)); newRow.last()->setData(QVariant(event.percentOfTime));
} }
if (m_fieldShown[TotalDuration]) { if (d->m_fieldShown[TotalTime]) {
newRow << new EventsViewItem(displayTime(binding->duration)); newRow << new EventsViewItem(displayTime(event.duration));
newRow.last()->setData(QVariant(binding->duration)); newRow.last()->setData(QVariant(event.duration));
} }
if (m_fieldShown[CallCount]) { if (d->m_fieldShown[CallCount]) {
newRow << new EventsViewItem(QString::number(binding->calls)); newRow << new EventsViewItem(QString::number(event.calls));
newRow.last()->setData(QVariant(binding->calls)); newRow.last()->setData(QVariant(event.calls));
} }
if (m_fieldShown[TimePerCall]) { if (d->m_fieldShown[TimePerCall]) {
newRow << new EventsViewItem(displayTime(binding->timePerCall)); newRow << new EventsViewItem(displayTime(event.timePerCall));
newRow.last()->setData(QVariant(binding->timePerCall)); newRow.last()->setData(QVariant(event.timePerCall));
} }
if (m_fieldShown[MedianTime]) { if (d->m_fieldShown[MedianTime]) {
newRow << new EventsViewItem(displayTime(binding->medianTime)); newRow << new EventsViewItem(displayTime(event.medianTime));
newRow.last()->setData(QVariant(binding->medianTime)); newRow.last()->setData(QVariant(event.medianTime));
} }
if (m_fieldShown[MaxTime]) { if (d->m_fieldShown[MaxTime]) {
newRow << new EventsViewItem(displayTime(binding->maxTime)); newRow << new EventsViewItem(displayTime(event.maxTime));
newRow.last()->setData(QVariant(binding->maxTime)); newRow.last()->setData(QVariant(event.maxTime));
} }
if (m_fieldShown[MinTime]) { if (d->m_fieldShown[MinTime]) {
newRow << new EventsViewItem(displayTime(binding->minTime)); newRow << new EventsViewItem(displayTime(event.minTime));
newRow.last()->setData(QVariant(binding->minTime)); newRow.last()->setData(QVariant(event.minTime));
} }
if (m_fieldShown[Details]) { if (d->m_fieldShown[Details]) {
newRow << new EventsViewItem(binding->details); newRow << new EventsViewItem(event.details);
newRow.last()->setData(QVariant(binding->details)); newRow.last()->setData(QVariant(event.details));
} }
@@ -669,16 +586,17 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
item->setEditable(false); item->setEditable(false);
// metadata // metadata
newRow.at(0)->setData(QVariant(binding->eventHashStr),EventHashStrRole); newRow.at(0)->setData(QVariant(event.eventHashStr),EventHashStrRole);
newRow.at(0)->setData(QVariant(binding->location.filename),FilenameRole); newRow.at(0)->setData(QVariant(event.location.filename),FilenameRole);
newRow.at(0)->setData(QVariant(binding->location.line),LineRole); newRow.at(0)->setData(QVariant(event.location.line),LineRole);
newRow.at(0)->setData(QVariant(binding->location.column),ColumnRole); newRow.at(0)->setData(QVariant(event.location.column),ColumnRole);
newRow.at(0)->setData(QVariant(binding->eventId),EventIdRole);
if (binding->isBindingLoop) if (event.isBindingLoop) {
foreach (QStandardItem *item, newRow) { foreach (QStandardItem *item, newRow) {
item->setBackground(colors()->bindingLoopBackground); item->setBackground(colors()->bindingLoopBackground);
item->setToolTip(tr("Binding loop detected.")); item->setToolTip(tr("Binding loop detected."));
} }
}
// append // append
parentItem->appendRow(newRow); parentItem->appendRow(newRow);
@@ -686,58 +604,6 @@ void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildModelFrom
} }
} }
void QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
{
for (int index = 0; index < list.count(); index++) {
QV8EventData *v8event = list.at(index);
QList<QStandardItem *> newRow;
if (m_fieldShown[Name])
newRow << new EventsViewItem(v8event->displayName);
if (m_fieldShown[Percent]) {
newRow << new EventsViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
newRow.last()->setData(QVariant(v8event->totalPercent));
}
if (m_fieldShown[TotalDuration]) {
newRow << new EventsViewItem(displayTime(v8event->totalTime));
newRow.last()->setData(QVariant(v8event->totalTime));
}
if (m_fieldShown[SelfPercent]) {
newRow << new EventsViewItem(QString::number(v8event->selfPercent,'f',2)+QLatin1String(" %"));
newRow.last()->setData(QVariant(v8event->selfPercent));
}
if (m_fieldShown[SelfDuration]) {
newRow << new EventsViewItem(displayTime(v8event->selfTime));
newRow.last()->setData(QVariant(v8event->selfTime));
}
if (m_fieldShown[Details]) {
newRow << new EventsViewItem(v8event->functionName);
newRow.last()->setData(QVariant(v8event->functionName));
}
if (!newRow.isEmpty()) {
// no edit
foreach (QStandardItem *item, newRow)
item->setEditable(false);
// metadata
newRow.at(0)->setData(QString::fromLatin1("%1:%2").arg(v8event->filename, QString::number(v8event->line)), EventHashStrRole);
newRow.at(0)->setData(QVariant(v8event->filename), FilenameRole);
newRow.at(0)->setData(QVariant(v8event->line), LineRole);
newRow.at(0)->setData(QVariant(0),ColumnRole); // v8 events have no column info
newRow.at(0)->setData(QVariant(v8event->eventId), EventIdRole);
// append
m_model->invisibleRootItem()->appendRow(newRow);
}
}
}
QString QmlProfilerEventsMainView::displayTime(double time) QString QmlProfilerEventsMainView::displayTime(double time)
{ {
if (time < 1e6) if (time < 1e6)
@@ -762,26 +628,16 @@ QString QmlProfilerEventsMainView::nameForType(int typeNumber)
void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd) void QmlProfilerEventsMainView::getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd)
{ {
if (d->m_profilerDataModel) d->modelProxy->limitToRange(rangeStart, rangeEnd);
d->m_profilerDataModel->compileStatistics(rangeStart, rangeEnd);
buildModel();
} }
bool QmlProfilerEventsMainView::isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const QString QmlProfilerEventsMainView::selectedEventHash() const
{
if (d->m_profilerDataModel)
return d->m_profilerDataModel->traceStartTime() == rangeStart && d->m_profilerDataModel->traceEndTime() == rangeEnd;
else
return true;
}
int QmlProfilerEventsMainView::selectedEventId() const
{ {
QModelIndex index = selectedItem(); QModelIndex index = selectedItem();
if (!index.isValid()) if (!index.isValid())
return -1; return QString();
QStandardItem *item = d->m_model->item(index.row(), 0); QStandardItem *item = d->m_model->item(index.row(), 0);
return item->data(EventIdRole).toInt(); return item->data(EventHashStrRole).toString();
} }
@@ -806,20 +662,16 @@ void QmlProfilerEventsMainView::jumpToItem(const QModelIndex &index)
emit gotoSourceLocation(fileName, line, column); emit gotoSourceLocation(fileName, line, column);
// show in callers/callees subwindow // show in callers/callees subwindow
emit eventSelected(infoItem->data(EventIdRole).toInt()); emit eventSelected(infoItem->data(EventHashStrRole).toString());
// show in timelinerenderer
if (d->m_viewType == EventsView)
emit showEventInTimeline(infoItem->data(EventIdRole).toInt());
d->m_preventSelectBounce = false; d->m_preventSelectBounce = false;
} }
void QmlProfilerEventsMainView::selectEvent(int eventId) void QmlProfilerEventsMainView::selectEvent(const QString &eventHash)
{ {
for (int i=0; i<d->m_model->rowCount(); i++) { for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0); QStandardItem *infoItem = d->m_model->item(i, 0);
if (infoItem->data(EventIdRole).toInt() == eventId) { if (infoItem->data(EventHashStrRole).toString() == eventHash) {
setCurrentIndex(d->m_model->indexFromItem(infoItem)); setCurrentIndex(d->m_model->indexFromItem(infoItem));
jumpToItem(currentIndex()); jumpToItem(currentIndex());
return; return;
@@ -827,14 +679,18 @@ void QmlProfilerEventsMainView::selectEvent(int eventId)
} }
} }
void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line) void QmlProfilerEventsMainView::selectEventByLocation(const QString &filename, int line, int column)
{ {
if (d->m_preventSelectBounce) if (d->m_preventSelectBounce)
return; return;
for (int i=0; i<d->m_model->rowCount(); i++) { for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0); QStandardItem *infoItem = d->m_model->item(i, 0);
if (currentIndex() != d->m_model->indexFromItem(infoItem) && infoItem->data(FilenameRole).toString() == filename && infoItem->data(LineRole).toInt() == line) { if (currentIndex() != d->m_model->indexFromItem(infoItem) &&
infoItem->data(FilenameRole).toString() == filename &&
infoItem->data(LineRole).toInt() == line &&
(column == -1 ||
infoItem->data(ColumnRole).toInt() == column)) {
setCurrentIndex(d->m_model->indexFromItem(infoItem)); setCurrentIndex(d->m_model->indexFromItem(infoItem));
jumpToItem(currentIndex()); jumpToItem(currentIndex());
return; return;
@@ -851,23 +707,7 @@ QModelIndex QmlProfilerEventsMainView::selectedItem() const
return sel.first(); return sel.first();
} }
void QmlProfilerEventsMainView::changeDetailsForEvent(int eventId, const QString &newString) QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive) const
{
// available only for QML
if (d->m_viewType != EventsView)
return;
for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0);
if (infoItem->data(EventIdRole).toInt() == eventId) {
d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString),Qt::DisplayRole);
d->m_model->item(i,d->m_columnIndex[Details])->setData(QVariant(newString));
return;
}
}
}
QString QmlProfilerEventsMainView::QmlProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
{ {
QString str; QString str;
@@ -931,114 +771,82 @@ void QmlProfilerEventsMainView::copyRowToClipboard() const
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
QmlProfilerEventsParentsAndChildrenView::QmlProfilerEventsParentsAndChildrenView( class QmlProfilerEventRelativesView::QmlProfilerEventParentsViewPrivate
SubViewType subtableType, QWidget *parent, QmlProfilerDataModel *model)
: QTreeView(parent)
{ {
m_profilerDataModel = model; public:
QmlProfilerEventParentsViewPrivate(QmlProfilerEventRelativesView *qq):q(qq) {}
~QmlProfilerEventParentsViewPrivate() {}
QmlProfilerEventRelativesModelProxy *modelProxy;
QmlProfilerEventRelativesView *q;
};
QmlProfilerEventRelativesView::QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager, QmlProfilerEventRelativesModelProxy *modelProxy, QWidget *parent)
: QmlProfilerTreeView(parent), d(new QmlProfilerEventParentsViewPrivate(this))
{
Q_UNUSED(modelManager);
setSortingEnabled(false);
d->modelProxy = modelProxy;
setModel(new QStandardItemModel(this)); setModel(new QStandardItemModel(this));
setRootIsDecorated(false); setRootIsDecorated(false);
setFrameStyle(QFrame::NoFrame);
m_subtableType = subtableType;
updateHeader(); updateHeader();
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex))); connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
} }
QmlProfilerEventsParentsAndChildrenView::~QmlProfilerEventsParentsAndChildrenView() QmlProfilerEventRelativesView::~QmlProfilerEventRelativesView()
{ {
delete d;
} }
void QmlProfilerEventsParentsAndChildrenView::setViewType(SubViewType type) void QmlProfilerEventRelativesView::displayEvent(const QString &eventHash)
{ {
m_subtableType = type; rebuildTree(d->modelProxy->getData(eventHash));
updateHeader();
}
void QmlProfilerEventsParentsAndChildrenView::displayEvent(int eventId)
{
if (!m_profilerDataModel)
return;
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
if (isV8) {
QV8EventData *v8event = m_profilerDataModel->v8EventDescription(eventId);
if (v8event) {
if (isChildren) {
QList <QV8EventSub *> childrenList = v8event->childrenHash.values();
rebuildTree((QObject *)&childrenList);
} else {
QList <QV8EventSub *> parentList = v8event->parentHash.values();
rebuildTree((QObject *)&parentList);
}
}
} else {
QmlRangeEventData *qmlEvent = m_profilerDataModel->eventDescription(eventId);
if (qmlEvent) {
if (isChildren) {
QList <QmlRangeEventRelative *> childrenList = qmlEvent->childrenHash.values();
rebuildTree((QObject *)&childrenList);
} else {
QList <QmlRangeEventRelative *> parentList = qmlEvent->parentHash.values();
rebuildTree((QObject *)&parentList);
}
}
}
updateHeader(); updateHeader();
resizeColumnToContents(0); resizeColumnToContents(0);
setSortingEnabled(true); setSortingEnabled(true);
if (isV8) sortByColumn(2);
sortByColumn(1);
else
sortByColumn(2);
} }
void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataModel) void QmlProfilerEventRelativesView::rebuildTree(QmlProfilerEventRelativesModelProxy::QmlEventRelativesMap eventMap)
{ {
Q_ASSERT(treeModel()); Q_ASSERT(treeModel());
treeModel()->clear(); treeModel()->clear();
QStandardItem *topLevelItem = treeModel()->invisibleRootItem(); QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView;
QList <QmlRangeEventRelative *> *qmlList = static_cast< QList <QmlRangeEventRelative *> *>(profilerDataModel); //foreach (const QmlProfilerEventParentsModelProxy::QmlEventParentData &event, eventMap.values()) {
QList <QV8EventSub*> *v8List = static_cast< QList <QV8EventSub *> *>(profilerDataModel); foreach (const QString &key, eventMap.keys()) {
const QmlProfilerEventRelativesModelProxy::QmlEventRelativesData &event = eventMap[key];
int listLength;
if (!isV8)
listLength = qmlList->length();
else
listLength = v8List->length();
for (int index=0; index < listLength; index++) {
QList<QStandardItem *> newRow; QList<QStandardItem *> newRow;
if (!isV8) {
QmlRangeEventRelative *event = qmlList->at(index);
newRow << new EventsViewItem(event->reference->displayName); // ToDo: here we were going to search for the data in the other modelproxy
newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType)); // maybe we should store the data in this proxy and get it here
newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration)); // no indirections at this level of abstraction!
newRow << new EventsViewItem(QString::number(event->calls)); newRow << new EventsViewItem(event.displayName);
newRow << new EventsViewItem(event->reference->details); newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event.eventType));
newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole); newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event.duration));
newRow.at(2)->setData(QVariant(event->duration)); newRow << new EventsViewItem(QString::number(event.calls));
newRow.at(3)->setData(QVariant(event->calls)); newRow << new EventsViewItem(event.details);
if (event->inLoopPath)
foreach (QStandardItem *item, newRow) { // newRow << new EventsViewItem(event->reference->displayName);
item->setBackground(colors()->bindingLoopBackground); // newRow << new EventsViewItem(QmlProfilerEventsMainView::nameForType(event->reference->eventType));
item->setToolTip(tr("Part of binding loop.")); // newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->duration));
} // newRow << new EventsViewItem(QString::number(event->calls));
} else { // newRow << new EventsViewItem(event->reference->details);
QV8EventSub *event = v8List->at(index); newRow.at(0)->setData(QVariant(key), EventHashStrRole);
newRow << new EventsViewItem(event->reference->displayName); newRow.at(2)->setData(QVariant(event.duration));
newRow << new EventsViewItem(QmlProfilerEventsMainView::displayTime(event->totalTime)); newRow.at(3)->setData(QVariant(event.calls));
newRow << new EventsViewItem(event->reference->functionName);
newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole); if (event.isBindingLoop) {
newRow.at(1)->setData(QVariant(event->totalTime)); foreach (QStandardItem *item, newRow) {
item->setBackground(colors()->bindingLoopBackground);
item->setToolTip(tr("Part of binding loop."));
}
} }
foreach (QStandardItem *item, newRow) foreach (QStandardItem *item, newRow)
item->setEditable(false); item->setEditable(false);
@@ -1046,7 +854,7 @@ void QmlProfilerEventsParentsAndChildrenView::rebuildTree(void *profilerDataMode
} }
} }
void QmlProfilerEventsParentsAndChildrenView::clear() void QmlProfilerEventRelativesView::clear()
{ {
if (treeModel()) { if (treeModel()) {
treeModel()->clear(); treeModel()->clear();
@@ -1054,52 +862,43 @@ void QmlProfilerEventsParentsAndChildrenView::clear()
} }
} }
void QmlProfilerEventsParentsAndChildrenView::updateHeader() void QmlProfilerEventRelativesView::updateHeader()
{ {
bool isV8 = m_subtableType == V8ParentsView || m_subtableType == V8ChildrenView; bool calleesView = qobject_cast<QmlProfilerEventChildrenModelProxy *>(d->modelProxy) != 0;
bool isChildren = m_subtableType == ChildrenView || m_subtableType == V8ChildrenView;
header()->setResizeMode(QHeaderView::Interactive);
header()->setDefaultSectionSize(100);
header()->setMinimumSectionSize(50);
if (treeModel()) { if (treeModel()) {
if (isV8) treeModel()->setColumnCount(5);
treeModel()->setColumnCount(3);
else
treeModel()->setColumnCount(5);
int columnIndex = 0; int columnIndex = 0;
if (isChildren) if (calleesView)
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee"))); treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Callee)));
else else
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller"))); treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Caller)));
if (!isV8) treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Type")));
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Total Time"))); treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
if (!isV8) treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Calls")));
if (isChildren)
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Callee Description"))); if (calleesView)
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CalleeDescription)));
else else
treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(tr("Caller Description"))); treeModel()->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallerDescription)));
} }
} }
QStandardItemModel *QmlProfilerEventsParentsAndChildrenView::treeModel() QStandardItemModel *QmlProfilerEventRelativesView::treeModel()
{ {
return qobject_cast<QStandardItemModel *>(model()); return qobject_cast<QStandardItemModel *>(model());
} }
void QmlProfilerEventsParentsAndChildrenView::jumpToItem(const QModelIndex &index) void QmlProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
{ {
if (treeModel()) { if (treeModel()) {
QStandardItem *infoItem = treeModel()->item(index.row(), 0); QStandardItem *infoItem = treeModel()->item(index.row(), 0);
emit eventClicked(infoItem->data(EventIdRole).toInt()); emit eventClicked(infoItem->data(EventHashStrRole).toString());
} }
} }

View File

@@ -33,7 +33,9 @@
#include <QTreeView> #include <QTreeView>
#include <QStandardItemModel> #include <QStandardItemModel>
#include <qmldebug/qmlprofilereventtypes.h> #include <qmldebug/qmlprofilereventtypes.h>
#include "qmlprofilerdatamodel.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilereventsmodelproxy.h"
#include "qmlprofilertreeview.h"
#include <analyzerbase/ianalyzertool.h> #include <analyzerbase/ianalyzertool.h>
@@ -43,7 +45,8 @@ namespace QmlProfiler {
namespace Internal { namespace Internal {
class QmlProfilerEventsMainView; class QmlProfilerEventsMainView;
class QmlProfilerEventsParentsAndChildrenView; class QmlProfilerEventChildrenView;
class QmlProfilerEventRelativesView;
enum ItemRole { enum ItemRole {
EventHashStrRole = Qt::UserRole+1, EventHashStrRole = Qt::UserRole+1,
@@ -60,10 +63,9 @@ public:
explicit QmlProfilerEventsWidget(QWidget *parent, explicit QmlProfilerEventsWidget(QWidget *parent,
QmlProfilerTool *profilerTool, QmlProfilerTool *profilerTool,
QmlProfilerViewManager *container, QmlProfilerViewManager *container,
QmlProfilerDataModel *profilerDataModel ); QmlProfilerModelManager *profilerModelManager );
~QmlProfilerEventsWidget(); ~QmlProfilerEventsWidget();
void switchToV8View();
void clear(); void clear();
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd); void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
@@ -76,16 +78,14 @@ public:
void setShowExtendedStatistics(bool show); void setShowExtendedStatistics(bool show);
bool showExtendedStatistics() const; bool showExtendedStatistics() const;
bool isQml() const;
bool isV8() const;
signals: signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber); void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
void showEventInTimeline(int eventId); void eventSelectedByHash(const QString &eventHash);
void resized(); void resized();
public slots: public slots:
void updateSelectedEvent(int eventId) const; void updateSelectedEvent(const QString &eventHash) const;
void selectBySourceLocation(const QString &filename, int line, int column); void selectBySourceLocation(const QString &filename, int line, int column);
private slots: private slots:
@@ -100,44 +100,15 @@ private:
QmlProfilerEventsWidgetPrivate *d; QmlProfilerEventsWidgetPrivate *d;
}; };
class QmlProfilerEventsMainView : public QTreeView class QmlProfilerEventsMainView : public QmlProfilerTreeView
{ {
Q_OBJECT Q_OBJECT
public: public:
enum Fields { explicit QmlProfilerEventsMainView(QWidget *parent,
Name, QmlProfilerEventsModelProxy *modelProxy);
Type,
Percent,
TotalDuration,
SelfPercent,
SelfDuration,
CallCount,
TimePerCall,
MaxTime,
MinTime,
MedianTime,
Details,
MaxFields
};
enum ViewTypes {
EventsView,
CallersView,
CalleesView,
V8ProfileView,
MaxViewTypes
};
explicit QmlProfilerEventsMainView(ViewTypes viewType,
QWidget *parent,
QmlProfilerDataModel *dataModel);
~QmlProfilerEventsMainView(); ~QmlProfilerEventsMainView();
void setFieldViewable(Fields field, bool show); void setFieldViewable(Fields field, bool show);
void setViewType(ViewTypes type);
ViewTypes viewType() const;
void setShowAnonymousEvents( bool showThem ); void setShowAnonymousEvents( bool showThem );
QModelIndex selectedItem() const; QModelIndex selectedItem() const;
@@ -148,30 +119,30 @@ public:
static QString nameForType(int typeNumber); static QString nameForType(int typeNumber);
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd); void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
bool isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const; // int selectedEventId() const;
int selectedEventId() const; QString selectedEventHash() const;
void setShowExtendedStatistics(bool); void setShowExtendedStatistics(bool);
bool showExtendedStatistics() const; bool showExtendedStatistics() const;
signals: signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber); void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
void eventSelected(int eventId); void eventSelected(const QString &eventHash);
void showEventInTimeline(int eventId);
public slots: public slots:
void clear(); void clear();
void jumpToItem(const QModelIndex &index); void jumpToItem(const QModelIndex &index);
void selectEvent(int eventId); void selectEvent(const QString &eventHash);
void selectEventByLocation(const QString &filename, int line); void selectEventByLocation(const QString &filename, int line, int column);
void buildModel(); void buildModel();
void changeDetailsForEvent(int eventId, const QString &newString);
private slots: private slots:
void profilerDataModelStateChanged(); void profilerDataModelStateChanged();
private: private:
void setHeaderLabels(); void setHeaderLabels();
void parseModelProxy();
private: private:
class QmlProfilerEventsMainViewPrivate; class QmlProfilerEventsMainViewPrivate;
@@ -179,40 +150,31 @@ private:
}; };
class QmlProfilerEventsParentsAndChildrenView : public QTreeView class QmlProfilerEventRelativesView : public QmlProfilerTreeView
{ {
Q_OBJECT Q_OBJECT
public: public:
enum SubViewType { explicit QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager,
ParentsView, QmlProfilerEventRelativesModelProxy *modelProxy,
ChildrenView, QWidget *parent );
V8ParentsView, ~QmlProfilerEventRelativesView();
V8ChildrenView,
MaxSubtableTypes
};
explicit QmlProfilerEventsParentsAndChildrenView(SubViewType subtableType,
QWidget *parent,
QmlProfilerDataModel *model);
~QmlProfilerEventsParentsAndChildrenView();
void setViewType(SubViewType type);
signals: signals:
void eventClicked(int eventId); void eventClicked(const QString &eventHash);
public slots: public slots:
void displayEvent(int eventId); void displayEvent(const QString &eventHash);
void jumpToItem(const QModelIndex &); void jumpToItem(const QModelIndex &);
void clear(); void clear();
private: private:
void rebuildTree(void *profilerDataModel); void rebuildTree(QmlProfilerEventParentsModelProxy::QmlEventRelativesMap eventMap);
void updateHeader(); void updateHeader();
QStandardItemModel *treeModel(); QStandardItemModel *treeModel();
QmlProfilerDataModel *m_profilerDataModel; // QmlProfilerModelManager *m_profilerModelManager;
SubViewType m_subtableType; class QmlProfilerEventParentsViewPrivate;
QmlProfilerEventParentsViewPrivate *d;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -0,0 +1,364 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilermodelmanager.h"
#include "qmlprofilersimplemodel.h"
#include "qmlprofilerprocessedmodel.h"
#include "qv8profilerdatamodel.h"
#include "qmlprofilertracefile.h"
#include <utils/qtcassert.h>
#include <QDebug>
#include <QFile>
namespace QmlProfiler {
namespace Internal {
/////////////////////////////////////////////////////////////////////
QmlProfilerDataState::QmlProfilerDataState(QmlProfilerModelManager *modelManager, QObject *parent)
: QObject(parent), m_state(Empty), m_modelManager(modelManager)
{
connect(this, SIGNAL(error(QString)), m_modelManager, SIGNAL(error(QString)));
connect(this, SIGNAL(stateChanged()), m_modelManager, SIGNAL(stateChanged()));
}
void QmlProfilerDataState::setState(QmlProfilerDataState::State state)
{
// It's not an error, we are continuously calling "AcquiringData" for example
if (m_state == state)
return;
switch (state) {
case Empty:
// if it's not empty, complain but go on
QTC_ASSERT(m_modelManager->isEmpty(), /**/);
break;
case AcquiringData:
// we're not supposed to receive new data while processing older data
QTC_ASSERT(m_state != ProcessingData, return);
break;
case ProcessingData:
QTC_ASSERT(m_state == AcquiringData, return);
break;
case Done:
QTC_ASSERT(m_state == ProcessingData || m_state == Empty, return);
break;
default:
emit error(tr("Trying to set unknown state in events list"));
break;
}
m_state = state;
emit stateChanged();
return;
}
/////////////////////////////////////////////////////////////////////
QmlProfilerTraceTime::QmlProfilerTraceTime(QObject *parent) : QObject(parent)
{
clear();
}
QmlProfilerTraceTime::~QmlProfilerTraceTime()
{
}
qint64 QmlProfilerTraceTime::startTime() const
{
return m_startTime;
}
qint64 QmlProfilerTraceTime::endTime() const
{
return m_endTime;
}
qint64 QmlProfilerTraceTime::duration() const
{
return endTime() - startTime();
}
void QmlProfilerTraceTime::clear()
{
m_startTime = -1;
m_endTime = 0;
}
void QmlProfilerTraceTime::setStartTime(qint64 time)
{
m_startTime = time;
}
void QmlProfilerTraceTime::setEndTime(qint64 time)
{
m_endTime = time;
}
} // namespace Internal
/////////////////////////////////////////////////////////////////////
class QmlProfilerModelManager::QmlProfilerModelManagerPrivate
{
public:
QmlProfilerModelManagerPrivate(QmlProfilerModelManager *qq) : q(qq) {}
~QmlProfilerModelManagerPrivate() {}
QmlProfilerModelManager *q;
QmlProfilerSimpleModel *model;
QV8ProfilerDataModel *v8Model;
QmlProfilerDataState *dataState;
QmlProfilerTraceTime *traceTime;
QVector <double> partialCounts;
double progress;
qint64 estimatedTime;
// file to load
QString fileName;
};
QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent) :
QObject(parent), d(new QmlProfilerModelManagerPrivate(this))
{
d->model = new QmlProfilerProcessedModel(finder, this);
d->v8Model = new QV8ProfilerDataModel(this);
// d->model = new QmlProfilerSimpleModel(this);
d->dataState = new QmlProfilerDataState(this, this);
d->traceTime = new QmlProfilerTraceTime(this);
}
QmlProfilerModelManager::~QmlProfilerModelManager()
{
delete d;
}
QmlProfilerTraceTime *QmlProfilerModelManager::traceTime() const
{
return d->traceTime;
}
QmlProfilerSimpleModel *QmlProfilerModelManager::simpleModel() const
{
return d->model;
}
QV8ProfilerDataModel *QmlProfilerModelManager::v8Model() const
{
return d->v8Model;
}
bool QmlProfilerModelManager::isEmpty() const
{
return d->model->isEmpty() && d->v8Model->isEmpty();
}
int QmlProfilerModelManager::count() const
{
return d->model->count();
}
double QmlProfilerModelManager::progress() const
{
return d->progress;
}
int QmlProfilerModelManager::registerModelProxy()
{
d->partialCounts << 0;
return d->partialCounts.count()-1;
}
void QmlProfilerModelManager::modelProxyCountUpdated(int proxyId, qint64 count, qint64 max)
{
d->progress -= d->partialCounts[proxyId] / d->partialCounts.count();
if (max <= 0)
d->partialCounts[proxyId] = 1;
else
d->partialCounts[proxyId] = (double)count / (double) max;
d->progress += d->partialCounts[proxyId] / d->partialCounts.count();
emit progressChanged();
if (d->progress > 0.99)
emit dataAvailable();
}
qint64 QmlProfilerModelManager::estimatedProfilingTime() const
{
return d->estimatedTime;
}
void QmlProfilerModelManager::newTimeEstimation(qint64 estimation)
{
d->estimatedTime = estimation;
}
void QmlProfilerModelManager::addQmlEvent(int type,
int bindingType,
qint64 startTime,
qint64 length,
const QStringList &data,
const QmlDebug::QmlEventLocation &location,
qint64 ndata1,
qint64 ndata2,
qint64 ndata3,
qint64 ndata4,
qint64 ndata5)
{
// If trace start time was not explicitly set, use the first event
if (d->traceTime->startTime() == -1)
d->traceTime->setStartTime(startTime);
QTC_ASSERT(state() == QmlProfilerDataState::AcquiringData, /**/);
d->model->addQmlEvent(type, bindingType, startTime, length, data, location, ndata1, ndata2, ndata3, ndata4, ndata5);
emit countChanged();
}
void QmlProfilerModelManager::addV8Event(int depth, const QString &function, const QString &filename,
int lineNumber, double totalTime, double selfTime)
{
d->v8Model->addV8Event(depth, function, filename, lineNumber,totalTime, selfTime);
}
void QmlProfilerModelManager::complete()
{
if (state() == QmlProfilerDataState::AcquiringData) {
// If trace end time was not explicitly set, use the last event
if (d->traceTime->endTime() == 0)
d->traceTime->setEndTime(d->model->lastTimeMark());
setState(QmlProfilerDataState::ProcessingData);
d->model->complete();
d->v8Model->complete();
setState(QmlProfilerDataState::Done);
} else
if (state() == QmlProfilerDataState::Empty) {
setState(QmlProfilerDataState::Done);
} else
if (state() == QmlProfilerDataState::Done) {
// repeated Done states are ignored
} else {
emit error(tr("Unexpected complete signal in data model"));
}
}
void QmlProfilerModelManager::save(const QString &filename)
{
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
emit error(tr("Could not open %1 for writing.").arg(filename));
return;
}
QmlProfilerFileWriter writer;
writer.setTraceTime(traceTime()->startTime(), traceTime()->endTime(), traceTime()->duration());
writer.setV8DataModel(d->v8Model);
writer.setQmlEvents(d->model->getEvents());
writer.save(&file);
}
void QmlProfilerModelManager::load(const QString &filename)
{
d->fileName = filename;
load();
}
void QmlProfilerModelManager::setFilename(const QString &filename)
{
d->fileName = filename;
}
void QmlProfilerModelManager::load()
{
QString filename = d->fileName;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
emit error(tr("Could not open %1 for reading.").arg(filename));
return;
}
// erase current
clear();
setState(QmlProfilerDataState::AcquiringData);
QmlProfilerFileReader reader;
connect(&reader, SIGNAL(error(QString)), this, SIGNAL(error(QString)));
connect(&reader, SIGNAL(rangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
qint64, qint64, qint64, qint64, qint64)),
this, SLOT(addQmlEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation,
qint64, qint64, qint64, qint64, qint64)));
connect(&reader, SIGNAL(traceStartTime(qint64)), traceTime(), SLOT(setStartTime(qint64)));
connect(&reader, SIGNAL(traceEndTime(qint64)), traceTime(), SLOT(setEndTime(qint64)));
reader.setV8DataModel(d->v8Model);
reader.load(&file);
complete();
}
void QmlProfilerModelManager::setState(QmlProfilerDataState::State state)
{
d->dataState->setState(state);
}
QmlProfilerDataState::State QmlProfilerModelManager::state() const
{
return d->dataState->state();
}
void QmlProfilerModelManager::clear()
{
for (int i = 0; i < d->partialCounts.count(); i++)
d->partialCounts[i] = 0;
d->progress = 0;
d->model->clear();
d->v8Model->clear();
d->traceTime->clear();
emit countChanged();
setState(QmlProfilerDataState::Empty);
}
void QmlProfilerModelManager::prepareForWriting()
{
setState(QmlProfilerDataState::AcquiringData);
}
} // namespace QmlProfiler

View File

@@ -0,0 +1,159 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERMODELMANAGER_H
#define QMLPROFILERMODELMANAGER_H
#include <QObject>
#include "qmlprofiler_global.h"
#include "qmldebug/qmlprofilereventlocation.h"
#include <utils/fileinprojectfinder.h>
namespace QmlProfiler {
class QmlProfilerSimpleModel;
class QmlProfilerModelManager;
namespace Internal {
class QV8ProfilerDataModel;
class QmlProfilerDataState : public QObject
{
Q_OBJECT
public:
enum State {
Empty,
AcquiringData,
ProcessingData,
Done
};
explicit QmlProfilerDataState(QmlProfilerModelManager *modelManager, QObject *parent = 0);
~QmlProfilerDataState() {}
State state() const { return m_state; }
signals:
void stateChanged();
void error(const QString &error);
private:
void setState(State state);
State m_state;
QmlProfilerModelManager *m_modelManager;
friend class QmlProfiler::QmlProfilerModelManager;
};
class QmlProfilerTraceTime : public QObject
{
Q_OBJECT
public:
explicit QmlProfilerTraceTime(QObject *parent);
~QmlProfilerTraceTime();
qint64 startTime() const;
qint64 endTime() const;
qint64 duration() const;
public slots:
void clear();
void setStartTime(qint64 time);
void setEndTime(qint64 time);
private:
qint64 m_startTime;
qint64 m_endTime;
};
} // End internal namespace
using namespace Internal;
// Interface between the Data Model and the Engine/Tool
class QMLPROFILER_EXPORT QmlProfilerModelManager : public QObject
{
Q_OBJECT
public:
explicit QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent = 0);
~QmlProfilerModelManager();
QmlProfilerDataState::State state() const;
QmlProfilerTraceTime *traceTime() const;
QmlProfilerSimpleModel *simpleModel() const;
QV8ProfilerDataModel *v8Model() const;
bool isEmpty() const;
int count() const;
double progress() const;
int registerModelProxy();
void modelProxyCountUpdated(int proxyId, qint64 count, qint64 max);
qint64 estimatedProfilingTime() const;
signals:
void countChanged();
void error(const QString &error);
void stateChanged();
void progressChanged();
void dataAvailable();
void requestDetailsForLocation(int eventType, const QmlDebug::QmlEventLocation &location);
public slots:
void clear();
void prepareForWriting();
void addQmlEvent(int type, int bindingType, qint64 startTime, qint64 length,
const QStringList &data, const QmlDebug::QmlEventLocation &location,
qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5);
void addV8Event(int depth, const QString &function,const QString &filename, int lineNumber,
double totalTime, double selfTime);
void complete();
void save(const QString &filename);
void load(const QString &filename);
void setFilename(const QString &filename);
void load();
void newTimeEstimation(qint64 estimation);
private:
void setState(QmlProfilerDataState::State state);
private:
class QmlProfilerModelManagerPrivate;
QmlProfilerModelManagerPrivate *d;
};
}
#endif

View File

@@ -0,0 +1,458 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilerpainteventsmodelproxy.h"
#include "qmlprofilermodelmanager.h"
#include "qmlprofilersimplemodel.h"
#include <QCoreApplication>
#include <QVector>
#include <QHash>
#include <QUrl>
#include <QString>
#include <QStack>
#include <QDebug>
namespace QmlProfiler {
namespace Internal {
struct CategorySpan {
bool expanded;
int expandedRows;
int contractedRows;
};
class PaintEventsModelProxy::PaintEventsModelProxyPrivate
{
public:
PaintEventsModelProxyPrivate(PaintEventsModelProxy *qq) : q(qq) {}
~PaintEventsModelProxyPrivate() {}
QString displayTime(double time);
void computeAnimationCountLimit();
QVector <PaintEventsModelProxy::QmlPaintEventData> eventList;
int minAnimationCount;
int maxAnimationCount;
bool expanded;
PaintEventsModelProxy *q;
};
PaintEventsModelProxy::PaintEventsModelProxy(QObject *parent)
: AbstractTimelineModel(parent), d(new PaintEventsModelProxyPrivate(this))
{
}
PaintEventsModelProxy::~PaintEventsModelProxy()
{
delete d;
}
int PaintEventsModelProxy::categories() const
{
return categoryCount();
}
QStringList PaintEventsModelProxy::categoryTitles() const
{
QStringList retString;
for (int i=0; i<categories(); i++)
retString << categoryLabel(i);
return retString;
}
QString PaintEventsModelProxy::name() const
{
return QLatin1String("PaintEventsModelProxy");
}
const QVector<PaintEventsModelProxy::QmlPaintEventData> PaintEventsModelProxy::getData() const
{
return d->eventList;
}
const QVector<PaintEventsModelProxy::QmlPaintEventData> PaintEventsModelProxy::getData(qint64 fromTime, qint64 toTime) const
{
int fromIndex = findFirstIndex(fromTime);
int toIndex = findLastIndex(toTime);
if (fromIndex != -1 && toIndex > fromIndex)
return d->eventList.mid(fromIndex, toIndex - fromIndex + 1);
else
return QVector<PaintEventsModelProxy::QmlPaintEventData>();
}
void PaintEventsModelProxy::clear()
{
d->eventList.clear();
d->minAnimationCount = 1;
d->maxAnimationCount = 1;
d->expanded = false;
m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1);
}
void PaintEventsModelProxy::dataChanged()
{
if (m_modelManager->state() == QmlProfilerDataState::ProcessingData)
loadData();
if (m_modelManager->state() == QmlProfilerDataState::Empty)
clear();
emit stateChanged();
emit dataAvailable();
emit emptyChanged();
emit expandedChanged();
}
bool compareStartTimes(const PaintEventsModelProxy::QmlPaintEventData &t1, const PaintEventsModelProxy::QmlPaintEventData &t2)
{
return t1.startTime < t2.startTime;
}
bool PaintEventsModelProxy::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const
{
return (event.eventType == QmlDebug::Painting && event.bindingType == QmlDebug::AnimationFrame);
}
void PaintEventsModelProxy::loadData()
{
clear();
QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
if (simpleModel->isEmpty())
return;
// collect events
const QVector<QmlProfilerSimpleModel::QmlEventData> referenceList = simpleModel->getEvents();
foreach (const QmlProfilerSimpleModel::QmlEventData &event, referenceList) {
if (!eventAccepted(event))
continue;
qint64 estimatedDuration = 0;
// initial estimation of the event duration: 1/framerate
if (event.numericData1 > 0)
estimatedDuration = 1e9/event.numericData1;
// the profiler registers the animation events at the end of them
qint64 realStartTime = event.startTime - estimatedDuration;
// the duration of the events is estimated from the framerate
// we need to correct it before appending a new event
if (d->eventList.count() > 0) {
QmlPaintEventData *lastEvent = &d->eventList[d->eventList.count()-1];
if (lastEvent->startTime + lastEvent->duration >= realStartTime) {
// 1 nanosecond less to prevent overlap
lastEvent->duration = realStartTime - lastEvent->startTime - 1;
lastEvent->framerate = 1e9/lastEvent->duration;
}
}
QmlPaintEventData newEvent = {
realStartTime,
estimatedDuration,
(int)event.numericData1,
(int)event.numericData2
};
d->eventList.append(newEvent);
m_modelManager->modelProxyCountUpdated(m_modelId, d->eventList.count(), referenceList.count());
}
d->computeAnimationCountLimit();
qSort(d->eventList.begin(), d->eventList.end(), compareStartTimes);
m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1);
emit countChanged();
}
/////////////////// QML interface
bool PaintEventsModelProxy::isEmpty() const
{
return count() == 0;
}
int PaintEventsModelProxy::count() const
{
return d->eventList.count();
}
qint64 PaintEventsModelProxy::lastTimeMark() const
{
return d->eventList.last().startTime + d->eventList.last().duration;
}
bool PaintEventsModelProxy::expanded(int ) const
{
return d->expanded;
}
void PaintEventsModelProxy::setExpanded(int category, bool expanded)
{
Q_UNUSED(category);
d->expanded = expanded;
emit expandedChanged();
}
int PaintEventsModelProxy::categoryDepth(int categoryIndex) const
{
Q_UNUSED(categoryIndex);
if (isEmpty())
return 0;
else
return 2;
}
int PaintEventsModelProxy::categoryCount() const
{
return 1;
}
const QString PaintEventsModelProxy::categoryLabel(int categoryIndex) const
{
Q_UNUSED(categoryIndex);
return tr("Painting");
}
int PaintEventsModelProxy::findFirstIndex(qint64 startTime) const
{
return findFirstIndexNoParents(startTime);
}
int PaintEventsModelProxy::findFirstIndexNoParents(qint64 startTime) const
{
if (d->eventList.isEmpty())
return -1;
if (d->eventList.count() == 1 || d->eventList.first().startTime+d->eventList.first().duration >= startTime)
return 0;
else
if (d->eventList.last().startTime+d->eventList.last().duration <= startTime)
return -1;
int fromIndex = 0;
int toIndex = d->eventList.count()-1;
while (toIndex - fromIndex > 1) {
int midIndex = (fromIndex + toIndex)/2;
if (d->eventList[midIndex].startTime + d->eventList[midIndex].duration < startTime)
fromIndex = midIndex;
else
toIndex = midIndex;
}
return toIndex;
}
int PaintEventsModelProxy::findLastIndex(qint64 endTime) const
{
if (d->eventList.isEmpty())
return -1;
if (d->eventList.first().startTime >= endTime)
return -1;
if (d->eventList.count() == 1)
return 0;
if (d->eventList.last().startTime <= endTime)
return d->eventList.count()-1;
int fromIndex = 0;
int toIndex = d->eventList.count()-1;
while (toIndex - fromIndex > 1) {
int midIndex = (fromIndex + toIndex)/2;
if (d->eventList[midIndex].startTime < endTime)
fromIndex = midIndex;
else
toIndex = midIndex;
}
return fromIndex;
}
int PaintEventsModelProxy::getEventType(int index) const
{
Q_UNUSED(index);
return (int)QmlDebug::Painting;
}
int PaintEventsModelProxy::getEventCategory(int index) const
{
Q_UNUSED(index);
// there is only one category, all events belong to it
return 0;
}
int PaintEventsModelProxy::getEventRow(int index) const
{
Q_UNUSED(index);
return 1;
}
qint64 PaintEventsModelProxy::getDuration(int index) const
{
return d->eventList[index].duration;
}
qint64 PaintEventsModelProxy::getStartTime(int index) const
{
return d->eventList[index].startTime;
}
qint64 PaintEventsModelProxy::getEndTime(int index) const
{
return d->eventList[index].startTime + d->eventList[index].duration;
}
int PaintEventsModelProxy::getEventId(int index) const
{
// there is only one event Id for all painting events
Q_UNUSED(index);
return 0;
}
QColor PaintEventsModelProxy::getColor(int index) const
{
double fpsFraction = d->eventList[index].framerate / 60.0;
if (fpsFraction > 1.0)
fpsFraction = 1.0;
if (fpsFraction < 0.0)
fpsFraction = 0.0;
return QColor::fromHsl((fpsFraction*96)+10, 76, 166);
}
float PaintEventsModelProxy::getHeight(int index) const
{
float scale = d->maxAnimationCount - d->minAnimationCount;
float fraction = 1.0f;
if (scale > 1)
fraction = (float)(d->eventList[index].animationcount -
d->minAnimationCount) / scale;
return fraction * 0.85f + 0.15f;
}
const QVariantList PaintEventsModelProxy::getLabelsForCategory(int category) const
{
Q_UNUSED(category);
QVariantList result;
if (!isEmpty()) {
QVariantMap element;
element.insert(QLatin1String("displayName"), QVariant(QLatin1String("Animations")));
element.insert(QLatin1String("description"), QVariant(QLatin1String("Animations")));
element.insert(QLatin1String("id"), QVariant(0));
result << element;
}
return result;
}
QString PaintEventsModelProxy::PaintEventsModelProxyPrivate::displayTime(double time)
{
if (time < 1e6)
return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
if (time < 1e9)
return QString::number(time/1e6,'f',3) + tr(" ms");
return QString::number(time/1e9,'f',3) + tr(" s");
}
void PaintEventsModelProxy::PaintEventsModelProxyPrivate::computeAnimationCountLimit()
{
minAnimationCount = 1;
maxAnimationCount = 1;
if (eventList.isEmpty())
return;
for (int i=0; i < eventList.count(); i++) {
if (eventList[i].animationcount < minAnimationCount)
minAnimationCount = eventList[i].animationcount;
if (eventList[i].animationcount > maxAnimationCount)
maxAnimationCount = eventList[i].animationcount;
}
}
const QVariantList PaintEventsModelProxy::getEventDetails(int index) const
{
QVariantList result;
// int eventId = getEventId(index);
static const char trContext[] = "RangeDetails";
{
QVariantMap valuePair;
valuePair.insert(QLatin1String("title"), QVariant(categoryLabel(0)));
result << valuePair;
}
// duration
{
QVariantMap valuePair;
valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->eventList[index].duration)));
result << valuePair;
}
// duration
{
QVariantMap valuePair;
valuePair.insert(QCoreApplication::translate(trContext, "Framerate:"), QVariant(QString::fromLatin1("%1 FPS").arg(d->eventList[index].framerate)));
result << valuePair;
}
// duration
{
QVariantMap valuePair;
valuePair.insert(QCoreApplication::translate(trContext, "Animations:"), QVariant(QString::fromLatin1("%1").arg(d->eventList[index].animationcount)));
result << valuePair;
}
return result;
}
const QVariantMap PaintEventsModelProxy::getEventLocation(int /*index*/) const
{
QVariantMap map;
return map;
}
int PaintEventsModelProxy::getEventIdForHash(const QString &/*eventHash*/) const
{
// paint events do not have an eventHash
return -1;
}
int PaintEventsModelProxy::getEventIdForLocation(const QString &/*filename*/, int /*line*/, int /*column*/) const
{
// paint events do not have a defined location
return -1;
}
}
}

View File

@@ -0,0 +1,123 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERPAINTEVENTSMODELPROXY_H
#define QMLPROFILERPAINTEVENTSMODELPROXY_H
#include <QObject>
#include "abstracttimelinemodel.h"
#include <qmldebug/qmlprofilereventtypes.h>
#include <qmldebug/qmlprofilereventlocation.h>
//#include <QHash>
//#include <QVector>
#include <QVariantList>
//#include <QVariantMap>
#include "qmlprofilersimplemodel.h"
#include <QColor>
namespace QmlProfiler {
class QmlProfilerModelManager;
namespace Internal {
class PaintEventsModelProxy : public AbstractTimelineModel
{
// Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
Q_OBJECT
public:
struct QmlPaintEventData {
qint64 startTime;
qint64 duration;
int framerate;
int animationcount;
};
PaintEventsModelProxy(QObject *parent = 0);
~PaintEventsModelProxy();
int categories() const;
QStringList categoryTitles() const;
QString name() const;
const QVector<QmlPaintEventData> getData() const;
const QVector<QmlPaintEventData> getData(qint64 fromTime, qint64 toTime) const;
void loadData();
Q_INVOKABLE int count() const;
void clear();
bool isEmpty() const;
Q_INVOKABLE qint64 lastTimeMark() const;
Q_INVOKABLE bool expanded(int category) const;
Q_INVOKABLE void setExpanded(int category, bool expanded);
Q_INVOKABLE int categoryDepth(int categoryIndex) const;
Q_INVOKABLE int categoryCount() const;
Q_INVOKABLE const QString categoryLabel(int categoryIndex) const;
int findFirstIndex(qint64 startTime) const;
int findFirstIndexNoParents(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
int getEventType(int index) const;
Q_INVOKABLE int getEventCategory(int index) const;
int getEventRow(int index) const;
Q_INVOKABLE qint64 getDuration(int index) const;
Q_INVOKABLE qint64 getStartTime(int index) const;
Q_INVOKABLE qint64 getEndTime(int index) const;
Q_INVOKABLE int getEventId(int index) const;
Q_INVOKABLE QColor getColor(int index) const;
Q_INVOKABLE float getHeight(int index) const;
Q_INVOKABLE const QVariantList getLabelsForCategory(int category) const;
Q_INVOKABLE const QVariantList getEventDetails(int index) const;
Q_INVOKABLE const QVariantMap getEventLocation(int index) const;
Q_INVOKABLE int getEventIdForHash(const QString &eventHash) const;
Q_INVOKABLE int getEventIdForLocation(const QString &filename, int line, int column) const;
private slots:
bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const;
protected slots:
void dataChanged();
private:
class PaintEventsModelProxyPrivate;
PaintEventsModelProxyPrivate *d;
};
}
}
#endif

View File

@@ -31,8 +31,10 @@
#include "qmlprofilerruncontrolfactory.h" #include "qmlprofilerruncontrolfactory.h"
#include "qmlprofilertool.h" #include "qmlprofilertool.h"
#include "abstracttimelinemodel.h"
#include <analyzerbase/analyzermanager.h> #include <analyzerbase/analyzermanager.h>
#include <extensionsystem/pluginmanager.h>
#include <QtPlugin> #include <QtPlugin>
@@ -48,6 +50,7 @@ public:
}; };
bool QmlProfilerPlugin::debugOutput = false; bool QmlProfilerPlugin::debugOutput = false;
QmlProfilerPlugin *QmlProfilerPlugin::instance = 0;
bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorString) bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorString)
{ {
@@ -81,15 +84,14 @@ bool QmlProfilerPlugin::initialize(const QStringList &arguments, QString *errorS
AnalyzerManager::addAction(action); AnalyzerManager::addAction(action);
addAutoReleasedObject(new QmlProfilerRunControlFactory()); addAutoReleasedObject(new QmlProfilerRunControlFactory());
QmlProfilerPlugin::instance = this;
return true; return true;
} }
void QmlProfilerPlugin::extensionsInitialized() void QmlProfilerPlugin::extensionsInitialized()
{ {
// Retrieve objects from the plugin manager's object pool. timelineModels = ExtensionSystem::PluginManager::getObjects<AbstractTimelineModel>();
// "In the extensionsInitialized method, a plugin can be sure that all
// plugins that depend on it are completely initialized."
} }
ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown() ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown()
@@ -100,8 +102,12 @@ ExtensionSystem::IPlugin::ShutdownFlag QmlProfilerPlugin::aboutToShutdown()
return SynchronousShutdown; return SynchronousShutdown;
} }
QList<AbstractTimelineModel *> QmlProfilerPlugin::getModels() const
{
return timelineModels;
}
} // namespace Internal } // namespace Internal
} // namespace QmlProfiler } // namespace QmlProfiler
Q_EXPORT_PLUGIN(QmlProfiler::Internal::QmlProfilerPlugin) Q_EXPORT_PLUGIN(QmlProfiler::Internal::QmlProfilerPlugin)

View File

@@ -34,6 +34,8 @@
#include <extensionsystem/iplugin.h> #include <extensionsystem/iplugin.h>
#include "abstracttimelinemodel.h"
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
@@ -50,6 +52,14 @@ public:
ShutdownFlag aboutToShutdown(); ShutdownFlag aboutToShutdown();
static bool debugOutput; static bool debugOutput;
static QmlProfilerPlugin *instance;
QList<AbstractTimelineModel *> getModels() const;
private:
QList<AbstractTimelineModel*> timelineModels;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -0,0 +1,182 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilerprocessedmodel.h"
#include <qmldebug/qmlprofilereventtypes.h>
#include <utils/qtcassert.h>
#include <QUrl>
#include <QDebug>
namespace QmlProfiler {
namespace Internal {
QmlDebug::QmlEventLocation getLocation(const QmlProfilerSimpleModel::QmlEventData &event);
QString getDisplayName(const QmlProfilerSimpleModel::QmlEventData &event);
QString getInitialDetails(const QmlProfilerSimpleModel::QmlEventData &event);
QmlDebug::QmlEventLocation getLocation(const QmlProfilerSimpleModel::QmlEventData &event)
{
QmlDebug::QmlEventLocation eventLocation = event.location;
if ((event.eventType == QmlDebug::Creating || event.eventType == QmlDebug::Compiling)
&& eventLocation.filename.isEmpty()) {
eventLocation.filename = getInitialDetails(event);
eventLocation.line = 1;
eventLocation.column = 1;
}
return eventLocation;
}
QString getDisplayName(const QmlProfilerSimpleModel::QmlEventData &event)
{
const QmlDebug::QmlEventLocation eventLocation = getLocation(event);
QString displayName;
// generate hash
if (eventLocation.filename.isEmpty()) {
displayName = QmlProfilerProcessedModel::tr("<bytecode>");
} else {
const QString filePath = QUrl(eventLocation.filename).path();
displayName = filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
QString::number(eventLocation.line);
}
return displayName;
}
QString getInitialDetails(const QmlProfilerSimpleModel::QmlEventData &event)
{
QString details;
// generate details string
if (event.data.isEmpty())
details = QmlProfilerProcessedModel::tr("Source code not available.");
else {
details = event.data.join(QLatin1String(" ")).replace(QLatin1Char('\n'),QLatin1Char(' ')).simplified();
QRegExp rewrite(QLatin1String("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
bool match = rewrite.exactMatch(details);
if (match)
details = rewrite.cap(1) + QLatin1String(": ") + rewrite.cap(3);
if (details.startsWith(QLatin1String("file://")))
details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
}
return details;
}
bool compareStartTimes(const QmlProfilerSimpleModel::QmlEventData &t1, const QmlProfilerSimpleModel::QmlEventData &t2)
{
return t1.startTime < t2.startTime;
}
//////////////////////////////////////////////////////////////////////////////
QmlProfilerProcessedModel::QmlProfilerProcessedModel(Utils::FileInProjectFinder *fileFinder, QObject *parent)
: QmlProfilerSimpleModel(parent)
, m_detailsRewriter(new QmlProfilerDetailsRewriter(this, fileFinder))
, m_emitChanged(false)
{
connect(m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QString)),
this, SLOT(detailsChanged(int,QString)));
connect(m_detailsRewriter, SIGNAL(eventDetailsChanged()),
this, SLOT(detailsDone()));
}
QmlProfilerProcessedModel::~QmlProfilerProcessedModel()
{
}
void QmlProfilerProcessedModel::clear()
{
m_detailsRewriter->clearRequests();
QmlProfilerSimpleModel::clear();
emit changed();
m_emitChanged = false;
}
void QmlProfilerProcessedModel::complete()
{
// post-processing
// sort events by start time
qSort(eventList.begin(), eventList.end(), compareStartTimes);
// rewrite strings
int n = eventList.count();
for (int i = 0; i < n; i++) {
QmlEventData *event = &eventList[i];
event->location = getLocation(*event);
event->displayName = getDisplayName(*event);
event->data = QStringList() << getInitialDetails(*event);
//
// request further details from files
//
if (event->eventType != QmlDebug::Binding && event->eventType != QmlDebug::HandlingSignal)
continue;
// This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
if (event->location.filename.isEmpty())
continue;
// Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
if (event->location.column == -1)
continue;
m_detailsRewriter->requestDetailsForLocation(i, event->location);
}
m_detailsRewriter->reloadDocuments();
QmlProfilerSimpleModel::complete();
emit changed();
m_emitChanged = false;
}
void QmlProfilerProcessedModel::detailsChanged(int requestId, const QString &newString)
{
QTC_ASSERT(requestId < eventList.count(), return);
QmlEventData *event = &eventList[requestId];
event->data = QStringList(newString);
m_emitChanged = true;
}
void QmlProfilerProcessedModel::detailsDone()
{
if (m_emitChanged) {
emit changed();
m_emitChanged = false;
}
}
}
}

View File

@@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERPROCESSEDMODEL_H
#define QMLPROFILERPROCESSEDMODEL_H
#include "qmlprofilersimplemodel.h"
#include "qmlprofilerdetailsrewriter.h"
namespace QmlProfiler {
namespace Internal {
class QmlProfilerProcessedModel : public QmlProfilerSimpleModel
{
Q_OBJECT
public:
explicit QmlProfilerProcessedModel(Utils::FileInProjectFinder *fileFinder, QObject *parent = 0);
~QmlProfilerProcessedModel();
virtual void clear();
virtual void complete();
private slots:
void detailsChanged(int requestId, const QString &newString);
void detailsDone();
private:
QmlProfilerDetailsRewriter *m_detailsRewriter;
bool m_emitChanged;
};
}
}
#endif

View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilersimplemodel.h"
#include "qmlprofilermodelmanager.h"
#include <QStringList>
#include <QVector>
#include <QDebug>
#include "qmldebug/qmlprofilereventtypes.h"
namespace QmlProfiler {
QmlProfilerSimpleModel::QmlProfilerSimpleModel(QObject *parent)
: QObject(parent)
{
m_modelManager = qobject_cast<QmlProfilerModelManager *>(parent);
Q_ASSERT(m_modelManager);
m_modelId = m_modelManager->registerModelProxy();
}
QmlProfilerSimpleModel::~QmlProfilerSimpleModel()
{
}
void QmlProfilerSimpleModel::clear()
{
m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1);
eventList.clear();
emit changed();
}
bool QmlProfilerSimpleModel::isEmpty() const
{
return eventList.isEmpty();
}
const QVector<QmlProfilerSimpleModel::QmlEventData> &QmlProfilerSimpleModel::getEvents() const
{
return eventList;
}
int QmlProfilerSimpleModel::count() const
{
return eventList.count();
}
void QmlProfilerSimpleModel::addQmlEvent(int type, int bindingType, qint64 startTime, qint64 duration, const QStringList &data, const QmlDebug::QmlEventLocation &location, qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5)
{
QString displayName;
if (type == QmlDebug::Painting && bindingType == QmlDebug::AnimationFrame) {
displayName = tr("Animations");
} else {
displayName = QString::fromLatin1("%1:%2").arg(
location.filename,
QString::number(location.line));
}
QmlEventData eventData = {displayName, type, bindingType, startTime, duration, data, location, ndata1, ndata2, ndata3, ndata4, ndata5};
eventList.append(eventData);
m_modelManager->modelProxyCountUpdated(m_modelId, startTime, m_modelManager->estimatedProfilingTime());
}
qint64 QmlProfilerSimpleModel::lastTimeMark() const
{
if (eventList.isEmpty())
return 0;
return eventList.last().startTime + eventList.last().duration;
}
void QmlProfilerSimpleModel::complete()
{
m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1);
emit changed();
}
QString QmlProfilerSimpleModel::getHashString(const QmlProfilerSimpleModel::QmlEventData &event)
{
return QString::fromLatin1("%1:%2:%3:%4:%5").arg(
event.location.filename,
QString::number(event.location.line),
QString::number(event.location.column),
QString::number(event.eventType),
QString::number(event.bindingType));
}
}

View File

@@ -0,0 +1,88 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERSIMPLEMODEL_H
#define QMLPROFILERSIMPLEMODEL_H
#include "qmlprofiler_global.h"
#include <QObject>
#include <QVector>
#include <QStringList>
#include "qmldebug/qmlprofilereventlocation.h"
namespace QmlProfiler {
class QmlProfilerModelManager;
// stores the data from the client as-is
class QMLPROFILER_EXPORT QmlProfilerSimpleModel : public QObject
{
Q_OBJECT
public:
struct QmlEventData {
QString displayName;
int eventType;
int bindingType;
qint64 startTime;
qint64 duration;
QStringList data;
QmlDebug::QmlEventLocation location;
qint64 numericData1;
qint64 numericData2;
qint64 numericData3;
qint64 numericData4;
qint64 numericData5;
};
explicit QmlProfilerSimpleModel(QObject *parent = 0);
~QmlProfilerSimpleModel();
virtual void clear();
bool isEmpty() const;
const QVector<QmlEventData> &getEvents() const;
int count() const;
void addQmlEvent(int type, int bindingType, qint64 startTime, qint64 duration, const QStringList &data, const QmlDebug::QmlEventLocation &location,
qint64 ndata1, qint64 ndata2, qint64 ndata3, qint64 ndata4, qint64 ndata5);
qint64 lastTimeMark() const;
virtual void complete();
static QString getHashString(const QmlProfilerSimpleModel::QmlEventData &event);
signals:
void changed();
protected:
QVector<QmlEventData> eventList;
QmlProfilerModelManager *m_modelManager;
int m_modelId;
};
}
#endif

View File

@@ -38,8 +38,7 @@
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
static QString stringForState(int state) inline QString stringForState(int state) {
{
switch (state) { switch (state) {
case QmlProfilerStateManager::Idle: return QLatin1String("Idle"); case QmlProfilerStateManager::Idle: return QLatin1String("Idle");
case QmlProfilerStateManager::AppStarting: return QLatin1String("AppStarting"); case QmlProfilerStateManager::AppStarting: return QLatin1String("AppStarting");
@@ -54,79 +53,92 @@ static QString stringForState(int state)
return QString(); return QString();
} }
QmlProfilerStateManager::QmlProfilerStateManager(QObject *parent) : class QmlProfilerStateManager::QmlProfilerStateManagerPrivate
QObject(parent)
{ {
m_currentState = Idle; public:
m_clientRecording = true; QmlProfilerStateManagerPrivate(QmlProfilerStateManager *qq) : q(qq) {}
m_serverRecording = false; ~QmlProfilerStateManagerPrivate() {}
QmlProfilerStateManager *q;
QmlProfilerStateManager::QmlProfilerState m_currentState;
bool m_clientRecording;
bool m_serverRecording;
};
QmlProfilerStateManager::QmlProfilerStateManager(QObject *parent) :
QObject(parent),d(new QmlProfilerStateManagerPrivate(this))
{
d->m_currentState = Idle;
d->m_clientRecording = true;
d->m_serverRecording = false;
} }
QmlProfilerStateManager::~QmlProfilerStateManager() QmlProfilerStateManager::~QmlProfilerStateManager()
{ {
delete d;
} }
QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState() const QmlProfilerStateManager::QmlProfilerState QmlProfilerStateManager::currentState()
{ {
return m_currentState; return d->m_currentState;
} }
bool QmlProfilerStateManager::clientRecording() const bool QmlProfilerStateManager::clientRecording()
{ {
return m_clientRecording; return d->m_clientRecording;
} }
bool QmlProfilerStateManager::serverRecording() const bool QmlProfilerStateManager::serverRecording()
{ {
return m_serverRecording; return d->m_serverRecording;
} }
QString QmlProfilerStateManager::currentStateAsString() const QString QmlProfilerStateManager::currentStateAsString()
{ {
return stringForState(m_currentState); return stringForState(d->m_currentState);
} }
void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState) void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
{ {
#ifdef _DEBUG_PROFILERSTATE_ #ifdef _DEBUG_PROFILERSTATE_
qDebug() << "Profiler state change request from" << currentStateAsString() << "to" << stringForState(newState); qDebug() << "Profiler state change request from" << stringForState(d->m_currentState) << "to" << stringForState(newState);
#endif #endif
QTC_ASSERT(m_currentState != newState, /**/); QTC_ASSERT(d->m_currentState != newState, /**/);
switch (newState) { switch (newState) {
case Idle: case Idle:
QTC_ASSERT(m_currentState == AppStarting || QTC_ASSERT(d->m_currentState == AppStarting ||
m_currentState == AppStopped || d->m_currentState == AppStopped ||
m_currentState == AppKilled, d->m_currentState == AppKilled,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppStarting: case AppStarting:
QTC_ASSERT(m_currentState == Idle, QTC_ASSERT(d->m_currentState == Idle,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppRunning: case AppRunning:
QTC_ASSERT(m_currentState == AppStarting, QTC_ASSERT(d->m_currentState == AppStarting,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppStopRequested: case AppStopRequested:
QTC_ASSERT(m_currentState == AppRunning, QTC_ASSERT(d->m_currentState == AppRunning,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppReadyToStop: case AppReadyToStop:
QTC_ASSERT(m_currentState == AppStopRequested, QTC_ASSERT(d->m_currentState == AppStopRequested,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppStopped: case AppStopped:
QTC_ASSERT(m_currentState == AppReadyToStop || QTC_ASSERT(d->m_currentState == AppReadyToStop ||
m_currentState == AppDying, d->m_currentState == AppDying,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppDying: case AppDying:
QTC_ASSERT(m_currentState == AppRunning, QTC_ASSERT(d->m_currentState == AppRunning,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
case AppKilled: case AppKilled:
QTC_ASSERT(m_currentState == AppDying, QTC_ASSERT(d->m_currentState == AppDying,
qDebug() << "from" << currentStateAsString()); qDebug() << "from" << stringForState(d->m_currentState));
break; break;
default: { default: {
const QString message = QString::fromLatin1("Switching to unknown state in %1:%2").arg(QString::fromLatin1(__FILE__), QString::number(__LINE__)); const QString message = QString::fromLatin1("Switching to unknown state in %1:%2").arg(QString::fromLatin1(__FILE__), QString::number(__LINE__));
@@ -135,17 +147,17 @@ void QmlProfilerStateManager::setCurrentState(QmlProfilerState newState)
break; break;
} }
m_currentState = newState; d->m_currentState = newState;
emit stateChanged(); emit stateChanged();
} }
void QmlProfilerStateManager::setClientRecording(bool recording) void QmlProfilerStateManager::setClientRecording(bool recording)
{ {
#ifdef _DEBUG_PROFILERSTATE_ #ifdef _DEBUG_PROFILERSTATE_
qDebug() << "Setting client recording flag from" << m_serverRecording << "to" << recording; qDebug() << "Setting client recording flag from" << d->m_serverRecording << "to" << recording;
#endif #endif
if (m_clientRecording != recording) { if (d->m_clientRecording != recording) {
m_clientRecording = recording; d->m_clientRecording = recording;
emit clientRecordingChanged(); emit clientRecordingChanged();
} }
} }
@@ -153,13 +165,13 @@ void QmlProfilerStateManager::setClientRecording(bool recording)
void QmlProfilerStateManager::setServerRecording(bool recording) void QmlProfilerStateManager::setServerRecording(bool recording)
{ {
#ifdef _DEBUG_PROFILERSTATE_ #ifdef _DEBUG_PROFILERSTATE_
qDebug() << "Setting server recording flag from" << m_serverRecording << "to" << recording; qDebug() << "Setting server recording flag from" << d->m_serverRecording << "to" << recording;
#endif #endif
if (m_serverRecording != recording) { if (d->m_serverRecording != recording) {
m_serverRecording = recording; d->m_serverRecording = recording;
emit serverRecordingChanged(); emit serverRecordingChanged();
} }
} }
} // namespace Internal }
} // namespace QmlProfiler }

View File

@@ -38,7 +38,6 @@ namespace Internal {
class QmlProfilerStateManager : public QObject class QmlProfilerStateManager : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
enum QmlProfilerState { enum QmlProfilerState {
Idle, Idle,
@@ -54,11 +53,11 @@ public:
explicit QmlProfilerStateManager(QObject *parent = 0); explicit QmlProfilerStateManager(QObject *parent = 0);
~QmlProfilerStateManager(); ~QmlProfilerStateManager();
QmlProfilerState currentState() const; QmlProfilerState currentState();
bool clientRecording() const; bool clientRecording();
bool serverRecording() const; bool serverRecording();
QString currentStateAsString() const; QString currentStateAsString();
signals: signals:
void stateChanged(); void stateChanged();
@@ -71,12 +70,11 @@ public slots:
void setServerRecording(bool recording); void setServerRecording(bool recording);
private: private:
QmlProfilerStateManager::QmlProfilerState m_currentState; class QmlProfilerStateManagerPrivate;
bool m_clientRecording; QmlProfilerStateManagerPrivate *d;
bool m_serverRecording;
}; };
} // namespace Internal }
} // namespace QmlProfiler }
#endif // QMLPROFILERSTATEMANAGER_H #endif // QMLPROFILERSTATEMANAGER_H

View File

@@ -35,6 +35,7 @@
#include <QLabel> #include <QLabel>
#include <QProgressBar> #include <QProgressBar>
#include <QTime> #include <QTime>
#include <QDebug>
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
@@ -49,7 +50,7 @@ class QmlProfilerStateWidget::QmlProfilerStateWidgetPrivate
QPixmap shadowPic; QPixmap shadowPic;
QmlProfilerStateManager *m_profilerState; QmlProfilerStateManager *m_profilerState;
QmlProfilerDataModel *m_profilerDataModel; QmlProfilerModelManager *m_modelManager;
bool isRecording; bool isRecording;
bool appKilled; bool appKilled;
@@ -60,11 +61,9 @@ class QmlProfilerStateWidget::QmlProfilerStateWidgetPrivate
qint64 estimatedProfilingTime; qint64 estimatedProfilingTime;
}; };
QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateManager, QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateManager,
QmlProfilerDataModel *dataModel, QWidget *parent) : QmlProfilerModelManager *modelManager, QWidget *parent)
QWidget(parent), d(new QmlProfilerStateWidgetPrivate(this)) : QWidget(parent), d(new QmlProfilerStateWidgetPrivate(this))
{ {
setObjectName(QLatin1String("QML Profiler State Display")); setObjectName(QLatin1String("QML Profiler State Display"));
@@ -80,6 +79,7 @@ QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateMan
d->progressBar = new QProgressBar(this); d->progressBar = new QProgressBar(this);
layout->addWidget(d->progressBar); layout->addWidget(d->progressBar);
d->progressBar->setMaximum(1000);
d->progressBar->setVisible(false); d->progressBar->setVisible(false);
setLayout(layout); setLayout(layout);
@@ -92,9 +92,11 @@ QmlProfilerStateWidget::QmlProfilerStateWidget(QmlProfilerStateManager *stateMan
d->emptyList = true; d->emptyList = true;
// profiler state // profiler state
d->m_profilerDataModel = dataModel; d->m_modelManager = modelManager;
connect(d->m_profilerDataModel,SIGNAL(stateChanged()), this, SLOT(dataStateChanged())); connect(d->m_modelManager,SIGNAL(stateChanged()), this, SLOT(dataStateChanged()));
connect(d->m_profilerDataModel,SIGNAL(countChanged()), this, SLOT(dataStateChanged())); connect(d->m_modelManager,SIGNAL(countChanged()), this, SLOT(dataStateChanged()));
connect(d->m_modelManager,SIGNAL(progressChanged()), this, SLOT(dataStateChanged()));
connect(this, SIGNAL(newTimeEstimation(qint64)), d->m_modelManager, SLOT(newTimeEstimation(qint64)));
d->m_profilerState = stateManager; d->m_profilerState = stateManager;
connect(d->m_profilerState,SIGNAL(stateChanged()), this, SLOT(profilerStateChanged())); connect(d->m_profilerState,SIGNAL(stateChanged()), this, SLOT(profilerStateChanged()));
connect(d->m_profilerState, SIGNAL(serverRecordingChanged()), connect(d->m_profilerState, SIGNAL(serverRecordingChanged()),
@@ -196,9 +198,9 @@ void QmlProfilerStateWidget::updateDisplay()
if (d->isRecording) { if (d->isRecording) {
d->isRecording = false; d->isRecording = false;
d->estimatedProfilingTime = d->profilingTimer.elapsed(); d->estimatedProfilingTime = d->profilingTimer.elapsed();
emit newTimeEstimation(d->estimatedProfilingTime);
} }
d->progressBar->setMaximum(d->estimatedProfilingTime); d->progressBar->setValue(d->m_modelManager->progress() * 1000);
d->progressBar->setValue(d->m_profilerDataModel->lastTimeMark() * 1e-6);
d->progressBar->setVisible(true); d->progressBar->setVisible(true);
resize(300,70); resize(300,70);
reposition(); reposition();
@@ -232,9 +234,9 @@ void QmlProfilerStateWidget::updateDisplay()
if (d->isRecording) { if (d->isRecording) {
d->isRecording = false; d->isRecording = false;
d->estimatedProfilingTime = d->profilingTimer.elapsed(); d->estimatedProfilingTime = d->profilingTimer.elapsed();
emit newTimeEstimation(d->estimatedProfilingTime);
} }
d->progressBar->setMaximum(d->estimatedProfilingTime); d->progressBar->setValue(d->m_modelManager->progress() * 1000);
d->progressBar->setValue(d->m_profilerDataModel->lastTimeMark() * 1e-6);
d->progressBar->setVisible(true); d->progressBar->setVisible(true);
resize(300,70); resize(300,70);
reposition(); reposition();
@@ -252,15 +254,17 @@ void QmlProfilerStateWidget::updateDisplay()
// } // }
// There is a trace on view, hide this dialog // There is a trace on view, hide this dialog
d->progressBar->setVisible(false);
setVisible(false); setVisible(false);
} }
void QmlProfilerStateWidget::dataStateChanged() void QmlProfilerStateWidget::dataStateChanged()
{ {
d->loadingDone = d->m_profilerDataModel->currentState() == QmlProfilerDataModel::Done || // consider possible rounding errors
d->m_profilerDataModel->currentState() == QmlProfilerDataModel::Empty; d->loadingDone = d->m_modelManager->progress() >= 0.99 ||
d->traceAvailable = d->m_profilerDataModel->traceDuration() > 0; d->m_modelManager->state() == QmlProfilerDataState::Empty;
d->emptyList = d->m_profilerDataModel->count() == 0; d->traceAvailable = d->m_modelManager->traceTime()->duration() > 0;
d->emptyList = d->m_modelManager->isEmpty() || d->m_modelManager->progress() == 0;
updateDisplay(); updateDisplay();
} }
@@ -275,8 +279,11 @@ void QmlProfilerStateWidget::profilerStateChanged()
d->isRecording = d->m_profilerState->serverRecording(); d->isRecording = d->m_profilerState->serverRecording();
if (d->isRecording) if (d->isRecording)
d->profilingTimer.start(); d->profilingTimer.start();
else else {
d->estimatedProfilingTime = d->profilingTimer.elapsed(); // estimated time in ns
d->estimatedProfilingTime = d->profilingTimer.elapsed() * 1e6;
emit newTimeEstimation(d->estimatedProfilingTime);
}
updateDisplay(); updateDisplay();
} }

View File

@@ -33,7 +33,7 @@
#include <QWidget> #include <QWidget>
#include "qmlprofilerstatemanager.h" #include "qmlprofilerstatemanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilermodelmanager.h"
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
@@ -43,7 +43,7 @@ class QmlProfilerStateWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit QmlProfilerStateWidget(QmlProfilerStateManager *stateManager, explicit QmlProfilerStateWidget(QmlProfilerStateManager *stateManager,
QmlProfilerDataModel *dataModel, QWidget *parent = 0); QmlProfilerModelManager *modelManager, QWidget *parent = 0);
~QmlProfilerStateWidget(); ~QmlProfilerStateWidget();
private slots: private slots:
@@ -52,6 +52,9 @@ private slots:
void profilerStateChanged(); void profilerStateChanged();
void reposition(); void reposition();
signals:
void newTimeEstimation(qint64);
protected: protected:
void paintEvent(QPaintEvent *event); void paintEvent(QPaintEvent *event);

View File

@@ -0,0 +1,721 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilertimelinemodelproxy.h"
#include "qmlprofilermodelmanager.h"
#include "qmlprofilersimplemodel.h"
#include <QCoreApplication>
#include <QVector>
#include <QHash>
#include <QUrl>
#include <QString>
#include <QStack>
#include <QDebug>
namespace QmlProfiler {
namespace Internal {
struct CategorySpan {
bool expanded;
int expandedRows;
int contractedRows;
int rowStart;
bool empty;
};
class BasicTimelineModel::BasicTimelineModelPrivate
{
public:
BasicTimelineModelPrivate(BasicTimelineModel *qq) : q(qq) {}
~BasicTimelineModelPrivate() {}
// convenience functions
void prepare();
void computeNestingContracted();
void computeExpandedLevels();
void buildEndTimeList();
void findBindingLoops();
void computeRowStarts();
QString displayTime(double time);
QVector <BasicTimelineModel::QmlRangeEventData> eventDict;
QVector <QString> eventHashes;
QVector <BasicTimelineModel::QmlRangeEventStartInstance> startTimeData;
QVector <BasicTimelineModel::QmlRangeEventEndInstance> endTimeData;
QVector <CategorySpan> categorySpan;
BasicTimelineModel *q;
};
BasicTimelineModel::BasicTimelineModel(QObject *parent)
: AbstractTimelineModel(parent), d(new BasicTimelineModelPrivate(this))
{
}
BasicTimelineModel::~BasicTimelineModel()
{
delete d;
}
int BasicTimelineModel::categories() const
{
return categoryCount();
}
QStringList BasicTimelineModel::categoryTitles() const
{
QStringList retString;
for (int i=0; i<categories(); i++)
retString << categoryLabel(i);
return retString;
}
QString BasicTimelineModel::name() const
{
return QLatin1String("BasicTimelineModel");
}
const QVector<BasicTimelineModel::QmlRangeEventStartInstance> BasicTimelineModel::getData() const
{
return d->startTimeData;
}
const QVector<BasicTimelineModel::QmlRangeEventStartInstance> BasicTimelineModel::getData(qint64 fromTime, qint64 toTime) const
{
int fromIndex = findFirstIndex(fromTime);
int toIndex = findLastIndex(toTime);
if (fromIndex != -1 && toIndex > fromIndex)
return d->startTimeData.mid(fromIndex, toIndex - fromIndex + 1);
else
return QVector<BasicTimelineModel::QmlRangeEventStartInstance>();
}
void BasicTimelineModel::clear()
{
d->eventDict.clear();
d->eventHashes.clear();
d->startTimeData.clear();
d->endTimeData.clear();
d->categorySpan.clear();
m_modelManager->modelProxyCountUpdated(m_modelId, 0, 1);
}
void BasicTimelineModel::dataChanged()
{
if (m_modelManager->state() == QmlProfilerDataState::ProcessingData)
loadData();
if (m_modelManager->state() == QmlProfilerDataState::Empty)
clear();
emit stateChanged();
emit dataAvailable();
emit emptyChanged();
emit expandedChanged();
}
void BasicTimelineModel::BasicTimelineModelPrivate::prepare()
{
categorySpan.clear();
for (int i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
CategorySpan newCategory = {false, 1, 1, i, true};
categorySpan << newCategory;
}
}
bool compareStartTimes(const BasicTimelineModel::QmlRangeEventStartInstance&t1, const BasicTimelineModel::QmlRangeEventStartInstance &t2)
{
return t1.startTime < t2.startTime;
}
bool compareEndTimes(const BasicTimelineModel::QmlRangeEventEndInstance &t1, const BasicTimelineModel::QmlRangeEventEndInstance &t2)
{
return t1.endTime < t2.endTime;
}
bool BasicTimelineModel::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const
{
// only accept Qt4.x Painting events
if (event.eventType == QmlDebug::Painting)
return event.bindingType == QmlDebug::QPainterEvent;
return (event.eventType <= QmlDebug::HandlingSignal);
}
void BasicTimelineModel::loadData()
{
clear();
QmlProfilerSimpleModel *simpleModel = m_modelManager->simpleModel();
if (simpleModel->isEmpty())
return;
int lastEventId = 0;
d->prepare();
// collect events
const QVector<QmlProfilerSimpleModel::QmlEventData> eventList = simpleModel->getEvents();
foreach (const QmlProfilerSimpleModel::QmlEventData &event, eventList) {
if (!eventAccepted(event))
continue;
QString eventHash = QmlProfilerSimpleModel::getHashString(event);
// store in dictionary
if (!d->eventHashes.contains(eventHash)) {
QmlRangeEventData rangeEventData = {
event.displayName,
event.data.join(QLatin1String(" ")),
event.location,
(QmlDebug::QmlEventType)event.eventType,
lastEventId++ // event id
};
d->eventDict << rangeEventData;
d->eventHashes << eventHash;
}
// store starttime-based instance
QmlRangeEventStartInstance eventStartInstance = {
event.startTime,
event.duration,
d->eventHashes.indexOf(eventHash), // event id
QmlDebug::Constants::QML_MIN_LEVEL, // displayRowExpanded;
QmlDebug::Constants::QML_MIN_LEVEL, // displayRowCollapsed;
1,
-1 // bindingLoopHead
};
d->startTimeData.append(eventStartInstance);
m_modelManager->modelProxyCountUpdated(m_modelId, d->startTimeData.count(), eventList.count() * 7);
}
qSort(d->startTimeData.begin(), d->startTimeData.end(), compareStartTimes);
m_modelManager->modelProxyCountUpdated(m_modelId, 2, 7);
// compute nestingLevel - nonexpanded
d->computeNestingContracted();
m_modelManager->modelProxyCountUpdated(m_modelId, 3, 7);
// compute nestingLevel - expanded
d->computeExpandedLevels();
m_modelManager->modelProxyCountUpdated(m_modelId, 4, 7);
// populate endtimelist
d->buildEndTimeList();
m_modelManager->modelProxyCountUpdated(m_modelId, 5, 7);
d->findBindingLoops();
m_modelManager->modelProxyCountUpdated(m_modelId, 6, 7);
d->computeRowStarts();
m_modelManager->modelProxyCountUpdated(m_modelId, 1, 1);
emit countChanged();
}
void BasicTimelineModel::BasicTimelineModelPrivate::computeNestingContracted()
{
int i;
int eventCount = startTimeData.count();
QHash<int, qint64> endtimesPerLevel;
QList<int> nestingLevels;
QList< QHash<int, qint64> > endtimesPerNestingLevel;
int level = QmlDebug::Constants::QML_MIN_LEVEL;
endtimesPerLevel[QmlDebug::Constants::QML_MIN_LEVEL] = 0;
int lastBaseEventIndex = 0;
qint64 lastBaseEventEndTime = q->m_modelManager->traceTime()->startTime();
for (i = 0; i < QmlDebug::MaximumQmlEventType; i++) {
nestingLevels << QmlDebug::Constants::QML_MIN_LEVEL;
QHash<int, qint64> dummyHash;
dummyHash[QmlDebug::Constants::QML_MIN_LEVEL] = 0;
endtimesPerNestingLevel << dummyHash;
}
for (i = 0; i < eventCount; i++) {
qint64 st = startTimeData[i].startTime;
int type = q->getEventType(i);
// general level
if (endtimesPerLevel[level] > st) {
level++;
} else {
while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= st)
level--;
}
endtimesPerLevel[level] = st + startTimeData[i].duration;
// per type
if (endtimesPerNestingLevel[type][nestingLevels[type]] > st) {
nestingLevels[type]++;
} else {
while (nestingLevels[type] > QmlDebug::Constants::QML_MIN_LEVEL &&
endtimesPerNestingLevel[type][nestingLevels[type]-1] <= st)
nestingLevels[type]--;
}
endtimesPerNestingLevel[type][nestingLevels[type]] =
st + startTimeData[i].duration;
startTimeData[i].displayRowCollapsed = nestingLevels[type];
if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
if (lastBaseEventEndTime < startTimeData[i].startTime) {
lastBaseEventIndex = i;
lastBaseEventEndTime = startTimeData[i].startTime + startTimeData[i].duration;
}
}
startTimeData[i].baseEventIndex = lastBaseEventIndex;
}
// nestingdepth
for (i = 0; i < eventCount; i++) {
int eventType = q->getEventType(i);
categorySpan[eventType].empty = false;
if (categorySpan[eventType].contractedRows <= startTimeData[i].displayRowCollapsed)
categorySpan[eventType].contractedRows = startTimeData[i].displayRowCollapsed + 1;
}
}
void BasicTimelineModel::BasicTimelineModelPrivate::computeExpandedLevels()
{
QHash<int, int> eventRow;
int eventCount = startTimeData.count();
for (int i = 0; i < eventCount; i++) {
int eventId = startTimeData[i].eventId;
int eventType = eventDict[eventId].eventType;
if (!eventRow.contains(eventId)) {
categorySpan[eventType].empty = false;
eventRow[eventId] = categorySpan[eventType].expandedRows++;
}
startTimeData[i].displayRowExpanded = eventRow[eventId];
}
}
void BasicTimelineModel::BasicTimelineModelPrivate::buildEndTimeList()
{
endTimeData.clear();
int eventCount = startTimeData.count();
for (int i = 0; i < eventCount; i++) {
BasicTimelineModel::QmlRangeEventEndInstance endInstance = {
i,
startTimeData[i].startTime + startTimeData[i].duration
};
endTimeData << endInstance;
}
qSort(endTimeData.begin(), endTimeData.end(), compareEndTimes);
}
void BasicTimelineModel::BasicTimelineModelPrivate::findBindingLoops()
{
typedef QPair<QString, int> CallStackEntry;
QStack<CallStackEntry> callStack;
for (int i = 0; i < startTimeData.size(); ++i) {
QmlRangeEventStartInstance *event = &startTimeData[i];
BasicTimelineModel::QmlRangeEventData data = eventDict.at(event->eventId);
static QVector<QmlDebug::QmlEventType> acceptedTypes =
QVector<QmlDebug::QmlEventType>() << QmlDebug::Compiling << QmlDebug::Creating
<< QmlDebug::Binding << QmlDebug::HandlingSignal;
if (!acceptedTypes.contains(data.eventType))
continue;
const QString eventHash = eventHashes.at(event->eventId);
const QmlRangeEventStartInstance *potentialParent = callStack.isEmpty()
? 0 : &startTimeData[callStack.top().second];
while (potentialParent
&& !(potentialParent->startTime + potentialParent->duration > event->startTime)) {
callStack.pop();
potentialParent = callStack.isEmpty() ? 0
: &startTimeData[callStack.top().second];
}
// check whether event is already in stack
for (int ii = 0; ii < callStack.size(); ++ii) {
if (callStack.at(ii).first == eventHash) {
event->bindingLoopHead = callStack.at(ii).second;
break;
}
}
CallStackEntry newEntry(eventHash, i);
callStack.push(newEntry);
}
}
void BasicTimelineModel::BasicTimelineModelPrivate::computeRowStarts()
{
int rowStart = 0;
for (int i = 0; i < categorySpan.count(); i++) {
categorySpan[i].rowStart = rowStart;
rowStart += q->categoryDepth(i);
}
}
/////////////////// QML interface
bool BasicTimelineModel::isEmpty() const
{
return count() == 0;
}
int BasicTimelineModel::count() const
{
return d->startTimeData.count();
}
qint64 BasicTimelineModel::lastTimeMark() const
{
return d->startTimeData.last().startTime + d->startTimeData.last().duration;
}
bool BasicTimelineModel::expanded(int category) const
{
if (d->categorySpan.count() <= category)
return false;
return d->categorySpan[category].expanded;
}
void BasicTimelineModel::setExpanded(int category, bool expanded)
{
if (d->categorySpan.count() <= category)
return;
d->categorySpan[category].expanded = expanded;
d->computeRowStarts();
emit expandedChanged();
}
int BasicTimelineModel::categoryDepth(int categoryIndex) const
{
if (d->categorySpan.count() <= categoryIndex)
return 1;
// special for paint events: show only when empty model or there's actual events
if (categoryIndex == QmlDebug::Painting && d->categorySpan[categoryIndex].empty && !isEmpty())
return 0;
if (d->categorySpan[categoryIndex].expanded)
return d->categorySpan[categoryIndex].expandedRows;
else
return d->categorySpan[categoryIndex].contractedRows;
}
int BasicTimelineModel::categoryCount() const
{
return 5;
}
const QString BasicTimelineModel::categoryLabel(int categoryIndex) const
{
switch (categoryIndex) {
case 0: return QCoreApplication::translate("MainView", "Painting"); break;
case 1: return QCoreApplication::translate("MainView", "Compiling"); break;
case 2: return QCoreApplication::translate("MainView", "Creating"); break;
case 3: return QCoreApplication::translate("MainView", "Binding"); break;
case 4: return QCoreApplication::translate("MainView", "Handling Signal"); break;
default: return QString();
}
}
int BasicTimelineModel::findFirstIndex(qint64 startTime) const
{
int candidate = -1;
// in the "endtime" list, find the first event that ends after startTime
if (d->endTimeData.isEmpty())
return -1;
if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
candidate = 0;
else
if (d->endTimeData.last().endTime <= startTime)
return -1;
if (candidate == -1)
{
int fromIndex = 0;
int toIndex = d->endTimeData.count()-1;
while (toIndex - fromIndex > 1) {
int midIndex = (fromIndex + toIndex)/2;
if (d->endTimeData[midIndex].endTime < startTime)
fromIndex = midIndex;
else
toIndex = midIndex;
}
candidate = toIndex;
}
int eventIndex = d->endTimeData[candidate].startTimeIndex;
return d->startTimeData[eventIndex].baseEventIndex;
}
int BasicTimelineModel::findFirstIndexNoParents(qint64 startTime) const
{
int candidate = -1;
// in the "endtime" list, find the first event that ends after startTime
if (d->endTimeData.isEmpty())
return -1;
if (d->endTimeData.count() == 1 || d->endTimeData.first().endTime >= startTime)
candidate = 0;
else
if (d->endTimeData.last().endTime <= startTime)
return -1;
if (candidate == -1) {
int fromIndex = 0;
int toIndex = d->endTimeData.count()-1;
while (toIndex - fromIndex > 1) {
int midIndex = (fromIndex + toIndex)/2;
if (d->endTimeData[midIndex].endTime < startTime)
fromIndex = midIndex;
else
toIndex = midIndex;
}
candidate = toIndex;
}
int ndx = d->endTimeData[candidate].startTimeIndex;
return ndx;
}
int BasicTimelineModel::findLastIndex(qint64 endTime) const
{
// in the "starttime" list, find the last event that starts before endtime
if (d->startTimeData.isEmpty())
return -1;
if (d->startTimeData.first().startTime >= endTime)
return -1;
if (d->startTimeData.count() == 1)
return 0;
if (d->startTimeData.last().startTime <= endTime)
return d->startTimeData.count()-1;
int fromIndex = 0;
int toIndex = d->startTimeData.count()-1;
while (toIndex - fromIndex > 1) {
int midIndex = (fromIndex + toIndex)/2;
if (d->startTimeData[midIndex].startTime < endTime)
fromIndex = midIndex;
else
toIndex = midIndex;
}
return fromIndex;
}
int BasicTimelineModel::getEventType(int index) const
{
return d->eventDict[d->startTimeData[index].eventId].eventType;
}
int BasicTimelineModel::getEventCategory(int index) const
{
int evTy = getEventType(index);
// special: paint events shown?
if (d->categorySpan[0].empty && !isEmpty())
return evTy - 1;
return evTy;
}
int BasicTimelineModel::getEventRow(int index) const
{
if (d->categorySpan[getEventType(index)].expanded)
return d->startTimeData[index].displayRowExpanded + d->categorySpan[getEventType(index)].rowStart;
else
return d->startTimeData[index].displayRowCollapsed + d->categorySpan[getEventType(index)].rowStart;
}
qint64 BasicTimelineModel::getDuration(int index) const
{
return d->startTimeData[index].duration;
}
qint64 BasicTimelineModel::getStartTime(int index) const
{
return d->startTimeData[index].startTime;
}
qint64 BasicTimelineModel::getEndTime(int index) const
{
return d->startTimeData[index].startTime + d->startTimeData[index].duration;
}
int BasicTimelineModel::getEventId(int index) const
{
return d->startTimeData[index].eventId;
}
int BasicTimelineModel::getBindingLoopDest(int index) const
{
return d->startTimeData[index].bindingLoopHead;
}
QColor BasicTimelineModel::getColor(int index) const
{
int ndx = getEventId(index);
return QColor::fromHsl((ndx*25)%360, 76, 166);
}
float BasicTimelineModel::getHeight(int index) const
{
Q_UNUSED(index);
// 100% height for regular events
return 1.0f;
}
const QVariantList BasicTimelineModel::getLabelsForCategory(int category) const
{
QVariantList result;
if (d->categorySpan.count() > category && d->categorySpan[category].expanded) {
int eventCount = d->eventDict.count();
for (int i = 0; i < eventCount; i++) {
if (d->eventDict[i].eventType == category) {
QVariantMap element;
element.insert(QLatin1String("displayName"), QVariant(d->eventDict[i].displayName));
element.insert(QLatin1String("description"), QVariant(d->eventDict[i].details));
element.insert(QLatin1String("id"), QVariant(d->eventDict[i].eventId));
result << element;
}
}
}
return result;
}
QString BasicTimelineModel::BasicTimelineModelPrivate::displayTime(double time)
{
if (time < 1e6)
return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
if (time < 1e9)
return QString::number(time/1e6,'f',3) + tr(" ms");
return QString::number(time/1e9,'f',3) + tr(" s");
}
const QVariantList BasicTimelineModel::getEventDetails(int index) const
{
QVariantList result;
int eventId = getEventId(index);
static const char trContext[] = "RangeDetails";
{
QVariantMap valuePair;
valuePair.insert(QLatin1String("title"), QVariant(categoryLabel(d->eventDict[eventId].eventType)));
result << valuePair;
}
// duration
{
QVariantMap valuePair;
valuePair.insert(QCoreApplication::translate(trContext, "Duration:"), QVariant(d->displayTime(d->startTimeData[index].duration)));
result << valuePair;
}
// details
{
QVariantMap valuePair;
QString detailsString = d->eventDict[eventId].details;
if (detailsString.length() > 40)
detailsString = detailsString.left(40) + QLatin1String("...");
valuePair.insert(QCoreApplication::translate(trContext, "Details:"), QVariant(detailsString));
result << valuePair;
}
// location
{
QVariantMap valuePair;
valuePair.insert(QCoreApplication::translate(trContext, "Location:"), QVariant(d->eventDict[eventId].displayName));
result << valuePair;
}
// isbindingloop
{}
return result;
}
const QVariantMap BasicTimelineModel::getEventLocation(int index) const
{
QVariantMap result;
int eventId = getEventId(index);
QmlDebug::QmlEventLocation location
= d->eventDict.at(eventId).location;
result.insert(QLatin1String("file"), location.filename);
result.insert(QLatin1String("line"), location.line);
result.insert(QLatin1String("column"), location.column);
return result;
}
int BasicTimelineModel::getEventIdForHash(const QString &eventHash) const
{
return d->eventHashes.indexOf(eventHash);
}
int BasicTimelineModel::getEventIdForLocation(const QString &filename, int line, int column) const
{
// if this is called from v8 view, we don't have the column number, it will be -1
foreach (const QmlRangeEventData &eventData, d->eventDict) {
if (eventData.location.filename == filename &&
eventData.location.line == line &&
(column == -1 || eventData.location.column == column))
return eventData.eventId;
}
return -1;
}
}
}

View File

@@ -0,0 +1,146 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERTIMELINEMODELPROXY_H
#define QMLPROFILERTIMELINEMODELPROXY_H
#include <QObject>
#include "abstracttimelinemodel.h"
#include <qmldebug/qmlprofilereventtypes.h>
#include <qmldebug/qmlprofilereventlocation.h>
//#include <QHash>
//#include <QVector>
#include <QVariantList>
//#include <QVariantMap>
#include "qmlprofilersimplemodel.h"
#include <QColor>
namespace QmlProfiler {
class QmlProfilerModelManager;
namespace Internal {
class BasicTimelineModel : public AbstractTimelineModel
{
// Q_PROPERTY(bool empty READ isEmpty NOTIFY emptyChanged)
Q_OBJECT
public:
struct QmlRangeEventData
{
QString displayName;
QString details;
QmlDebug::QmlEventLocation location;
QmlDebug::QmlEventType eventType;
int eventId; // separate
};
struct QmlRangeEventStartInstance {
qint64 startTime;
qint64 duration;
int eventId;
// not-expanded, per type
int displayRowExpanded;
int displayRowCollapsed;
int baseEventIndex; // used by findfirstindex
int bindingLoopHead;
};
struct QmlRangeEventEndInstance {
int startTimeIndex;
qint64 endTime;
};
BasicTimelineModel(QObject *parent = 0);
~BasicTimelineModel();
int categories() const;
QStringList categoryTitles() const;
QString name() const;
const QVector<QmlRangeEventStartInstance> getData() const;
const QVector<QmlRangeEventStartInstance> getData(qint64 fromTime, qint64 toTime) const;
void loadData();
Q_INVOKABLE int count() const;
void clear();
// QML interface
bool isEmpty() const;
Q_INVOKABLE qint64 lastTimeMark() const;
Q_INVOKABLE bool expanded(int category) const;
Q_INVOKABLE void setExpanded(int category, bool expanded);
Q_INVOKABLE int categoryDepth(int categoryIndex) const;
Q_INVOKABLE int categoryCount() const;
Q_INVOKABLE const QString categoryLabel(int categoryIndex) const;
int findFirstIndex(qint64 startTime) const;
int findFirstIndexNoParents(qint64 startTime) const;
int findLastIndex(qint64 endTime) const;
int getEventType(int index) const;
int getEventCategory(int index) const;
int getEventRow(int index) const;
Q_INVOKABLE qint64 getDuration(int index) const;
Q_INVOKABLE qint64 getStartTime(int index) const;
Q_INVOKABLE qint64 getEndTime(int index) const;
Q_INVOKABLE int getEventId(int index) const;
int getBindingLoopDest(int index) const;
Q_INVOKABLE QColor getColor(int index) const;
Q_INVOKABLE float getHeight(int index) const;
Q_INVOKABLE const QVariantList getLabelsForCategory(int category) const;
Q_INVOKABLE const QVariantList getEventDetails(int index) const;
Q_INVOKABLE const QVariantMap getEventLocation(int index) const;
Q_INVOKABLE int getEventIdForHash(const QString &eventHash) const;
Q_INVOKABLE int getEventIdForLocation(const QString &filename, int line, int column) const;
private slots:
bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const;
protected slots:
void dataChanged();
private:
class BasicTimelineModelPrivate;
BasicTimelineModelPrivate *d;
};
}
}
#endif

View File

@@ -34,7 +34,7 @@
#include "qmlprofilerattachdialog.h" #include "qmlprofilerattachdialog.h"
#include "qmlprofilerviewmanager.h" #include "qmlprofilerviewmanager.h"
#include "qmlprofilerclientmanager.h" #include "qmlprofilerclientmanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilerdetailsrewriter.h" #include "qmlprofilerdetailsrewriter.h"
#include "timelinerenderer.h" #include "timelinerenderer.h"
@@ -98,8 +98,7 @@ class QmlProfilerTool::QmlProfilerToolPrivate
public: public:
QmlProfilerStateManager *m_profilerState; QmlProfilerStateManager *m_profilerState;
QmlProfilerClientManager *m_profilerConnections; QmlProfilerClientManager *m_profilerConnections;
QmlProfilerDataModel *m_profilerDataModel; QmlProfilerModelManager *m_profilerModelManager;
QmlProfilerDetailsRewriter *m_detailsRewriter;
QmlProfilerViewManager *m_viewContainer; QmlProfilerViewManager *m_viewContainer;
Utils::FileInProjectFinder m_projectFinder; Utils::FileInProjectFinder m_projectFinder;
@@ -138,31 +137,11 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState); d->m_profilerConnections->registerProfilerStateManager(d->m_profilerState);
connect(d->m_profilerConnections, SIGNAL(connectionClosed()), this, SLOT(clientsDisconnected())); connect(d->m_profilerConnections, SIGNAL(connectionClosed()), this, SLOT(clientsDisconnected()));
d->m_profilerDataModel = new QmlProfilerDataModel(this); d->m_profilerModelManager = new QmlProfilerModelManager(&d->m_projectFinder, this);
connect(d->m_profilerDataModel, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged())); connect(d->m_profilerModelManager, SIGNAL(stateChanged()), this, SLOT(profilerDataModelStateChanged()));
connect(d->m_profilerDataModel, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString))); connect(d->m_profilerModelManager, SIGNAL(error(QString)), this, SLOT(showErrorDialog(QString)));
connect(d->m_profilerConnections,
SIGNAL(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)),
d->m_profilerDataModel,
SLOT(addRangedEvent(int,int,qint64,qint64,QStringList,QmlDebug::QmlEventLocation)));
connect(d->m_profilerConnections,
SIGNAL(addV8Event(int,QString,QString,int,double,double)),
d->m_profilerDataModel,
SLOT(addV8Event(int,QString,QString,int,double,double)));
connect(d->m_profilerConnections, SIGNAL(addFrameEvent(qint64,int,int)), d->m_profilerDataModel, SLOT(addFrameEvent(qint64,int,int)));
connect(d->m_profilerConnections, SIGNAL(traceStarted(qint64)), d->m_profilerDataModel, SLOT(setTraceStartTime(qint64)));
connect(d->m_profilerConnections, SIGNAL(traceFinished(qint64)), d->m_profilerDataModel, SLOT(setTraceEndTime(qint64)));
connect(d->m_profilerConnections, SIGNAL(dataReadyForProcessing()), d->m_profilerDataModel, SLOT(complete()));
d->m_detailsRewriter = new QmlProfilerDetailsRewriter(this, &d->m_projectFinder);
connect(d->m_profilerDataModel, SIGNAL(requestDetailsForLocation(int,QmlDebug::QmlEventLocation)),
d->m_detailsRewriter, SLOT(requestDetailsForLocation(int,QmlDebug::QmlEventLocation)));
connect(d->m_detailsRewriter, SIGNAL(rewriteDetailsString(int,QmlDebug::QmlEventLocation,QString)),
d->m_profilerDataModel, SLOT(rewriteDetailsString(int,QmlDebug::QmlEventLocation,QString)));
connect(d->m_detailsRewriter, SIGNAL(eventDetailsChanged()), d->m_profilerDataModel, SLOT(finishedRewritingDetails()));
connect(d->m_profilerDataModel, SIGNAL(reloadDocumentsForDetails()), d->m_detailsRewriter, SLOT(reloadDocuments()));
d->m_profilerConnections->setModelManager(d->m_profilerModelManager);
Command *command = 0; Command *command = 0;
const Context globalContext(C_GLOBAL); const Context globalContext(C_GLOBAL);
@@ -266,7 +245,7 @@ QWidget *QmlProfilerTool::createWidgets()
d->m_viewContainer = new QmlProfilerViewManager(this, d->m_viewContainer = new QmlProfilerViewManager(this,
this, this,
d->m_profilerDataModel, d->m_profilerModelManager,
d->m_profilerState); d->m_profilerState);
connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)), connect(d->m_viewContainer, SIGNAL(gotoSourceLocation(QString,int,int)),
this, SLOT(gotoSourceLocation(QString,int,int))); this, SLOT(gotoSourceLocation(QString,int,int)));
@@ -398,8 +377,9 @@ void QmlProfilerTool::updateTimeDisplay()
if (d->m_profilerState->serverRecording() && if (d->m_profilerState->serverRecording() &&
d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) { d->m_profilerState->currentState() == QmlProfilerStateManager::AppRunning) {
seconds = d->m_recordingElapsedTime.elapsed() / 1000.0; seconds = d->m_recordingElapsedTime.elapsed() / 1000.0;
} else if (d->m_profilerDataModel->currentState() != QmlProfilerDataModel::Empty ) { } else if (d->m_profilerModelManager->state() != QmlProfilerDataState::Empty ) {
seconds = (d->m_profilerDataModel->traceEndTime() - d->m_profilerDataModel->traceStartTime()) / 1.0e9; //seconds = d->m_profilerModelManager->traceDuration() / 1.0e9;
seconds = d->m_profilerModelManager->traceTime()->duration() / 1.0e9;
} }
QString timeString = QString::number(seconds,'f',1); QString timeString = QString::number(seconds,'f',1);
QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6); QString profilerTimeStr = QmlProfilerTool::tr("%1 s").arg(timeString, 6);
@@ -408,7 +388,7 @@ void QmlProfilerTool::updateTimeDisplay()
void QmlProfilerTool::clearData() void QmlProfilerTool::clearData()
{ {
d->m_profilerDataModel->clear(); d->m_profilerModelManager->clear();
d->m_profilerConnections->discardPendingData(); d->m_profilerConnections->discardPendingData();
} }
@@ -514,7 +494,7 @@ void QmlProfilerTool::showErrorDialog(const QString &error)
void QmlProfilerTool::showSaveOption() void QmlProfilerTool::showSaveOption()
{ {
d->m_saveQmlTrace->setEnabled(!d->m_profilerDataModel->isEmpty()); d->m_saveQmlTrace->setEnabled(!d->m_profilerModelManager->isEmpty());
} }
void QmlProfilerTool::showSaveDialog() void QmlProfilerTool::showSaveDialog()
@@ -524,7 +504,7 @@ void QmlProfilerTool::showSaveDialog()
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
if (!filename.endsWith(QLatin1String(TraceFileExtension))) if (!filename.endsWith(QLatin1String(TraceFileExtension)))
filename += QLatin1String(TraceFileExtension); filename += QLatin1String(TraceFileExtension);
d->m_profilerDataModel->save(filename); d->m_profilerModelManager->save(filename);
} }
} }
@@ -540,8 +520,8 @@ void QmlProfilerTool::showLoadDialog()
if (!filename.isEmpty()) { if (!filename.isEmpty()) {
// delayed load (prevent graphical artifacts due to long load time) // delayed load (prevent graphical artifacts due to long load time)
d->m_profilerDataModel->setFilename(filename); d->m_profilerModelManager->setFilename(filename);
QTimer::singleShot(100, d->m_profilerDataModel, SLOT(load())); QTimer::singleShot(100, d->m_profilerModelManager, SLOT(load()));
} }
} }
@@ -549,7 +529,7 @@ void QmlProfilerTool::clientsDisconnected()
{ {
// If the application stopped by itself, check if we have all the data // If the application stopped by itself, check if we have all the data
if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppDying) { if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppDying) {
if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData) if (d->m_profilerModelManager->state() == QmlProfilerDataState::AcquiringData)
d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled); d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppKilled);
else else
d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped); d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppStopped);
@@ -557,21 +537,20 @@ void QmlProfilerTool::clientsDisconnected()
// ... and return to the "base" state // ... and return to the "base" state
d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle); d->m_profilerState->setCurrentState(QmlProfilerStateManager::Idle);
} }
// If the connection is closed while the app is still running, no special action is needed // If the connection is closed while the app is still running, no special action is needed
} }
void QmlProfilerTool::profilerDataModelStateChanged() void QmlProfilerTool::profilerDataModelStateChanged()
{ {
switch (d->m_profilerDataModel->currentState()) { switch (d->m_profilerModelManager->state()) {
case QmlProfilerDataModel::Empty : case QmlProfilerDataState::Empty :
clearDisplay(); clearDisplay();
break; break;
case QmlProfilerDataModel::AcquiringData : case QmlProfilerDataState::AcquiringData :
case QmlProfilerDataModel::ProcessingData : case QmlProfilerDataState::ProcessingData :
// nothing to be done for these two // nothing to be done for these two
break; break;
case QmlProfilerDataModel::Done : case QmlProfilerDataState::Done :
if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested) if (d->m_profilerState->currentState() == QmlProfilerStateManager::AppStopRequested)
d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop); d->m_profilerState->setCurrentState(QmlProfilerStateManager::AppReadyToStop);
showSaveOption(); showSaveOption();
@@ -623,7 +602,7 @@ void QmlProfilerTool::profilerStateChanged()
} }
case QmlProfilerStateManager::AppKilled : { case QmlProfilerStateManager::AppKilled : {
showNonmodalWarning(tr("Application finished before loading profiled data.\nPlease use the stop button instead.")); showNonmodalWarning(tr("Application finished before loading profiled data.\nPlease use the stop button instead."));
d->m_profilerDataModel->clear(); d->m_profilerModelManager->clear();
break; break;
} }
case QmlProfilerStateManager::Idle : case QmlProfilerStateManager::Idle :
@@ -650,9 +629,13 @@ void QmlProfilerTool::serverRecordingChanged()
setRecording(d->m_profilerState->serverRecording()); setRecording(d->m_profilerState->serverRecording());
// clear the old data each time we start a new profiling session // clear the old data each time we start a new profiling session
if (d->m_profilerState->serverRecording()) { if (d->m_profilerState->serverRecording()) {
d->m_clearButton->setEnabled(false);
clearData(); clearData();
d->m_profilerDataModel->prepareForWriting(); d->m_profilerModelManager->prepareForWriting();
} else {
d->m_clearButton->setEnabled(true);
} }
} else {
d->m_clearButton->setEnabled(true);
} }
} }

View File

@@ -0,0 +1,562 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilertracefile.h"
#include <utils/qtcassert.h>
#include <QIODevice>
#include <QStringList>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QDebug>
// import QmlEventType, QmlBindingType enums, QmlEventLocation
using namespace QmlDebug;
const char PROFILER_FILE_VERSION[] = "1.02";
const char TYPE_PAINTING_STR[] = "Painting";
const char TYPE_COMPILING_STR[] = "Compiling";
const char TYPE_CREATING_STR[] = "Creating";
const char TYPE_BINDING_STR[] = "Binding";
const char TYPE_HANDLINGSIGNAL_STR[] = "HandlingSignal";
const char TYPE_PIXMAPCACHE_STR[] = "PixmapCache";
const char TYPE_SCENEGRAPH_STR[] = "SceneGraph";
#define _(X) QLatin1String(X)
//
// "be strict in your output but tolerant in your inputs"
//
namespace QmlProfiler {
namespace Internal {
static QmlEventType qmlEventTypeAsEnum(const QString &typeString)
{
if (typeString == _(TYPE_PAINTING_STR)) {
return Painting;
} else if (typeString == _(TYPE_COMPILING_STR)) {
return Compiling;
} else if (typeString == _(TYPE_CREATING_STR)) {
return Creating;
} else if (typeString == _(TYPE_BINDING_STR)) {
return Binding;
} else if (typeString == _(TYPE_HANDLINGSIGNAL_STR)) {
return HandlingSignal;
} else if (typeString == _(TYPE_PIXMAPCACHE_STR)) {
return PixmapCacheEvent;
} else if (typeString == _(TYPE_SCENEGRAPH_STR)) {
return SceneGraphFrameEvent;
} else {
bool isNumber = false;
int type = typeString.toUInt(&isNumber);
if (isNumber)
return (QmlEventType)type;
else
return MaximumQmlEventType;
}
}
static QString qmlEventTypeAsString(QmlEventType typeEnum)
{
switch (typeEnum) {
case Painting:
return _(TYPE_PAINTING_STR);
break;
case Compiling:
return _(TYPE_COMPILING_STR);
break;
case Creating:
return _(TYPE_CREATING_STR);
break;
case Binding:
return _(TYPE_BINDING_STR);
break;
case HandlingSignal:
return _(TYPE_HANDLINGSIGNAL_STR);
break;
case PixmapCacheEvent:
return _(TYPE_PIXMAPCACHE_STR);
break;
case SceneGraphFrameEvent:
return _(TYPE_SCENEGRAPH_STR);
break;
default:
return QString::number((int)typeEnum);
}
}
QmlProfilerFileReader::QmlProfilerFileReader(QObject *parent) :
QObject(parent),
m_v8Model(0)
{
}
void QmlProfilerFileReader::setV8DataModel(QV8ProfilerDataModel *dataModel)
{
m_v8Model = dataModel;
}
bool QmlProfilerFileReader::load(QIODevice *device)
{
QXmlStreamReader stream(device);
bool validVersion = true;
while (validVersion && !stream.atEnd() && !stream.hasError()) {
QXmlStreamReader::TokenType token = stream.readNext();
QString elementName = stream.name().toString();
switch (token) {
case QXmlStreamReader::StartDocument : continue;
case QXmlStreamReader::StartElement : {
if (elementName == _("trace")) {
QXmlStreamAttributes attributes = stream.attributes();
if (attributes.hasAttribute(_("version")))
validVersion = attributes.value(_("version")).toString()
== _(PROFILER_FILE_VERSION);
else
validVersion = false;
if (attributes.hasAttribute(_("traceStart")))
emit traceStartTime(attributes.value(_("traceStart")).toString().toLongLong());
if (attributes.hasAttribute(_("traceEnd")))
emit traceEndTime(attributes.value(_("traceEnd")).toString().toLongLong());
}
if (elementName == _("eventData")) {
loadEventData(stream);
break;
}
if (elementName == _("profilerDataModel")) {
loadProfilerDataModel(stream);
break;
}
if (elementName == _("v8profile")) {
if (m_v8Model)
m_v8Model->load(stream);
break;
}
break;
}
default: break;
}
}
if (stream.hasError()) {
emit error(tr("Error while parsing trace data file: %1").arg(stream.errorString()));
return false;
} else {
processQmlEvents();
return true;
}
}
void QmlProfilerFileReader::loadEventData(QXmlStreamReader &stream)
{
QTC_ASSERT(stream.name().toString() == _("eventData"), return);
QXmlStreamAttributes attributes = stream.attributes();
if (attributes.hasAttribute(_("totalTime"))) {
// not used any more
}
int eventIndex = -1;
QmlEvent event = {
QString(), // displayname
QString(), // filename
QString(), // details
Painting, // type
QmlBinding, // bindingType, set for backwards compatibility
0, // line
0 // column
};
const QmlEvent defaultEvent = event;
while (!stream.atEnd() && !stream.hasError()) {
QXmlStreamReader::TokenType token = stream.readNext();
const QString elementName = stream.name().toString();
switch (token) {
case QXmlStreamReader::StartElement: {
if (elementName == _("event")) {
event = defaultEvent;
const QXmlStreamAttributes attributes = stream.attributes();
if (attributes.hasAttribute(_("index"))) {
eventIndex = attributes.value(_("index")).toString().toInt();
} else {
// ignore event
eventIndex = -1;
}
break;
}
stream.readNext();
if (stream.tokenType() != QXmlStreamReader::Characters)
break;
const QString readData = stream.text().toString();
if (elementName == _("displayname")) {
event.displayName = readData;
break;
}
if (elementName == _("type")) {
event.type = qmlEventTypeAsEnum(readData);
break;
}
if (elementName == _("filename")) {
event.filename = readData;
break;
}
if (elementName == _("line")) {
event.line = readData.toInt();
break;
}
if (elementName == _("column")) {
event.column = readData.toInt();
break;
}
if (elementName == _("details")) {
event.details = readData;
break;
}
if (elementName == _("bindingType") ||
elementName == _("animationFrame") ||
elementName == _("cacheEventType") ||
elementName == _("sgEventType")) {
event.bindingType = readData.toInt();
break;
}
break;
}
case QXmlStreamReader::EndElement: {
if (elementName == _("event")) {
if (eventIndex >= 0) {
if (eventIndex >= m_qmlEvents.size())
m_qmlEvents.resize(eventIndex + 1);
m_qmlEvents[eventIndex] = event;
}
break;
}
if (elementName == _("eventData")) {
// done reading eventData
return;
}
break;
}
default: break;
} // switch
}
}
void QmlProfilerFileReader::loadProfilerDataModel(QXmlStreamReader &stream)
{
QTC_ASSERT(stream.name().toString() == _("profilerDataModel"), return);
while (!stream.atEnd() && !stream.hasError()) {
QXmlStreamReader::TokenType token = stream.readNext();
const QString elementName = stream.name().toString();
switch (token) {
case QXmlStreamReader::StartElement: {
if (elementName == _("range")) {
Range range = { 0, 0, 0, 0, 0, 0, 0 };
const QXmlStreamAttributes attributes = stream.attributes();
if (!attributes.hasAttribute(_("startTime"))
|| !attributes.hasAttribute(_("eventIndex"))) {
// ignore incomplete entry
continue;
}
range.startTime = attributes.value(_("startTime")).toString().toLongLong();
if (attributes.hasAttribute(_("duration")))
range.duration = attributes.value(_("duration")).toString().toLongLong();
// attributes for special events
if (attributes.hasAttribute(_("framerate")))
range.numericData1 = attributes.value(_("framerate")).toString().toLongLong();
if (attributes.hasAttribute(_("animationcount")))
range.numericData2 = attributes.value(_("animationcount")).toString().toLongLong();
if (attributes.hasAttribute(_("width")))
range.numericData1 = attributes.value(_("width")).toString().toLongLong();
if (attributes.hasAttribute(_("height")))
range.numericData2 = attributes.value(_("height")).toString().toLongLong();
if (attributes.hasAttribute(_("refCount")))
range.numericData3 = attributes.value(_("refCount")).toString().toLongLong();
if (attributes.hasAttribute(_("timing1")))
range.numericData1 = attributes.value(_("timing1")).toString().toLongLong();
if (attributes.hasAttribute(_("timing2")))
range.numericData2 = attributes.value(_("timing2")).toString().toLongLong();
if (attributes.hasAttribute(_("timing3")))
range.numericData3 = attributes.value(_("timing3")).toString().toLongLong();
if (attributes.hasAttribute(_("timing4")))
range.numericData4 = attributes.value(_("timing4")).toString().toLongLong();
if (attributes.hasAttribute(_("timing5")))
range.numericData5 = attributes.value(_("timing5")).toString().toLongLong();
int eventIndex = attributes.value(_("eventIndex")).toString().toInt();
m_ranges.append(QPair<Range,int>(range, eventIndex));
}
break;
}
case QXmlStreamReader::EndElement: {
if (elementName == _("profilerDataModel")) {
// done reading profilerDataModel
return;
}
break;
}
default: break;
} // switch
}
}
void QmlProfilerFileReader::processQmlEvents()
{
for (int i = 0; i < m_ranges.size(); ++i) {
Range range = m_ranges[i].first;
int eventIndex = m_ranges[i].second;
if (eventIndex < 0 || eventIndex >= m_qmlEvents.size()) {
qWarning() << ".qtd file - range index" << eventIndex
<< "is outside of bounds (0, " << m_qmlEvents.size() << ")";
continue;
}
QmlEvent &event = m_qmlEvents[eventIndex];
emit rangedEvent(event.type, event.bindingType, range.startTime, range.duration,
QStringList(event.displayName),
QmlEventLocation(event.filename, event.line, event.column),
range.numericData1,range.numericData2, range.numericData3, range.numericData4, range.numericData5);
}
}
QmlProfilerFileWriter::QmlProfilerFileWriter(QObject *parent) :
QObject(parent),
m_startTime(0),
m_endTime(0),
m_measuredTime(0),
m_v8Model(0)
{
m_acceptedTypes << QmlDebug::Compiling << QmlDebug::Creating << QmlDebug::Binding << QmlDebug::HandlingSignal;
}
void QmlProfilerFileWriter::setTraceTime(qint64 startTime, qint64 endTime, qint64 measuredTime)
{
m_startTime = startTime;
m_endTime = endTime;
m_measuredTime = measuredTime;
}
void QmlProfilerFileWriter::setV8DataModel(QV8ProfilerDataModel *dataModel)
{
m_v8Model = dataModel;
}
void QmlProfilerFileWriter::setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
{
foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
const QString hashStr = QmlProfilerSimpleModel::getHashString(event);
if (!m_qmlEvents.contains(hashStr)) {
QmlEvent e = { event.displayName,
event.location.filename,
event.data.join(_("")),
static_cast<QmlDebug::QmlEventType>(event.eventType),
event.bindingType,
event.location.line,
event.location.column
};
m_qmlEvents.insert(hashStr, e);
}
Range r = { event.startTime, event.duration, event.numericData1, event.numericData2, event.numericData3, event.numericData4, event.numericData5 };
m_ranges.append(QPair<Range, QString>(r, hashStr));
}
calculateMeasuredTime(events);
}
void QmlProfilerFileWriter::save(QIODevice *device)
{
QXmlStreamWriter stream(device);
stream.setAutoFormatting(true);
stream.writeStartDocument();
stream.writeStartElement(_("trace"));
stream.writeAttribute(_("version"), _(PROFILER_FILE_VERSION));
stream.writeAttribute(_("traceStart"), QString::number(m_startTime));
stream.writeAttribute(_("traceEnd"), QString::number(m_endTime));
stream.writeStartElement(_("eventData"));
stream.writeAttribute(_("totalTime"), QString::number(m_measuredTime));
QHash<QString,QmlEvent>::const_iterator eventIter = m_qmlEvents.constBegin();
for (; eventIter != m_qmlEvents.constEnd(); ++eventIter) {
QmlEvent event = eventIter.value();
stream.writeStartElement(_("event"));
stream.writeAttribute(_("index"), QString::number(m_qmlEvents.keys().indexOf(eventIter.key())));
stream.writeTextElement(_("displayname"), event.displayName);
stream.writeTextElement(_("type"), qmlEventTypeAsString(event.type));
if (!event.filename.isEmpty()) {
stream.writeTextElement(_("filename"), event.filename);
stream.writeTextElement(_("line"), QString::number(event.line));
stream.writeTextElement(_("column"), QString::number(event.column));
}
stream.writeTextElement(_("details"), event.details);
if (event.type == Binding)
stream.writeTextElement(_("bindingType"), QString::number(event.bindingType));
if (event.type == Painting && event.bindingType == AnimationFrame)
stream.writeTextElement(_("animationFrame"), QString::number(event.bindingType));
if (event.type == PixmapCacheEvent)
stream.writeTextElement(_("cacheEventType"), QString::number(event.bindingType));
if (event.type == SceneGraphFrameEvent)
stream.writeTextElement(_("sgEventType"), QString::number(event.bindingType));
stream.writeEndElement();
}
stream.writeEndElement(); // eventData
stream.writeStartElement(_("profilerDataModel"));
QVector<QPair<Range, QString> >::const_iterator rangeIter = m_ranges.constBegin();
for (; rangeIter != m_ranges.constEnd(); ++rangeIter) {
Range range = rangeIter->first;
QString eventHash = rangeIter->second;
stream.writeStartElement(_("range"));
stream.writeAttribute(_("startTime"), QString::number(range.startTime));
if (range.duration > 0) // no need to store duration of instantaneous events
stream.writeAttribute(_("duration"), QString::number(range.duration));
stream.writeAttribute(_("eventIndex"), QString::number(m_qmlEvents.keys().indexOf(eventHash)));
QmlEvent event = m_qmlEvents.value(eventHash);
// special: animation event
if (event.type == QmlDebug::Painting && event.bindingType == QmlDebug::AnimationFrame) {
stream.writeAttribute(_("framerate"), QString::number(range.numericData1));
stream.writeAttribute(_("animationcount"), QString::number(range.numericData2));
}
// special: pixmap cache event
if (event.type == QmlDebug::PixmapCacheEvent) {
// pixmap image size
if (event.bindingType == 0) {
stream.writeAttribute(_("width"), QString::number(range.numericData1));
stream.writeAttribute(_("height"), QString::number(range.numericData2));
}
// reference count (1) / cache size changed (2)
if (event.bindingType == 1 || event.bindingType == 2)
stream.writeAttribute(_("refCount"), QString::number(range.numericData3));
}
if (event.type == QmlDebug::SceneGraphFrameEvent) {
// special: scenegraph frame events
if (range.numericData1 > 0)
stream.writeAttribute(_("timing1"), QString::number(range.numericData1));
if (range.numericData2 > 0)
stream.writeAttribute(_("timing2"), QString::number(range.numericData2));
if (range.numericData3 > 0)
stream.writeAttribute(_("timing3"), QString::number(range.numericData3));
if (range.numericData4 > 0)
stream.writeAttribute(_("timing4"), QString::number(range.numericData4));
if (range.numericData5 > 0)
stream.writeAttribute(_("timing5"), QString::number(range.numericData5));
}
stream.writeEndElement();
}
stream.writeEndElement(); // profilerDataModel
m_v8Model->save(stream);
stream.writeEndElement(); // trace
stream.writeEndDocument();
}
void QmlProfilerFileWriter::calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events)
{
// measured time isn't used, but old clients might still need it
// -> we calculate it explicitly
qint64 duration = 0;
QHash<int, qint64> endtimesPerLevel;
int level = QmlDebug::Constants::QML_MIN_LEVEL;
endtimesPerLevel[0] = 0;
foreach (const QmlProfilerSimpleModel::QmlEventData &event, events) {
// whitelist
if (!m_acceptedTypes.contains(event.eventType))
continue;
// level computation
if (endtimesPerLevel[level] > event.startTime) {
level++;
} else {
while (level > QmlDebug::Constants::QML_MIN_LEVEL && endtimesPerLevel[level-1] <= event.startTime)
level--;
}
endtimesPerLevel[level] = event.startTime + event.duration;
if (level == QmlDebug::Constants::QML_MIN_LEVEL) {
duration += event.duration;
}
}
m_measuredTime = duration;
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -0,0 +1,134 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERTRACEFILE_H
#define QMLPROFILERTRACEFILE_H
#include <QObject>
#include <QVector>
#include <QString>
#include <qmldebug/qmlprofilereventlocation.h>
#include <qmldebug/qmlprofilereventtypes.h>
#include "qmlprofilersimplemodel.h"
#include "qv8profilerdatamodel.h"
QT_FORWARD_DECLARE_CLASS(QIODevice)
QT_FORWARD_DECLARE_CLASS(QXmlStreamReader)
namespace QmlProfiler {
namespace Internal {
struct QmlEvent {
QString displayName;
QString filename;
QString details;
QmlDebug::QmlEventType type;
int bindingType;
int line;
int column;
};
struct Range {
qint64 startTime;
qint64 duration;
// numeric data used by animations, pixmap cache, scenegraph
qint64 numericData1;
qint64 numericData2;
qint64 numericData3;
qint64 numericData4;
qint64 numericData5;
};
class QmlProfilerFileReader : public QObject
{
Q_OBJECT
public:
explicit QmlProfilerFileReader(QObject *parent = 0);
void setV8DataModel(QV8ProfilerDataModel *dataModel);
bool load(QIODevice *device);
signals:
void traceStartTime(qint64 traceStartTime);
void traceEndTime(qint64 traceStartTime);
void rangedEvent(int type, int bindingType, qint64 startTime, qint64 length,
const QStringList &data, const QmlDebug::QmlEventLocation &location,
qint64 param1, qint64 param2, qint64 param3, qint64 param4, qint64 param5);
void error(const QString &error);
private:
void loadEventData(QXmlStreamReader &reader);
void loadProfilerDataModel(QXmlStreamReader &reader);
void processQmlEvents();
QV8ProfilerDataModel *m_v8Model;
QVector<QmlEvent> m_qmlEvents;
QVector<QPair<Range, int> > m_ranges;
};
class QmlProfilerFileWriter : public QObject
{
Q_OBJECT
public:
explicit QmlProfilerFileWriter(QObject *parent = 0);
void setTraceTime(qint64 startTime, qint64 endTime, qint64 measturedTime);
void setV8DataModel(QV8ProfilerDataModel *dataModel);
void setQmlEvents(const QVector<QmlProfilerSimpleModel::QmlEventData> &events);
void save(QIODevice *device);
private:
void calculateMeasuredTime(const QVector<QmlProfilerSimpleModel::QmlEventData> &events);
qint64 m_startTime, m_endTime, m_measuredTime;
QV8ProfilerDataModel *m_v8Model;
QHash<QString,QmlEvent> m_qmlEvents;
QVector<QPair<Range, QString> > m_ranges;
QVector <int> m_acceptedTypes;
};
} // namespace Internal
} // namespace QmlProfiler
#endif // QMLPROFILERTRACEFILE_H

View File

@@ -30,12 +30,14 @@
#include "qmlprofilertraceview.h" #include "qmlprofilertraceview.h"
#include "qmlprofilertool.h" #include "qmlprofilertool.h"
#include "qmlprofilerstatemanager.h" #include "qmlprofilerstatemanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilertimelinemodelproxy.h"
#include "timelinemodelaggregator.h"
// Needed for the load&save actions in the context menu // Needed for the load&save actions in the context menu
#include <analyzerbase/ianalyzertool.h> #include <analyzerbase/ianalyzertool.h>
// Comunication with the other views (limit events to range) // Communication with the other views (limit events to range)
#include "qmlprofilerviewmanager.h" #include "qmlprofilerviewmanager.h"
#include <utils/styledbar.h> #include <utils/styledbar.h>
@@ -119,7 +121,9 @@ public:
ScrollableDeclarativeView *m_mainView; ScrollableDeclarativeView *m_mainView;
QDeclarativeView *m_timebar; QDeclarativeView *m_timebar;
QDeclarativeView *m_overview; QDeclarativeView *m_overview;
QmlProfilerDataModel *m_profilerDataModel; QmlProfilerModelManager *m_modelManager;
TimelineModelAggregator *m_modelProxy;
ZoomControl *m_zoomControl; ZoomControl *m_zoomControl;
@@ -129,7 +133,7 @@ public:
int m_currentZoomLevel; int m_currentZoomLevel;
}; };
QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState) QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerModelManager *modelManager, QmlProfilerStateManager *profilerState)
: QWidget(parent), d(new QmlProfilerTraceViewPrivate(this)) : QWidget(parent), d(new QmlProfilerTraceViewPrivate(this))
{ {
setObjectName(QLatin1String("QML Profiler")); setObjectName(QLatin1String("QML Profiler"));
@@ -180,13 +184,15 @@ QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerT
d->m_profilerTool = profilerTool; d->m_profilerTool = profilerTool;
d->m_viewContainer = container; d->m_viewContainer = container;
d->m_profilerDataModel = model; d->m_modelManager = modelManager;
connect(d->m_profilerDataModel, SIGNAL(stateChanged()), d->m_modelProxy = new TimelineModelAggregator(this);
d->m_modelProxy->setModelManager(modelManager);
connect(d->m_modelManager, SIGNAL(stateChanged()),
this, SLOT(profilerDataModelStateChanged())); this, SLOT(profilerDataModelStateChanged()));
d->m_mainView->rootContext()->setContextProperty(QLatin1String("qmlProfilerDataModel"), d->m_mainView->rootContext()->setContextProperty(QLatin1String("qmlProfilerModelProxy"),
d->m_profilerDataModel); d->m_modelProxy);
d->m_overview->rootContext()->setContextProperty(QLatin1String("qmlProfilerDataModel"), d->m_overview->rootContext()->setContextProperty(QLatin1String("qmlProfilerModelProxy"),
d->m_profilerDataModel); d->m_modelProxy);
d->m_profilerState = profilerState; d->m_profilerState = profilerState;
connect(d->m_profilerState, SIGNAL(stateChanged()), connect(d->m_profilerState, SIGNAL(stateChanged()),
@@ -372,12 +378,25 @@ void QmlProfilerTraceView::clearDisplay()
QMetaObject::invokeMethod(d->m_overview->rootObject(), "clearDisplay"); QMetaObject::invokeMethod(d->m_overview->rootObject(), "clearDisplay");
} }
void QmlProfilerTraceView::selectNextEventWithId(int eventId) void QmlProfilerTraceView::selectNextEventByHash(const QString &hash)
{ {
QGraphicsObject *rootObject = d->m_mainView->rootObject(); QGraphicsObject *rootObject = d->m_mainView->rootObject();
if (rootObject) if (rootObject)
QMetaObject::invokeMethod(rootObject, "selectNextWithId", QMetaObject::invokeMethod(rootObject, "selectNextByHash",
Q_ARG(QVariant,QVariant(eventId))); Q_ARG(QVariant,QVariant(hash)));
}
void QmlProfilerTraceView::selectNextEventByLocation(const QString &filename, const int line, const int column)
{
int eventId = d->m_modelProxy->getEventIdForLocation(filename, line, column);
if (eventId != -1) {
QGraphicsObject *rootObject = d->m_mainView->rootObject();
if (rootObject)
QMetaObject::invokeMethod(rootObject, "selectNextById",
Q_ARG(QVariant,QVariant(eventId)));
}
} }
///////////////////////////////////////////////////////// /////////////////////////////////////////////////////////
@@ -444,14 +463,14 @@ void QmlProfilerTraceView::setZoomLevel(int zoomLevel)
void QmlProfilerTraceView::updateRange() void QmlProfilerTraceView::updateRange()
{ {
if (!d->m_profilerDataModel) if (!d->m_modelManager)
return; return;
qreal duration = d->m_zoomControl->endTime() - d->m_zoomControl->startTime(); qreal duration = d->m_zoomControl->endTime() - d->m_zoomControl->startTime();
if (duration <= 0) if (duration <= 0)
return; return;
if (d->m_profilerDataModel->traceDuration() <= 0) if (d->m_modelManager->traceTime()->duration() <= 0)
return; return;
int newLevel = pow(duration / d->m_profilerDataModel->traceDuration(), 1/sliderExp) * sliderTicks; int newLevel = pow(duration / d->m_modelManager->traceTime()->duration(), 1/sliderExp) * sliderTicks;
if (d->m_currentZoomLevel != newLevel) { if (d->m_currentZoomLevel != newLevel) {
d->m_currentZoomLevel = newLevel; d->m_currentZoomLevel = newLevel;
emit zoomLevelChanged(newLevel); emit zoomLevelChanged(newLevel);
@@ -513,7 +532,7 @@ void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
if (d->m_viewContainer->hasGlobalStats()) if (d->m_viewContainer->hasGlobalStats())
getGlobalStatsAction->setEnabled(false); getGlobalStatsAction->setEnabled(false);
if (d->m_profilerDataModel->count() > 0) { if (!d->m_modelProxy->isEmpty()) {
menu.addSeparator(); menu.addSeparator();
viewAllAction = menu.addAction(tr("Reset Zoom")); viewAllAction = menu.addAction(tr("Reset Zoom"));
} }
@@ -523,8 +542,8 @@ void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
if (selectedAction) { if (selectedAction) {
if (selectedAction == viewAllAction) { if (selectedAction == viewAllAction) {
d->m_zoomControl->setRange( d->m_zoomControl->setRange(
d->m_profilerDataModel->traceStartTime(), d->m_modelManager->traceTime()->startTime(),
d->m_profilerDataModel->traceEndTime()); d->m_modelManager->traceTime()->endTime());
} }
if (selectedAction == getLocalStatsAction) { if (selectedAction == getLocalStatsAction) {
d->m_viewContainer->getStatisticsInRange( d->m_viewContainer->getStatisticsInRange(
@@ -532,9 +551,7 @@ void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
d->m_viewContainer->selectionEnd()); d->m_viewContainer->selectionEnd());
} }
if (selectedAction == getGlobalStatsAction) { if (selectedAction == getGlobalStatsAction) {
d->m_viewContainer->getStatisticsInRange( d->m_viewContainer->getStatisticsInRange(-1, -1);
d->m_profilerDataModel->traceStartTime(),
d->m_profilerDataModel->traceEndTime());
} }
} }
} }
@@ -558,19 +575,15 @@ void QmlProfilerTraceView::setAppKilled()
// Profiler State // Profiler State
void QmlProfilerTraceView::profilerDataModelStateChanged() void QmlProfilerTraceView::profilerDataModelStateChanged()
{ {
switch (d->m_profilerDataModel->currentState()) { switch (d->m_modelManager->state()) {
case QmlProfilerDataModel::Empty : case QmlProfilerDataState::Empty:
emit enableToolbar(false); emit enableToolbar(false);
break; break;
case QmlProfilerDataModel::AcquiringData : case QmlProfilerDataState::AcquiringData: break;
// nothing to be done case QmlProfilerDataState::ProcessingData: break;
case QmlProfilerDataState::Done:
emit enableToolbar(true);
break; break;
case QmlProfilerDataModel::ProcessingData :
// nothing to be done
break;
case QmlProfilerDataModel::Done :
emit enableToolbar(true);
break;
default: default:
break; break;
} }
@@ -580,7 +593,7 @@ void QmlProfilerTraceView::profilerStateChanged()
{ {
switch (d->m_profilerState->currentState()) { switch (d->m_profilerState->currentState()) {
case QmlProfilerStateManager::AppKilled : { case QmlProfilerStateManager::AppKilled : {
if (d->m_profilerDataModel->currentState() == QmlProfilerDataModel::AcquiringData) if (d->m_modelManager->state() == QmlProfilerDataState::AcquiringData)
setAppKilled(); setAppKilled();
break; break;
} }

View File

@@ -37,11 +37,12 @@ class IAnalyzerTool;
} }
namespace QmlProfiler { namespace QmlProfiler {
class QmlProfilerModelManager;
namespace Internal { namespace Internal {
class QmlProfilerStateManager; class QmlProfilerStateManager;
class QmlProfilerViewManager; class QmlProfilerViewManager;
class QmlProfilerDataModel;
// capture mouse wheel events // capture mouse wheel events
class MouseWheelResizer : public QObject { class MouseWheelResizer : public QObject {
@@ -83,12 +84,13 @@ protected:
void scrollContentsBy(int dx, int dy); void scrollContentsBy(int dx, int dy);
}; };
class QmlProfilerTraceView : public QWidget class QmlProfilerTraceView : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerDataModel *model, QmlProfilerStateManager *profilerState); explicit QmlProfilerTraceView(QWidget *parent, Analyzer::IAnalyzerTool *profilerTool, QmlProfilerViewManager *container, QmlProfilerModelManager *modelManager, QmlProfilerStateManager *profilerState);
~QmlProfilerTraceView(); ~QmlProfilerTraceView();
void reset(); void reset();
@@ -99,7 +101,8 @@ public:
public slots: public slots:
void clearDisplay(); void clearDisplay();
void selectNextEventWithId(int eventId); void selectNextEventByHash(const QString &eventHash);
void selectNextEventByLocation(const QString &filename, const int line, const int column);
private slots: private slots:
void updateCursorPosition(); void updateCursorPosition();

View File

@@ -0,0 +1,91 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qmlprofilertreeview.h"
#include <QCoreApplication>
#include <QHeaderView>
namespace QmlProfiler {
namespace Internal {
QmlProfilerTreeView::QmlProfilerTreeView(QWidget *parent)
: QTreeView(parent)
{
setFrameStyle(QFrame::NoFrame);
header()->setResizeMode(QHeaderView::Interactive);
header()->setDefaultSectionSize(100);
header()->setMinimumSectionSize(50);
}
// Translate from "old" context to keep 2.8 string freeze
QString QmlProfilerTreeView::displayHeader(Fields header) const
{
static const char ctxt1[] = "QmlProfiler::Internal::QmlProfilerEventsParentsAndChildrenView";
static const char ctxt2[] = "QmlProfiler::Internal::QmlProfilerEventsMainView";
switch (header) {
case Callee:
return QCoreApplication::translate(ctxt1, "Callee");
case CalleeDescription:
return QCoreApplication::translate(ctxt1, "Callee Description");
case Caller:
return QCoreApplication::translate(ctxt1, "Caller");
case CallerDescription:
return QCoreApplication::translate(ctxt1, "Caller Description");
case CallCount:
return QCoreApplication::translate(ctxt2, "Calls");
case Details:
return QCoreApplication::translate(ctxt2, "Details");
case Location:
return QCoreApplication::translate(ctxt2, "Location");
case MaxTime:
return QCoreApplication::translate(ctxt2, "Longest Time");
case TimePerCall:
return QCoreApplication::translate(ctxt2, "Mean Time");
case SelfTime:
return QCoreApplication::translate(ctxt2, "Self Time");
case SelfTimeInPercent:
return QCoreApplication::translate(ctxt2, "Self Time in Percent");
case MinTime:
return QCoreApplication::translate(ctxt2, "Shortest Time");
case TimeInPercent:
return QCoreApplication::translate(ctxt2, "Time in Percent");
case TotalTime:
return QCoreApplication::translate(ctxt2, "Total Time");
case Type:
return QCoreApplication::translate(ctxt2, "Type");
case MedianTime:
return QCoreApplication::translate(ctxt2, "Median Time");
}
return QString();
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILERTREEVIEW
#define QMLPROFILERTREEVIEW
#include <QTreeView>
namespace QmlProfiler {
namespace Internal {
class QmlProfilerTreeView : public QTreeView
{
Q_OBJECT
protected:
QmlProfilerTreeView(QWidget *parent = 0);
enum Fields {
Name,
Callee,
CalleeDescription,
Caller,
CallerDescription,
CallCount,
Details,
Location,
MaxTime,
TimePerCall,
SelfTime,
SelfTimeInPercent,
MinTime,
TimeInPercent,
TotalTime,
Type,
MedianTime,
MaxFields
};
QString displayHeader(Fields header) const;
};
} // namespace Internal
} // namespace QmlProfiler
#endif // QMLPROFILERTREEVIEW

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,202 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QMLPROFILEREVENTVIEW_H
#define QMLPROFILEREVENTVIEW_H
#include <QTreeView>
#include <QStandardItemModel>
#include <qmldebug/qmlprofilereventtypes.h>
#include "qmlprofilermodelmanager.h"
#include "qmlprofilereventsmodelproxy.h"
#include <analyzerbase/ianalyzertool.h>
#include "qmlprofilerviewmanager.h"
namespace QmlProfiler {
namespace Internal {
class QmlProfilerEventsMainView;
class QmlProfilerEventChildrenView;
class QmlProfilerEventRelativesView;
enum ItemRole {
EventHashStrRole = Qt::UserRole+1,
FilenameRole = Qt::UserRole+2,
LineRole = Qt::UserRole+3,
ColumnRole = Qt::UserRole+4,
EventIdRole = Qt::UserRole+5
};
class QmlProfilerEventsWidget : public QWidget
{
Q_OBJECT
public:
explicit QmlProfilerEventsWidget(QWidget *parent,
Analyzer::IAnalyzerTool *profilerTool,
QmlProfilerViewManager *container,
QmlProfilerModelManager *profilerModelManager );
~QmlProfilerEventsWidget();
void clear();
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
QModelIndex selectedItem() const;
bool mouseOnTable(const QPoint &position) const;
void copyTableToClipboard() const;
void copyRowToClipboard() const;
bool hasGlobalStats() const;
void setShowExtendedStatistics(bool show);
bool showExtendedStatistics() const;
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
void showEventInTimeline(int eventId);
void resized();
public slots:
void updateSelectedEvent(const QString &eventHash) const;
void selectBySourceLocation(const QString &filename, int line, int column);
private slots:
void profilerDataModelStateChanged();
protected:
void contextMenuEvent(QContextMenuEvent *ev);
virtual void resizeEvent(QResizeEvent *event);
private:
class QmlProfilerEventsWidgetPrivate;
QmlProfilerEventsWidgetPrivate *d;
};
class QmlProfilerEventsMainView : public QTreeView
{
Q_OBJECT
public:
enum Fields {
Name,
Type,
Percent,
TotalDuration,
SelfPercent,
SelfDuration,
CallCount,
TimePerCall,
MaxTime,
MinTime,
MedianTime,
Details,
MaxFields
};
explicit QmlProfilerEventsMainView(QWidget *parent,
QmlProfilerEventsModelProxy *modelProxy);
~QmlProfilerEventsMainView();
void setFieldViewable(Fields field, bool show);
void setShowAnonymousEvents( bool showThem );
QModelIndex selectedItem() const;
void copyTableToClipboard() const;
void copyRowToClipboard() const;
static QString displayTime(double time);
static QString nameForType(int typeNumber);
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
bool isRangeGlobal(qint64 rangeStart, qint64 rangeEnd) const;
// int selectedEventId() const;
const QString &selectedEventHash() const;
void setShowExtendedStatistics(bool);
bool showExtendedStatistics() const;
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
void eventSelected(const QString &eventHash);
void showEventInTimeline(int eventId);
public slots:
void clear();
void jumpToItem(const QModelIndex &index);
void selectEvent(const QString &eventHash);
void selectEventByLocation(const QString &filename, int line);
void buildModel();
void changeDetailsForEvent(int eventId, const QString &newString);
private slots:
void profilerDataModelStateChanged();
private:
void setHeaderLabels();
void parseModelProxy();
private:
class QmlProfilerEventsMainViewPrivate;
QmlProfilerEventsMainViewPrivate *d;
};
class QmlProfilerEventRelativesView : public QTreeView
{
Q_OBJECT
public:
explicit QmlProfilerEventRelativesView(QmlProfilerModelManager *modelManager,
QmlProfilerEventRelativesModelProxy *modelProxy,
QWidget *parent );
~QmlProfilerEventRelativesView();
signals:
void eventClicked(const QString &eventHash);
public slots:
void displayEvent(const QString &eventHash);
void jumpToItem(const QModelIndex &);
void clear();
private:
void rebuildTree(QmlProfilerEventParentsModelProxy::QmlEventRelativesMap eventMap);
void updateHeader();
QStandardItemModel *treeModel();
// QmlProfilerModelManager *m_profilerModelManager;
class QmlProfilerEventParentsViewPrivate;
QmlProfilerEventParentsViewPrivate *d;
};
} // namespace Internal
} // namespace QmlProfiler
#endif // QMLPROFILEREVENTVIEW_H

View File

@@ -33,8 +33,9 @@
#include "qmlprofilereventview.h" #include "qmlprofilereventview.h"
#include "qmlprofilertool.h" #include "qmlprofilertool.h"
#include "qmlprofilerstatemanager.h" #include "qmlprofilerstatemanager.h"
#include "qmlprofilerdatamodel.h" #include "qmlprofilermodelmanager.h"
#include "qmlprofilerstatewidget.h" #include "qmlprofilerstatewidget.h"
#include "qv8profilereventview.h"
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/fancymainwindow.h> #include <utils/fancymainwindow.h>
@@ -53,15 +54,15 @@ public:
QmlProfilerTraceView *traceView; QmlProfilerTraceView *traceView;
QmlProfilerEventsWidget *eventsView; QmlProfilerEventsWidget *eventsView;
QmlProfilerEventsWidget *v8profilerView; QV8ProfilerEventsWidget *v8profilerView;
QmlProfilerStateManager *profilerState; QmlProfilerStateManager *profilerState;
QmlProfilerDataModel *profilerDataModel; QmlProfilerModelManager *profilerModelManager;
QmlProfilerTool *profilerTool; QmlProfilerTool *profilerTool;
}; };
QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent, QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
QmlProfilerTool *profilerTool, QmlProfilerTool *profilerTool,
QmlProfilerDataModel *model, QmlProfilerModelManager *modelManager,
QmlProfilerStateManager *profilerState) QmlProfilerStateManager *profilerState)
: QObject(parent), d(new QmlProfilerViewManagerPrivate(this)) : QObject(parent), d(new QmlProfilerViewManagerPrivate(this))
{ {
@@ -70,7 +71,7 @@ QmlProfilerViewManager::QmlProfilerViewManager(QObject *parent,
d->eventsView = 0; d->eventsView = 0;
d->v8profilerView = 0; d->v8profilerView = 0;
d->profilerState = profilerState; d->profilerState = profilerState;
d->profilerDataModel = model; d->profilerModelManager = modelManager;
d->profilerTool = profilerTool; d->profilerTool = profilerTool;
createViews(); createViews();
} }
@@ -84,7 +85,8 @@ QmlProfilerViewManager::~QmlProfilerViewManager()
// Views // Views
void QmlProfilerViewManager::createViews() void QmlProfilerViewManager::createViews()
{ {
QTC_ASSERT(d->profilerDataModel, return);
QTC_ASSERT(d->profilerModelManager, return);
QTC_ASSERT(d->profilerState, return); QTC_ASSERT(d->profilerState, return);
Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow(); Utils::FancyMainWindow *mw = AnalyzerManager::mainWindow();
@@ -92,26 +94,28 @@ void QmlProfilerViewManager::createViews()
d->traceView = new QmlProfilerTraceView(mw, d->traceView = new QmlProfilerTraceView(mw,
d->profilerTool, d->profilerTool,
this, this,
d->profilerDataModel, d->profilerModelManager,
d->profilerState); d->profilerState);
connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)), connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
this, SIGNAL(gotoSourceLocation(QString,int,int))); this, SIGNAL(gotoSourceLocation(QString,int,int)));
d->traceView->reset(); d->traceView->reset();
d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this, d->profilerDataModel);
d->eventsView = new QmlProfilerEventsWidget(mw, d->profilerTool, this,
d->profilerModelManager);
connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this, connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), this,
SIGNAL(gotoSourceLocation(QString,int,int))); SIGNAL(gotoSourceLocation(QString,int,int)));
connect(d->eventsView, SIGNAL(showEventInTimeline(int)), d->traceView, connect(d->eventsView, SIGNAL(eventSelectedByHash(QString)), d->traceView,
SLOT(selectNextEventWithId(int))); SLOT(selectNextEventByHash(QString)));
connect(d->traceView, SIGNAL(selectedEventChanged(int)), d->eventsView, connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
SLOT(updateSelectedEvent(int))); d->eventsView, SLOT(selectBySourceLocation(QString,int,int)));
d->v8profilerView = new QmlProfilerEventsWidget(mw, d->profilerTool, d->v8profilerView = new QV8ProfilerEventsWidget(mw, d->profilerTool, this,
this, d->profilerDataModel); d->profilerModelManager);
d->v8profilerView->switchToV8View(); connect(d->traceView, SIGNAL(gotoSourceLocation(QString,int,int)),
d->v8profilerView, SLOT(selectBySourceLocation(QString,int,int)));
connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
this, SIGNAL(gotoSourceLocation(QString,int,int))); d->traceView, SLOT(selectNextEventByLocation(QString,int,int)));
connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)), connect(d->v8profilerView, SIGNAL(gotoSourceLocation(QString,int,int)),
d->eventsView, SLOT(selectBySourceLocation(QString,int,int))); d->eventsView, SLOT(selectBySourceLocation(QString,int,int)));
connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)), connect(d->eventsView, SIGNAL(gotoSourceLocation(QString,int,int)),
@@ -121,8 +125,8 @@ void QmlProfilerViewManager::createViews()
(d->profilerTool, tr("Events"), d->eventsView, Qt::BottomDockWidgetArea); (d->profilerTool, tr("Events"), d->eventsView, Qt::BottomDockWidgetArea);
QDockWidget *timelineDock = AnalyzerManager::createDockWidget QDockWidget *timelineDock = AnalyzerManager::createDockWidget
(d->profilerTool, tr("Timeline"), d->traceView, Qt::BottomDockWidgetArea); (d->profilerTool, tr("Timeline"), d->traceView, Qt::BottomDockWidgetArea);
QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget QDockWidget *v8profilerDock = AnalyzerManager::createDockWidget(
(d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea); d->profilerTool, tr("JavaScript"), d->v8profilerView, Qt::BottomDockWidgetArea);
eventsDock->show(); eventsDock->show();
timelineDock->show(); timelineDock->show();
@@ -132,9 +136,9 @@ void QmlProfilerViewManager::createViews()
mw->tabifyDockWidget(timelineDock, eventsDock); mw->tabifyDockWidget(timelineDock, eventsDock);
mw->tabifyDockWidget(eventsDock, v8profilerDock); mw->tabifyDockWidget(eventsDock, v8profilerDock);
new QmlProfilerStateWidget(d->profilerState, d->profilerDataModel, d->traceView); new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->eventsView);
new QmlProfilerStateWidget(d->profilerState, d->profilerDataModel, d->eventsView); new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->traceView);
new QmlProfilerStateWidget(d->profilerState, d->profilerDataModel, d->v8profilerView); new QmlProfilerStateWidget(d->profilerState, d->profilerModelManager, d->v8profilerView);
} }
bool QmlProfilerViewManager::hasValidSelection() const bool QmlProfilerViewManager::hasValidSelection() const

View File

@@ -33,10 +33,11 @@
#include <QObject> #include <QObject>
namespace QmlProfiler { namespace QmlProfiler {
class QmlProfilerModelManager;
namespace Internal { namespace Internal {
class QmlProfilerTool; class QmlProfilerTool;
class QmlProfilerDataModel;
class QmlProfilerStateManager; class QmlProfilerStateManager;
class QmlProfilerViewManager : public QObject class QmlProfilerViewManager : public QObject
@@ -45,7 +46,7 @@ class QmlProfilerViewManager : public QObject
public: public:
explicit QmlProfilerViewManager(QObject *parent, explicit QmlProfilerViewManager(QObject *parent,
QmlProfilerTool *profilerTool, QmlProfilerTool *profilerTool,
QmlProfilerDataModel *model, QmlProfilerModelManager *modelManager,
QmlProfilerStateManager *profilerState); QmlProfilerStateManager *profilerState);
~QmlProfilerViewManager(); ~QmlProfilerViewManager();

View File

@@ -28,12 +28,9 @@
****************************************************************************/ ****************************************************************************/
#include "qv8profilerdatamodel.h" #include "qv8profilerdatamodel.h"
#include "qmlprofilerdatamodel.h"
#include <QStringList> #include <QStringList>
using namespace QmlDebug;
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventData, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventData, Q_MOVABLE_TYPE);
Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventSub, Q_MOVABLE_TYPE); Q_DECLARE_TYPEINFO(QmlProfiler::Internal::QV8EventSub, Q_MOVABLE_TYPE);
@@ -66,7 +63,7 @@ QV8EventData &QV8EventData::operator=(const QV8EventData &ref)
totalTime = ref.totalTime; totalTime = ref.totalTime;
totalPercent = ref.totalPercent; totalPercent = ref.totalPercent;
selfTime = ref.selfTime; selfTime = ref.selfTime;
selfPercent = ref.selfPercent; SelfTimeInPercent = ref.SelfTimeInPercent;
eventId = ref.eventId; eventId = ref.eventId;
qDeleteAll(parentHash); qDeleteAll(parentHash);
@@ -85,7 +82,7 @@ QV8EventData::QV8EventData()
totalTime = 0; totalTime = 0;
selfTime = 0; selfTime = 0;
totalPercent = 0; totalPercent = 0;
selfPercent = 0; SelfTimeInPercent = 0;
} }
QV8EventData::~QV8EventData() QV8EventData::~QV8EventData()
@@ -101,8 +98,6 @@ class QV8ProfilerDataModel::QV8ProfilerDataModelPrivate
public: public:
QV8ProfilerDataModelPrivate(QV8ProfilerDataModel *qq) {Q_UNUSED(qq);} QV8ProfilerDataModelPrivate(QV8ProfilerDataModel *qq) {Q_UNUSED(qq);}
QmlProfilerDataModel *qmlProfilerDataModel;
void clearV8RootEvent(); void clearV8RootEvent();
void collectV8Statistics(); void collectV8Statistics();
@@ -112,13 +107,12 @@ public:
qint64 v8MeasuredTime; qint64 v8MeasuredTime;
}; };
QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent, QV8ProfilerDataModel::QV8ProfilerDataModel(QObject *parent)
QmlProfilerDataModel *profilerDataModel) : QObject(parent)
: QObject(parent), d(new QV8ProfilerDataModelPrivate(this)) , d(new QV8ProfilerDataModelPrivate(this))
{ {
d->v8MeasuredTime = 0; d->v8MeasuredTime = 0;
d->clearV8RootEvent(); d->clearV8RootEvent();
d->qmlProfilerDataModel = profilerDataModel;
} }
QV8ProfilerDataModel::~QV8ProfilerDataModel() QV8ProfilerDataModel::~QV8ProfilerDataModel()
@@ -133,6 +127,8 @@ void QV8ProfilerDataModel::clear()
d->v8parents.clear(); d->v8parents.clear();
d->clearV8RootEvent(); d->clearV8RootEvent();
d->v8MeasuredTime = 0; d->v8MeasuredTime = 0;
emit changed();
} }
bool QV8ProfilerDataModel::isEmpty() const bool QV8ProfilerDataModel::isEmpty() const
@@ -159,6 +155,11 @@ QList<QV8EventData *> QV8ProfilerDataModel::getV8Events() const
return d->v8EventHash.values(); return d->v8EventHash.values();
} }
QString getHashStringForV8Event(const QString &displayName, const QString &function)
{
return QString::fromLatin1("%1:%2").arg(displayName, function);
}
void QV8ProfilerDataModel::addV8Event(int depth, void QV8ProfilerDataModel::addV8Event(int depth,
const QString &function, const QString &function,
const QString &filename, const QString &filename,
@@ -168,9 +169,7 @@ void QV8ProfilerDataModel::addV8Event(int depth,
{ {
QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) + QString displayName = filename.mid(filename.lastIndexOf(QLatin1Char('/')) + 1) +
QLatin1Char(':') + QString::number(lineNumber); QLatin1Char(':') + QString::number(lineNumber);
QString hashStr = QmlProfilerDataModel::getHashStringForV8Event(displayName, function); QString hashStr = getHashStringForV8Event(displayName, function);
d->qmlProfilerDataModel->setState(QmlProfilerDataModel::AcquiringData);
// time is given in milliseconds, but internally we store it in microseconds // time is given in milliseconds, but internally we store it in microseconds
totalTime *= 1e6; totalTime *= 1e6;
@@ -223,6 +222,7 @@ void QV8ProfilerDataModel::addV8Event(int depth,
newChildSub->totalTime += totalTime; newChildSub->totalTime += totalTime;
} }
} }
} }
void QV8ProfilerDataModel::collectV8Statistics() void QV8ProfilerDataModel::collectV8Statistics()
@@ -250,9 +250,9 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
v8RootEvent.totalTime = v8MeasuredTime + 1; v8RootEvent.totalTime = v8MeasuredTime + 1;
v8RootEvent.selfTime = 0; v8RootEvent.selfTime = 0;
QString rootEventHash = QmlProfilerDataModel::getHashStringForV8Event( QString rootEventHash = getHashStringForV8Event(
QmlProfilerDataModel::rootEventName(), tr("<program>"),
QmlProfilerDataModel::rootEventDescription()); tr("Main Program"));
QV8EventData *v8RootEventPointer = v8EventHash[rootEventHash]; QV8EventData *v8RootEventPointer = v8EventHash[rootEventHash];
if (v8RootEventPointer) { if (v8RootEventPointer) {
v8RootEvent = *v8RootEventPointer; v8RootEvent = *v8RootEventPointer;
@@ -263,7 +263,7 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
foreach (QV8EventData *v8event, v8EventHash.values()) { foreach (QV8EventData *v8event, v8EventHash.values()) {
v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes; v8event->totalPercent = v8event->totalTime * 100.0 / totalTimes;
v8event->selfPercent = v8event->selfTime * 100.0 / selfTimes; v8event->SelfTimeInPercent = v8event->selfTime * 100.0 / selfTimes;
} }
int index = 0; int index = 0;
@@ -274,25 +274,20 @@ void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::collectV8Statistics()
} else { } else {
// On empty data, still add a fake root event // On empty data, still add a fake root event
clearV8RootEvent(); clearV8RootEvent();
v8RootEvent.totalPercent = 100;
QString rootEventHash = QmlProfilerDataModel::getHashStringForV8Event(
QmlProfilerDataModel::rootEventName(),
QmlProfilerDataModel::rootEventDescription());
v8EventHash[rootEventHash] = new QV8EventData;
*v8EventHash[rootEventHash] = v8RootEvent;
} }
} }
void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::clearV8RootEvent() void QV8ProfilerDataModel::QV8ProfilerDataModelPrivate::clearV8RootEvent()
{ {
v8RootEvent.displayName = QmlProfilerDataModel::rootEventName(); v8RootEvent.displayName = tr("<program>");
v8RootEvent.eventHashStr = QmlProfilerDataModel::rootEventName(); v8RootEvent.eventHashStr = tr("<program>");
v8RootEvent.functionName = QmlProfilerDataModel::rootEventDescription(); v8RootEvent.functionName = tr("Main Program");
v8RootEvent.line = -1; v8RootEvent.line = -1;
v8RootEvent.totalTime = 0; v8RootEvent.totalTime = 0;
v8RootEvent.totalPercent = 0; v8RootEvent.totalPercent = 0;
v8RootEvent.selfTime = 0; v8RootEvent.selfTime = 0;
v8RootEvent.selfPercent = 0; v8RootEvent.SelfTimeInPercent = 0;
v8RootEvent.eventId = -1; v8RootEvent.eventId = -1;
qDeleteAll(v8RootEvent.parentHash.values()); qDeleteAll(v8RootEvent.parentHash.values());
@@ -439,7 +434,7 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
} }
default: break; default: break;
} }
} }
// backwards compatibility // backwards compatibility
if (d->v8MeasuredTime == 0) if (d->v8MeasuredTime == 0)
@@ -472,11 +467,10 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
} }
} }
} }
// store v8 events // store v8 events
foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) { foreach (QV8EventData *storedV8Event, v8eventBuffer.values()) {
storedV8Event->eventHashStr = storedV8Event->eventHashStr =
QmlProfilerDataModel::getHashStringForV8Event( getHashStringForV8Event(
storedV8Event->displayName, storedV8Event->functionName); storedV8Event->displayName, storedV8Event->functionName);
d->v8EventHash[storedV8Event->eventHashStr] = storedV8Event; d->v8EventHash[storedV8Event->eventHashStr] = storedV8Event;
} }
@@ -485,5 +479,11 @@ void QV8ProfilerDataModel::load(QXmlStreamReader &stream)
} }
void QV8ProfilerDataModel::complete()
{
collectV8Statistics();
emit changed();
}
} // namespace Internal } // namespace Internal
} // namespace QmlProfiler } // namespace QmlProfiler

View File

@@ -39,7 +39,6 @@
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
class QmlProfilerDataModel;
struct QV8EventSub; struct QV8EventSub;
struct QV8EventData struct QV8EventData
@@ -55,7 +54,7 @@ struct QV8EventData
double totalTime; // given in milliseconds double totalTime; // given in milliseconds
double totalPercent; double totalPercent;
double selfTime; double selfTime;
double selfPercent; double SelfTimeInPercent;
QHash <QString, QV8EventSub *> parentHash; QHash <QString, QV8EventSub *> parentHash;
QHash <QString, QV8EventSub *> childrenHash; QHash <QString, QV8EventSub *> childrenHash;
int eventId; int eventId;
@@ -75,7 +74,7 @@ class QV8ProfilerDataModel : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit QV8ProfilerDataModel(QObject *parent, QmlProfilerDataModel *profilerDataModel); QV8ProfilerDataModel(QObject *parent = 0);
~QV8ProfilerDataModel(); ~QV8ProfilerDataModel();
void clear(); void clear();
@@ -89,6 +88,11 @@ public:
void save(QXmlStreamWriter &stream); void save(QXmlStreamWriter &stream);
void load(QXmlStreamReader &stream); void load(QXmlStreamReader &stream);
void complete();
signals:
void changed();
public slots: public slots:
void addV8Event(int depth, void addV8Event(int depth,
const QString &function, const QString &function,

View File

@@ -0,0 +1,725 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "qv8profilereventview.h"
#include <QUrl>
#include <QHash>
#include <QStandardItem>
#include <QHeaderView>
#include <QApplication>
#include <QClipboard>
#include <QContextMenuEvent>
#include <QDebug>
#include <coreplugin/minisplitter.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include "qmlprofilerviewmanager.h"
#include "qmlprofilertool.h"
#include "qv8profilerdatamodel.h"
#include <QMenu>
#include <utils/qtcassert.h>
using namespace QmlDebug;
namespace QmlProfiler {
namespace Internal {
enum ItemRole {
EventHashStrRole = Qt::UserRole+1,
FilenameRole = Qt::UserRole+2,
LineRole = Qt::UserRole+3,
ColumnRole = Qt::UserRole+4,
EventIdRole = Qt::UserRole+5
};
////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////
class EventsViewItem : public QStandardItem
{
public:
EventsViewItem(const QString &text) : QStandardItem(text) {}
virtual bool operator<(const QStandardItem &other) const
{
if (data().type() == QVariant::String) {
// first column
if (column() == 0) {
return data(FilenameRole).toString() == other.data(FilenameRole).toString() ?
data(LineRole).toInt() < other.data(LineRole).toInt() :
data(FilenameRole).toString() < other.data(FilenameRole).toString();
} else {
return data().toString().toLower() < other.data().toString().toLower();
}
}
return data().toDouble() < other.data().toDouble();
}
};
////////////////////////////////////////////////////////////////////////////////////
class QV8ProfilerEventsWidget::QV8ProfilerEventsWidgetPrivate
{
public:
QV8ProfilerEventsWidgetPrivate(QV8ProfilerEventsWidget *qq):q(qq) {}
~QV8ProfilerEventsWidgetPrivate() {}
QV8ProfilerEventsWidget *q;
Analyzer::IAnalyzerTool *m_profilerTool;
QmlProfilerViewManager *m_viewContainer;
QV8ProfilerEventsMainView *m_eventTree;
QV8ProfilerEventRelativesView *m_eventChildren;
QV8ProfilerEventRelativesView *m_eventParents;
QV8ProfilerDataModel *v8Model;
};
QV8ProfilerEventsWidget::QV8ProfilerEventsWidget(QWidget *parent,
Analyzer::IAnalyzerTool *profilerTool,
QmlProfilerViewManager *container,
QmlProfilerModelManager *profilerModelManager )
: QWidget(parent), d(new QV8ProfilerEventsWidgetPrivate(this))
{
setObjectName(QLatin1String("QmlProfilerV8ProfileView"));
d->v8Model = profilerModelManager->v8Model();
d->m_eventTree = new QV8ProfilerEventsMainView(this, d->v8Model);
connect(d->m_eventTree, SIGNAL(gotoSourceLocation(QString,int,int)), this, SIGNAL(gotoSourceLocation(QString,int,int)));
d->m_eventChildren = new QV8ProfilerEventRelativesView(d->v8Model,
QV8ProfilerEventRelativesView::ChildrenView,
this);
d->m_eventParents = new QV8ProfilerEventRelativesView(d->v8Model,
QV8ProfilerEventRelativesView::ParentsView,
this);
connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventChildren, SLOT(displayEvent(int)));
connect(d->m_eventTree, SIGNAL(eventSelected(int)), d->m_eventParents, SLOT(displayEvent(int)));
connect(d->m_eventChildren, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
connect(d->m_eventParents, SIGNAL(eventClicked(int)), d->m_eventTree, SLOT(selectEvent(int)));
// widget arrangement
QVBoxLayout *groupLayout = new QVBoxLayout;
groupLayout->setContentsMargins(0,0,0,0);
groupLayout->setSpacing(0);
Core::MiniSplitter *splitterVertical = new Core::MiniSplitter;
splitterVertical->addWidget(d->m_eventTree);
Core::MiniSplitter *splitterHorizontal = new Core::MiniSplitter;
splitterHorizontal->addWidget(d->m_eventParents);
splitterHorizontal->addWidget(d->m_eventChildren);
splitterHorizontal->setOrientation(Qt::Horizontal);
splitterVertical->addWidget(splitterHorizontal);
splitterVertical->setOrientation(Qt::Vertical);
splitterVertical->setStretchFactor(0,5);
splitterVertical->setStretchFactor(1,2);
groupLayout->addWidget(splitterVertical);
setLayout(groupLayout);
d->m_profilerTool = profilerTool;
d->m_viewContainer = container;
}
QV8ProfilerEventsWidget::~QV8ProfilerEventsWidget()
{
delete d;
}
void QV8ProfilerEventsWidget::clear()
{
d->m_eventTree->clear();
d->m_eventChildren->clear();
d->m_eventParents->clear();
}
QModelIndex QV8ProfilerEventsWidget::selectedItem() const
{
return d->m_eventTree->selectedItem();
}
void QV8ProfilerEventsWidget::contextMenuEvent(QContextMenuEvent *ev)
{
QTC_ASSERT(d->m_viewContainer, return;);
QMenu menu;
QAction *copyRowAction = 0;
QAction *copyTableAction = 0;
QmlProfilerTool *profilerTool = qobject_cast<QmlProfilerTool *>(d->m_profilerTool);
QPoint position = ev->globalPos();
if (profilerTool) {
QList <QAction *> commonActions = profilerTool->profilerContextMenuActions();
foreach (QAction *act, commonActions) {
menu.addAction(act);
}
}
if (mouseOnTable(position)) {
menu.addSeparator();
if (selectedItem().isValid())
copyRowAction = menu.addAction(QCoreApplication::translate("QmlProfiler::Internal::QmlProfilerEventsWidget", "Copy Row"));
copyTableAction = menu.addAction(QCoreApplication::translate("QmlProfiler::Internal::QmlProfilerEventsWidget", "Copy Table"));
}
QAction *selectedAction = menu.exec(position);
if (selectedAction) {
if (selectedAction == copyRowAction)
copyRowToClipboard();
if (selectedAction == copyTableAction)
copyTableToClipboard();
}
}
void QV8ProfilerEventsWidget::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
emit resized();
}
bool QV8ProfilerEventsWidget::mouseOnTable(const QPoint &position) const
{
QPoint tableTopLeft = d->m_eventTree->mapToGlobal(QPoint(0,0));
QPoint tableBottomRight = d->m_eventTree->mapToGlobal(QPoint(d->m_eventTree->width(), d->m_eventTree->height()));
return (position.x() >= tableTopLeft.x() && position.x() <= tableBottomRight.x() && position.y() >= tableTopLeft.y() && position.y() <= tableBottomRight.y());
}
void QV8ProfilerEventsWidget::copyTableToClipboard() const
{
d->m_eventTree->copyTableToClipboard();
}
void QV8ProfilerEventsWidget::copyRowToClipboard() const
{
d->m_eventTree->copyRowToClipboard();
}
void QV8ProfilerEventsWidget::updateSelectedEvent(int eventId) const
{
if (d->m_eventTree->selectedEventId() != eventId)
d->m_eventTree->selectEvent(eventId);
}
void QV8ProfilerEventsWidget::selectBySourceLocation(const QString &filename, int line, int column)
{
// This slot is used to connect the javascript pane with the qml events pane
// Our javascript trace data does not store column information
// thus we ignore it here
Q_UNUSED(column);
d->m_eventTree->selectEventByLocation(filename, line);
}
////////////////////////////////////////////////////////////////////////////////////
class QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate
{
public:
QV8ProfilerEventsMainViewPrivate(QV8ProfilerEventsMainView *qq) : q(qq) {}
void buildV8ModelFromList( const QList<QV8EventData *> &list );
int getFieldCount();
QString textForItem(QStandardItem *item, bool recursive) const;
QV8ProfilerEventsMainView *q;
QV8ProfilerDataModel *m_v8Model;
QStandardItemModel *m_model;
QList<bool> m_fieldShown;
QHash<int, int> m_columnIndex; // maps field enum to column index
int m_firstNumericColumn;
bool m_preventSelectBounce;
};
////////////////////////////////////////////////////////////////////////////////////
QV8ProfilerEventsMainView::QV8ProfilerEventsMainView(QWidget *parent,
QV8ProfilerDataModel *v8Model)
: QmlProfilerTreeView(parent), d(new QV8ProfilerEventsMainViewPrivate(this))
{
setObjectName(QLatin1String("QmlProfilerEventsTable"));
setSortingEnabled(false);
d->m_model = new QStandardItemModel(this);
setModel(d->m_model);
connect(this,SIGNAL(clicked(QModelIndex)), this,SLOT(jumpToItem(QModelIndex)));
d->m_v8Model = v8Model;
connect(d->m_v8Model, SIGNAL(changed()), this, SLOT(buildModel()));
d->m_firstNumericColumn = 0;
d->m_preventSelectBounce = false;
setFieldViewable(Name, true);
setFieldViewable(Type, false);
setFieldViewable(TimeInPercent, true);
setFieldViewable(TotalTime, true);
setFieldViewable(SelfTimeInPercent, true);
setFieldViewable(SelfTime, true);
setFieldViewable(CallCount, false);
setFieldViewable(TimePerCall, false);
setFieldViewable(MaxTime, false);
setFieldViewable(MinTime, false);
setFieldViewable(MedianTime, false);
setFieldViewable(Details, true);
buildModel();
}
QV8ProfilerEventsMainView::~QV8ProfilerEventsMainView()
{
clear();
delete d->m_model;
delete d;
}
void QV8ProfilerEventsMainView::setFieldViewable(Fields field, bool show)
{
if (field < MaxFields) {
int length = d->m_fieldShown.count();
if (field >= length) {
for (int i=length; i<MaxFields; i++)
d->m_fieldShown << false;
}
d->m_fieldShown[field] = show;
}
}
void QV8ProfilerEventsMainView::setHeaderLabels()
{
int fieldIndex = 0;
d->m_firstNumericColumn = 0;
d->m_columnIndex.clear();
if (d->m_fieldShown[Name]) {
d->m_columnIndex[Name] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Location)));
d->m_firstNumericColumn++;
}
if (d->m_fieldShown[Type]) {
d->m_columnIndex[Type] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Type)));
d->m_firstNumericColumn++;
}
if (d->m_fieldShown[TimeInPercent]) {
d->m_columnIndex[TimeInPercent] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimeInPercent)));
}
if (d->m_fieldShown[TotalTime]) {
d->m_columnIndex[TotalTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
}
if (d->m_fieldShown[SelfTimeInPercent]) {
d->m_columnIndex[Type] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTimeInPercent)));
}
if (d->m_fieldShown[SelfTime]) {
d->m_columnIndex[SelfTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(SelfTime)));
}
if (d->m_fieldShown[CallCount]) {
d->m_columnIndex[CallCount] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(CallCount)));
}
if (d->m_fieldShown[TimePerCall]) {
d->m_columnIndex[TimePerCall] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(TimePerCall)));
}
if (d->m_fieldShown[MedianTime]) {
d->m_columnIndex[MedianTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MedianTime)));
}
if (d->m_fieldShown[MaxTime]) {
d->m_columnIndex[MaxTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MaxTime)));
}
if (d->m_fieldShown[MinTime]) {
d->m_columnIndex[MinTime] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(MinTime)));
}
if (d->m_fieldShown[Details]) {
d->m_columnIndex[Details] = fieldIndex;
d->m_model->setHeaderData(fieldIndex++, Qt::Horizontal, QVariant(displayHeader(Details)));
}
}
void QV8ProfilerEventsMainView::clear()
{
d->m_model->clear();
d->m_model->setColumnCount(d->getFieldCount());
setHeaderLabels();
setSortingEnabled(false);
}
int QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::getFieldCount()
{
int count = 0;
for (int i=0; i < m_fieldShown.count(); ++i)
if (m_fieldShown[i])
count++;
return count;
}
void QV8ProfilerEventsMainView::buildModel()
{
clear();
d->buildV8ModelFromList( d->m_v8Model->getV8Events() );
setRootIsDecorated(false);
setSortingEnabled(true);
sortByColumn(d->m_firstNumericColumn,Qt::DescendingOrder);
expandAll();
if (d->m_fieldShown[Name])
resizeColumnToContents(0);
if (d->m_fieldShown[Type])
resizeColumnToContents(d->m_fieldShown[Name]?1:0);
collapseAll();
}
void QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::buildV8ModelFromList(const QList<QV8EventData *> &list)
{
for (int index = 0; index < list.count(); index++) {
QV8EventData *v8event = list.at(index);
QList<QStandardItem *> newRow;
if (m_fieldShown[Name])
newRow << new EventsViewItem(v8event->displayName);
if (m_fieldShown[TimeInPercent]) {
newRow << new EventsViewItem(QString::number(v8event->totalPercent,'f',2)+QLatin1String(" %"));
newRow.last()->setData(QVariant(v8event->totalPercent));
}
if (m_fieldShown[TotalTime]) {
newRow << new EventsViewItem(displayTime(v8event->totalTime));
newRow.last()->setData(QVariant(v8event->totalTime));
}
if (m_fieldShown[SelfTimeInPercent]) {
newRow << new EventsViewItem(QString::number(v8event->SelfTimeInPercent,'f',2)+QLatin1String(" %"));
newRow.last()->setData(QVariant(v8event->SelfTimeInPercent));
}
if (m_fieldShown[SelfTime]) {
newRow << new EventsViewItem(displayTime(v8event->selfTime));
newRow.last()->setData(QVariant(v8event->selfTime));
}
if (m_fieldShown[Details]) {
newRow << new EventsViewItem(v8event->functionName);
newRow.last()->setData(QVariant(v8event->functionName));
}
if (!newRow.isEmpty()) {
// no edit
foreach (QStandardItem *item, newRow)
item->setEditable(false);
// metadata
newRow.at(0)->setData(QString::fromLatin1("%1:%2").arg(v8event->filename, QString::number(v8event->line)), EventHashStrRole);
newRow.at(0)->setData(QVariant(v8event->filename), FilenameRole);
newRow.at(0)->setData(QVariant(v8event->line), LineRole);
newRow.at(0)->setData(QVariant(-1),ColumnRole); // v8 events have no column info
newRow.at(0)->setData(QVariant(v8event->eventId), EventIdRole);
// append
m_model->invisibleRootItem()->appendRow(newRow);
}
}
}
QString QV8ProfilerEventsMainView::displayTime(double time)
{
if (time < 1e6)
return QString::number(time/1e3,'f',3) + trUtf8(" \xc2\xb5s");
if (time < 1e9)
return QString::number(time/1e6,'f',3) + tr(" ms");
return QString::number(time/1e9,'f',3) + tr(" s");
}
QString QV8ProfilerEventsMainView::nameForType(int typeNumber)
{
switch (typeNumber) {
case 0: return QV8ProfilerEventsMainView::tr("Paint");
case 1: return QV8ProfilerEventsMainView::tr("Compile");
case 2: return QV8ProfilerEventsMainView::tr("Create");
case 3: return QV8ProfilerEventsMainView::tr("Binding");
case 4: return QV8ProfilerEventsMainView::tr("Signal");
}
return QString();
}
int QV8ProfilerEventsMainView::selectedEventId() const
{
QModelIndex index = selectedItem();
if (!index.isValid())
return -1;
QStandardItem *item = d->m_model->item(index.row(), 0);
return item->data(EventIdRole).toInt();
}
void QV8ProfilerEventsMainView::jumpToItem(const QModelIndex &index)
{
if (d->m_preventSelectBounce)
return;
d->m_preventSelectBounce = true;
QStandardItem *clickedItem = d->m_model->itemFromIndex(index);
QStandardItem *infoItem;
if (clickedItem->parent())
infoItem = clickedItem->parent()->child(clickedItem->row(), 0);
else
infoItem = d->m_model->item(index.row(), 0);
// show in editor
int line = infoItem->data(LineRole).toInt();
int column = infoItem->data(ColumnRole).toInt();
QString fileName = infoItem->data(FilenameRole).toString();
if (line!=-1 && !fileName.isEmpty())
emit gotoSourceLocation(fileName, line, column);
// show in callers/callees subwindow
emit eventSelected(infoItem->data(EventIdRole).toInt());
d->m_preventSelectBounce = false;
}
void QV8ProfilerEventsMainView::selectEvent(int eventId)
{
for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0);
if (infoItem->data(EventIdRole).toInt() == eventId) {
setCurrentIndex(d->m_model->indexFromItem(infoItem));
jumpToItem(currentIndex());
return;
}
}
}
void QV8ProfilerEventsMainView::selectEventByLocation(const QString &filename, int line)
{
if (d->m_preventSelectBounce)
return;
for (int i=0; i<d->m_model->rowCount(); i++) {
QStandardItem *infoItem = d->m_model->item(i, 0);
if (currentIndex() != d->m_model->indexFromItem(infoItem) &&
infoItem->data(FilenameRole).toString() == filename &&
infoItem->data(LineRole).toInt() == line) {
setCurrentIndex(d->m_model->indexFromItem(infoItem));
jumpToItem(currentIndex());
return;
}
}
}
QModelIndex QV8ProfilerEventsMainView::selectedItem() const
{
QModelIndexList sel = selectedIndexes();
if (sel.isEmpty())
return QModelIndex();
else
return sel.first();
}
QString QV8ProfilerEventsMainView::QV8ProfilerEventsMainViewPrivate::textForItem(QStandardItem *item, bool recursive = true) const
{
QString str;
if (recursive) {
// indentation
QStandardItem *itemParent = item->parent();
while (itemParent) {
str += QLatin1String(" ");
itemParent = itemParent->parent();
}
}
// item's data
int colCount = m_model->columnCount();
for (int j = 0; j < colCount; ++j) {
QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) : m_model->item(item->row(),j);
str += colItem->data(Qt::DisplayRole).toString();
if (j < colCount-1) str += QLatin1Char('\t');
}
str += QLatin1Char('\n');
// recursively print children
if (recursive && item->child(0))
for (int j = 0; j != item->rowCount(); j++)
str += textForItem(item->child(j));
return str;
}
void QV8ProfilerEventsMainView::copyTableToClipboard() const
{
QString str;
// headers
int columnCount = d->m_model->columnCount();
for (int i = 0; i < columnCount; ++i) {
str += d->m_model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
if (i < columnCount - 1)
str += QLatin1Char('\t');
else
str += QLatin1Char('\n');
}
// data
int rowCount = d->m_model->rowCount();
for (int i = 0; i != rowCount; ++i) {
str += d->textForItem(d->m_model->item(i));
}
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(str, QClipboard::Selection);
clipboard->setText(str, QClipboard::Clipboard);
}
void QV8ProfilerEventsMainView::copyRowToClipboard() const
{
QString str;
str = d->textForItem(d->m_model->itemFromIndex(selectedItem()), false);
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(str, QClipboard::Selection);
clipboard->setText(str, QClipboard::Clipboard);
}
////////////////////////////////////////////////////////////////////////////////////
QV8ProfilerEventRelativesView::QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model,
SubViewType viewType,
QWidget *parent)
: QmlProfilerTreeView(parent)
, m_type(viewType)
, m_v8Model(model)
, m_model(new QStandardItemModel(this))
{
setModel(m_model);
updateHeader();
setSortingEnabled(false);
connect(this, SIGNAL(clicked(QModelIndex)), this, SLOT(jumpToItem(QModelIndex)));
}
QV8ProfilerEventRelativesView::~QV8ProfilerEventRelativesView()
{
}
void QV8ProfilerEventRelativesView::displayEvent(int index)
{
QV8EventData *event = m_v8Model->v8EventDescription(index);
QTC_CHECK(event);
QList<QV8EventSub*> events;
if (m_type == ParentsView)
events = event->parentHash.values();
else
events = event->childrenHash.values();
rebuildTree(events);
updateHeader();
resizeColumnToContents(0);
setSortingEnabled(true);
sortByColumn(1);
}
void QV8ProfilerEventRelativesView::rebuildTree(QList<QV8EventSub*> events)
{
clear();
QStandardItem *topLevelItem = m_model->invisibleRootItem();
foreach (QV8EventSub *event, events) {
QList<QStandardItem *> newRow;
newRow << new EventsViewItem(event->reference->displayName);
newRow << new EventsViewItem(QV8ProfilerEventsMainView::displayTime(event->totalTime));
newRow << new EventsViewItem(event->reference->functionName);
newRow.at(0)->setData(QVariant(event->reference->eventId), EventIdRole);
newRow.at(1)->setData(QVariant(event->totalTime));
foreach (QStandardItem *item, newRow)
item->setEditable(false);
topLevelItem->appendRow(newRow);
}
}
void QV8ProfilerEventRelativesView::clear()
{
m_model->clear();
}
void QV8ProfilerEventRelativesView::updateHeader()
{
m_model->setColumnCount(3);
int columnIndex = 0;
if (m_type == ChildrenView)
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Callee)));
else
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(Caller)));
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(TotalTime)));
if (m_type == ChildrenView)
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CalleeDescription)));
else
m_model->setHeaderData(columnIndex++, Qt::Horizontal, QVariant(displayHeader(CallerDescription)));
}
void QV8ProfilerEventRelativesView::jumpToItem(const QModelIndex &index)
{
QStandardItem *infoItem = m_model->item(index.row(), 0);
emit eventClicked(infoItem->data(EventIdRole).toInt());
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -0,0 +1,163 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef QV8PROFILEREVENTVIEW_H
#define QV8PROFILEREVENTVIEW_H
#include <QStandardItemModel>
#include <qmldebug/qmlprofilereventtypes.h>
#include "qmlprofilermodelmanager.h"
#include "qmlprofilereventsmodelproxy.h"
#include "qmlprofilertreeview.h"
#include <analyzerbase/ianalyzertool.h>
#include "qmlprofilerviewmanager.h"
namespace QmlProfiler {
namespace Internal {
class QV8ProfilerEventsMainView;
class QV8ProfilerEventRelativesView;
struct QV8EventSub;
class QV8ProfilerEventsWidget : public QWidget
{
Q_OBJECT
public:
explicit QV8ProfilerEventsWidget(QWidget *parent,
Analyzer::IAnalyzerTool *profilerTool,
QmlProfilerViewManager *container,
QmlProfilerModelManager *profilerModelManager );
~QV8ProfilerEventsWidget();
void clear();
void getStatisticsInRange(qint64 rangeStart, qint64 rangeEnd);
QModelIndex selectedItem() const;
bool mouseOnTable(const QPoint &position) const;
void copyTableToClipboard() const;
void copyRowToClipboard() const;
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
void showEventInTimeline(int eventId);
void resized();
public slots:
void updateSelectedEvent(int eventId) const;
void selectBySourceLocation(const QString &filename, int line, int column);
protected:
void contextMenuEvent(QContextMenuEvent *ev);
virtual void resizeEvent(QResizeEvent *event);
private:
class QV8ProfilerEventsWidgetPrivate;
QV8ProfilerEventsWidgetPrivate *d;
};
class QV8ProfilerEventsMainView : public QmlProfilerTreeView
{
Q_OBJECT
public:
explicit QV8ProfilerEventsMainView(QWidget *parent,
QV8ProfilerDataModel *v8Model);
~QV8ProfilerEventsMainView();
void setFieldViewable(Fields field, bool show);
void setShowAnonymousEvents( bool showThem );
QModelIndex selectedItem() const;
void copyTableToClipboard() const;
void copyRowToClipboard() const;
static QString displayTime(double time);
static QString nameForType(int typeNumber);
int selectedEventId() const;
void setShowExtendedStatistics(bool);
bool showExtendedStatistics() const;
signals:
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
void eventSelected(int eventId);
public slots:
void clear();
void jumpToItem(const QModelIndex &index);
void selectEvent(int eventId);
void selectEventByLocation(const QString &filename, int line);
void buildModel();
private:
void setHeaderLabels();
private:
class QV8ProfilerEventsMainViewPrivate;
QV8ProfilerEventsMainViewPrivate *d;
};
class QV8ProfilerEventRelativesView : public QmlProfilerTreeView
{
Q_OBJECT
public:
enum SubViewType {
ParentsView,
ChildrenView
};
QV8ProfilerEventRelativesView(QV8ProfilerDataModel *model, SubViewType viewType,
QWidget *parent);
~QV8ProfilerEventRelativesView();
signals:
void eventClicked(int eventId);
public slots:
void displayEvent(int eventId);
void jumpToItem(const QModelIndex &);
void clear();
private:
void rebuildTree(QList<QV8EventSub*> events);
void updateHeader();
QV8ProfilerEventRelativesView::SubViewType m_type;
QV8ProfilerDataModel *m_v8Model;
QStandardItemModel *m_model;
};
} // namespace Internal
} // namespace QmlProfiler
#endif // QV8PROFILEREVENTVIEW_H

View File

@@ -0,0 +1,373 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "timelinemodelaggregator.h"
#include "qmlprofilertimelinemodelproxy.h"
#include "qmlprofilerpainteventsmodelproxy.h"
#include "qmlprofilerplugin.h"
#include <QStringList>
#include <QVariant>
namespace QmlProfiler {
namespace Internal {
class TimelineModelAggregator::TimelineModelAggregatorPrivate {
public:
TimelineModelAggregatorPrivate(TimelineModelAggregator *qq):q(qq) {}
~TimelineModelAggregatorPrivate() {}
TimelineModelAggregator *q;
int basicModelIndex;
QList <AbstractTimelineModel *> modelList;
QmlProfilerModelManager *modelManager;
};
TimelineModelAggregator::TimelineModelAggregator(QObject *parent)
: QObject(parent), d(new TimelineModelAggregatorPrivate(this))
{
}
TimelineModelAggregator::~TimelineModelAggregator()
{
delete d;
}
void TimelineModelAggregator::setModelManager(QmlProfilerModelManager *modelManager)
{
d->modelManager = modelManager;
connect(modelManager,SIGNAL(stateChanged()),this,SLOT(dataChanged()));
connect(modelManager,SIGNAL(countChanged()),this,SIGNAL(countChanged()));
connect(modelManager,SIGNAL(dataAvailable()),this,SIGNAL(dataAvailable()));
// external models pushed on top
foreach (AbstractTimelineModel *timelineModel, QmlProfilerPlugin::instance->getModels()) {
timelineModel->setModelManager(modelManager);
addModel(timelineModel);
}
PaintEventsModelProxy *paintEventsModelProxy = new PaintEventsModelProxy(this);
paintEventsModelProxy->setModelManager(modelManager);
addModel(paintEventsModelProxy);
BasicTimelineModel *basicTimelineModel = new BasicTimelineModel(this);
basicTimelineModel->setModelManager(modelManager);
addModel(basicTimelineModel);
// the basic model is the last one here
d->basicModelIndex = d->modelList.count() - 1;
}
void TimelineModelAggregator::addModel(AbstractTimelineModel *m)
{
d->modelList << m;
connect(m,SIGNAL(countChanged()),this,SIGNAL(countChanged()));
connect(m,SIGNAL(emptyChanged()),this,SIGNAL(emptyChanged()));
connect(m,SIGNAL(expandedChanged()),this,SIGNAL(expandedChanged()));
connect(m,SIGNAL(stateChanged()),this,SIGNAL(stateChanged()));
}
// order?
int TimelineModelAggregator::categories() const
{
int categoryCount = 0;
foreach (const AbstractTimelineModel *modelProxy, d->modelList)
categoryCount += modelProxy->categories();
return categoryCount;
}
int TimelineModelAggregator::visibleCategories() const
{
int categoryCount = 0;
foreach (const AbstractTimelineModel *modelProxy, d->modelList) {
for (int i = 0; i < modelProxy->categories(); i++)
if (modelProxy->categoryDepth(i) > 0)
categoryCount ++;
}
return categoryCount;
}
QStringList TimelineModelAggregator::categoryTitles() const
{
QStringList retString;
foreach (const AbstractTimelineModel *modelProxy, d->modelList)
retString += modelProxy->categoryTitles();
return retString;
}
int TimelineModelAggregator::count(int modelIndex) const
{
if (modelIndex == -1) {
int totalCount = 0;
foreach (const AbstractTimelineModel *modelProxy, d->modelList)
totalCount += modelProxy->count();
return totalCount;
} else {
return d->modelList[modelIndex]->count();
}
}
bool TimelineModelAggregator::isEmpty() const
{
foreach (const AbstractTimelineModel *modelProxy, d->modelList)
if (!modelProxy->isEmpty())
return false;
return true;
}
bool TimelineModelAggregator::eventAccepted(const QmlProfilerSimpleModel::QmlEventData &/*event*/) const
{
// accept all events
return true;
}
int TimelineModelAggregator::basicModelIndex() const
{
return d->basicModelIndex;
}
qint64 TimelineModelAggregator::lastTimeMark() const
{
qint64 mark = -1;
foreach (const AbstractTimelineModel *modelProxy, d->modelList) {
if (!modelProxy->isEmpty()) {
qint64 mk = modelProxy->lastTimeMark();
if (mark > mk)
mark = mk;
}
}
return mark;
}
bool TimelineModelAggregator::expanded(int modelIndex, int category) const
{
return d->modelList[modelIndex]->expanded(category);
}
void TimelineModelAggregator::setExpanded(int modelIndex, int category, bool expanded)
{
// int modelIndex = modelIndexForCategory(category);
// category = correctedCategoryIndexForModel(modelIndex, categoryIndex);
d->modelList[modelIndex]->setExpanded(category, expanded);
}
int TimelineModelAggregator::categoryDepth(int modelIndex, int categoryIndex) const
{
return d->modelList[modelIndex]->categoryDepth(categoryIndex);
}
int TimelineModelAggregator::categoryCount(int modelIndex) const
{
return d->modelList[modelIndex]->categoryCount();
}
int TimelineModelAggregator::rowCount(int modelIndex) const
{
return d->modelList[modelIndex]->rowCount();
}
const QString TimelineModelAggregator::categoryLabel(int modelIndex, int categoryIndex) const
{
// int modelIndex = modelIndexForCategory(categoryIndex);
// categoryIndex = correctedCategoryIndexForModel(modelIndex, categoryIndex);
return d->modelList[modelIndex]->categoryLabel(categoryIndex);
}
int TimelineModelAggregator::modelIndexForCategory(int absoluteCategoryIndex) const
{
int categoryIndex = absoluteCategoryIndex;
for (int modelIndex = 0; modelIndex < d->modelList.count(); modelIndex++)
if (categoryIndex < d->modelList[modelIndex]->categoryCount()) {
return modelIndex;
} else {
categoryIndex -= d->modelList[modelIndex]->categoryCount();
}
return modelCount()-1;
}
int TimelineModelAggregator::correctedCategoryIndexForModel(int modelIndex, int absoluteCategoryIndex) const
{
int categoryIndex = absoluteCategoryIndex;
for (int mi = 0; mi < modelIndex; mi++)
categoryIndex -= d->modelList[mi]->categoryCount();
return categoryIndex;
}
int TimelineModelAggregator::findFirstIndex(int modelIndex, qint64 startTime) const
{
return d->modelList[modelIndex]->findFirstIndex(startTime);
}
int TimelineModelAggregator::findFirstIndexNoParents(int modelIndex, qint64 startTime) const
{
return d->modelList[modelIndex]->findFirstIndexNoParents(startTime);
}
int TimelineModelAggregator::findLastIndex(int modelIndex, qint64 endTime) const
{
return d->modelList[modelIndex]->findLastIndex(endTime);
}
int TimelineModelAggregator::getEventType(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEventType(index);
}
int TimelineModelAggregator::getEventCategoryInModel(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEventCategory(index);
}
int TimelineModelAggregator::getEventRow(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEventRow(index);
}
qint64 TimelineModelAggregator::getDuration(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getDuration(index);
}
qint64 TimelineModelAggregator::getStartTime(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getStartTime(index);
}
qint64 TimelineModelAggregator::getEndTime(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEndTime(index);
}
int TimelineModelAggregator::getEventId(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEventId(index);
}
int TimelineModelAggregator::getBindingLoopDest(int modelIndex,int index) const
{
return d->modelList[modelIndex]->getBindingLoopDest(index);
}
QColor TimelineModelAggregator::getColor(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getColor(index);
}
QVariantList TimelineModelAggregator::getColorRGB(int modelIndex, int itemIndex) const
{
// return color as RGB list, for use in Qml
QColor c = getColor(modelIndex, itemIndex);
QVariantList res;
res.append(QVariant(c.red()));
res.append(QVariant(c.green()));
res.append(QVariant(c.blue()));
return res;
}
float TimelineModelAggregator::getHeight(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getHeight(index);
}
const QVariantList TimelineModelAggregator::getLabelsForCategory(int modelIndex, int category) const
{
// int modelIndex = modelIndexForCategory(category);
// category = correctedCategoryIndexForModel(modelIndex, category);
return d->modelList[modelIndex]->getLabelsForCategory(category);
}
const QVariantList TimelineModelAggregator::getEventDetails(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEventDetails(index);
}
const QVariantMap TimelineModelAggregator::getEventLocation(int modelIndex, int index) const
{
return d->modelList[modelIndex]->getEventLocation(index);
}
int TimelineModelAggregator::getEventIdForHash(const QString &hash) const
{
foreach (const AbstractTimelineModel *model, d->modelList) {
int eventId = model->getEventIdForHash(hash);
if (eventId != -1)
return eventId;
}
return -1;
}
int TimelineModelAggregator::getEventIdForLocation(const QString &filename, int line, int column) const
{
foreach (const AbstractTimelineModel *model, d->modelList) {
int eventId = model->getEventIdForLocation(filename, line, column);
if (eventId != -1)
return eventId;
}
return -1;
}
void TimelineModelAggregator::dataChanged()
{
// this is a slot connected for every modelproxy
// nothing to do here, each model will take care of itself
}
int TimelineModelAggregator::modelCount() const
{
return d->modelList.count();
}
qint64 TimelineModelAggregator::traceStartTime() const
{
return d->modelManager->traceTime()->startTime();
}
qint64 TimelineModelAggregator::traceEndTime() const
{
return d->modelManager->traceTime()->endTime();
}
qint64 TimelineModelAggregator::traceDuration() const
{
return d->modelManager->traceTime()->duration();
}
int TimelineModelAggregator::getState() const
{
return (int)d->modelManager->state();
}
} // namespace Internal
} // namespace QmlProfiler

View File

@@ -0,0 +1,122 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef TIMELINEMODELAGGREGATOR_H
#define TIMELINEMODELAGGREGATOR_H
#include "abstracttimelinemodel.h"
#include "qmlprofilermodelmanager.h"
namespace QmlProfiler {
namespace Internal {
class TimelineModelAggregator : public QObject
{
Q_OBJECT
public:
TimelineModelAggregator(QObject *parent = 0);
~TimelineModelAggregator();
void setModelManager(QmlProfilerModelManager *modelManager);
void addModel(AbstractTimelineModel *m);
Q_INVOKABLE int categories() const;
Q_INVOKABLE int visibleCategories() const;
Q_INVOKABLE QStringList categoryTitles() const;
Q_INVOKABLE int count(int modelIndex = -1) const;
void clear();
Q_INVOKABLE int modelCount() const;
Q_INVOKABLE qint64 traceStartTime() const;
Q_INVOKABLE qint64 traceEndTime() const;
Q_INVOKABLE qint64 traceDuration() const;
Q_INVOKABLE int getState() const;
bool isEmpty() const;
bool eventAccepted(const QmlProfilerSimpleModel::QmlEventData &event) const;
Q_INVOKABLE int basicModelIndex() const;
Q_INVOKABLE qint64 lastTimeMark() const;
Q_INVOKABLE bool expanded(int modelIndex, int category) const;
Q_INVOKABLE void setExpanded(int modelIndex, int category, bool expanded);
Q_INVOKABLE int categoryDepth(int modelIndex, int categoryIndex) const;
Q_INVOKABLE int categoryCount(int modelIndex) const;
Q_INVOKABLE int rowCount(int modelIndex) const;
Q_INVOKABLE const QString categoryLabel(int modelIndex, int categoryIndex) const;
int findFirstIndex(int modelIndex, qint64 startTime) const;
int findFirstIndexNoParents(int modelIndex, qint64 startTime) const;
int findLastIndex(int modelIndex, qint64 endTime) const;
int getEventType(int modelIndex, int index) const;
Q_INVOKABLE int getEventCategoryInModel(int modelIndex, int index) const;
int getEventRow(int modelIndex, int index) const;
Q_INVOKABLE qint64 getDuration(int modelIndex, int index) const;
Q_INVOKABLE qint64 getStartTime(int modelIndex, int index) const;
Q_INVOKABLE qint64 getEndTime(int modelIndex, int index) const;
Q_INVOKABLE int getEventId(int modelIndex, int index) const;
Q_INVOKABLE int getBindingLoopDest(int modelIndex, int index) const;
Q_INVOKABLE QColor getColor(int modelIndex, int index) const;
Q_INVOKABLE QVariantList getColorRGB(int modelIndex, int itemIndex) const;
Q_INVOKABLE float getHeight(int modelIndex, int index) const;
Q_INVOKABLE const QVariantList getLabelsForCategory(int modelIndex, int category) const;
Q_INVOKABLE const QVariantList getEventDetails(int modelIndex, int index) const;
Q_INVOKABLE const QVariantMap getEventLocation(int modelIndex, int index) const;
Q_INVOKABLE int getEventIdForHash(const QString &hash) const;
Q_INVOKABLE int getEventIdForLocation(const QString &filename, int line, int column) const;
Q_INVOKABLE int modelIndexForCategory(int absoluteCategoryIndex) const;
Q_INVOKABLE int correctedCategoryIndexForModel(int modelIndex, int absoluteCategoryIndex) const;
signals:
void countChanged();
void dataAvailable();
void stateChanged();
void emptyChanged();
void expandedChanged();
protected slots:
void dataChanged();
private:
class TimelineModelAggregatorPrivate;
TimelineModelAggregatorPrivate *d;
};
}
}
#endif // TIMELINEMODELAGGREGATOR_H

View File

@@ -35,6 +35,7 @@
#include <QPixmap> #include <QPixmap>
#include <QPainter> #include <QPainter>
#include <QGraphicsSceneMouseEvent> #include <QGraphicsSceneMouseEvent>
#include <QVarLengthArray>
#include <math.h> #include <math.h>
@@ -44,14 +45,26 @@ const int DefaultRowHeight = 30;
TimelineRenderer::TimelineRenderer(QDeclarativeItem *parent) : TimelineRenderer::TimelineRenderer(QDeclarativeItem *parent) :
QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0), QDeclarativeItem(parent), m_startTime(0), m_endTime(0), m_spacing(0),
m_lastStartTime(0), m_lastEndTime(0), m_profilerDataModel(0) m_lastStartTime(0), m_lastEndTime(0)
, m_profilerModelProxy(0)
{ {
clearData(); clearData();
setFlag(QGraphicsItem::ItemHasNoContents, false); setFlag(QGraphicsItem::ItemHasNoContents, false);
setAcceptedMouseButtons(Qt::LeftButton); setAcceptedMouseButtons(Qt::LeftButton);
setAcceptHoverEvents(true); setAcceptHoverEvents(true);
for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) }
m_rowsExpanded << false;
void TimelineRenderer::setProfilerModelProxy(QObject *profilerModelProxy)
{
if (m_profilerModelProxy) {
disconnect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
}
m_profilerModelProxy = qobject_cast<TimelineModelAggregator *>(profilerModelProxy);
if (m_profilerModelProxy) {
connect(m_profilerModelProxy, SIGNAL(expandedChanged()), this, SLOT(requestPaint()));
}
emit profilerModelProxyChanged(m_profilerModelProxy);
} }
void TimelineRenderer::componentComplete() void TimelineRenderer::componentComplete()
@@ -80,105 +93,68 @@ void TimelineRenderer::paint(QPainter *p, const QStyleOptionGraphicsItem *, QWid
m_spacing = qreal(width()) / windowDuration; m_spacing = qreal(width()) / windowDuration;
m_rowWidths.clear();
// The "1+" is because the reference screenshot features an empty row per type, in order to leave space for the title
for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) {
m_rowWidths << 1 + (m_rowsExpanded[i] ? m_profilerDataModel->uniqueEventsOfType(i) :
m_profilerDataModel->maxNestingForType(i));
}
// event rows
m_rowStarts.clear();
int pos = 0;
for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) {
m_rowStarts << pos;
pos += DefaultRowHeight * m_rowWidths[i];
}
p->setPen(Qt::transparent); p->setPen(Qt::transparent);
// speedup: don't draw overlapping events, just skip them for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
m_rowLastX.clear(); int lastIndex = m_profilerModelProxy->findLastIndex(modelIndex, m_endTime);
for (int i=0; i<QmlDebug::MaximumQmlEventType; i++) if (lastIndex >= 0 && lastIndex < m_profilerModelProxy->count(modelIndex)) {
for (int j=0; j<m_rowWidths[i]; j++) int firstIndex = m_profilerModelProxy->findFirstIndex(modelIndex, m_startTime);
m_rowLastX << -m_startTime * m_spacing; if (firstIndex >= 0) {
drawItemsToPainter(p, modelIndex, firstIndex, lastIndex);
int firstIndex = m_profilerDataModel->findFirstIndex(m_startTime); if (m_selectedModel == modelIndex)
int lastIndex = m_profilerDataModel->findLastIndex(m_endTime); drawSelectionBoxes(p, modelIndex, firstIndex, lastIndex);
drawBindingLoopMarkers(p, modelIndex, firstIndex, lastIndex);
if (lastIndex < m_profilerDataModel->count()) { }
drawItemsToPainter(p, firstIndex, lastIndex);
drawSelectionBoxes(p, firstIndex, lastIndex);
drawBindingLoopMarkers(p, firstIndex, lastIndex);
}
m_lastStartTime = m_startTime;
m_lastEndTime = m_endTime;
}
QColor TimelineRenderer::colorForItem(int itemIndex)
{
int ndx = m_profilerDataModel->getEventId(itemIndex);
return QColor::fromHsl((ndx*25)%360, 76, 166);
}
void TimelineRenderer::drawItemsToPainter(QPainter *p, int fromIndex, int toIndex)
{
int x, y, width, height, rowNumber, eventType;
for (int i = fromIndex; i <= toIndex; i++) {
x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing;
eventType = m_profilerDataModel->getType(i);
if (m_rowsExpanded[eventType])
y = m_rowStarts[eventType] + DefaultRowHeight *
(m_profilerDataModel->eventPosInType(i) + 1);
else
y = m_rowStarts[eventType] + DefaultRowHeight *
m_profilerDataModel->getNestingLevel(i);
width = m_profilerDataModel->getDuration(i)*m_spacing;
if (width<1)
width = 1;
rowNumber = y/DefaultRowHeight;
if (m_rowLastX[rowNumber] > x+width)
continue;
m_rowLastX[rowNumber] = x+width;
// special: animations
if (eventType == 0 && m_profilerDataModel->getAnimationCount(i) >= 0) {
double scale = m_profilerDataModel->getMaximumAnimationCount() -
m_profilerDataModel->getMinimumAnimationCount();
double fraction;
if (scale > 1)
fraction = (double)(m_profilerDataModel->getAnimationCount(i) -
m_profilerDataModel->getMinimumAnimationCount()) / scale;
else
fraction = 1.0;
height = DefaultRowHeight * (fraction * 0.85 + 0.15);
y += DefaultRowHeight - height;
double fpsFraction = m_profilerDataModel->getFramerate(i) / 60.0;
if (fpsFraction > 1.0)
fpsFraction = 1.0;
p->setBrush(QColor::fromHsl((fpsFraction*96)+10, 76, 166));
p->drawRect(x, y, width, height);
} else {
// normal events
p->setBrush(colorForItem(i));
p->drawRect(x, y, width, DefaultRowHeight);
} }
} }
m_lastStartTime = m_startTime;
m_lastEndTime = m_endTime;
} }
void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex) void TimelineRenderer::drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex)
{
p->save();
p->setPen(Qt::transparent);
int modelRowStart = 0;
for (int mi = 0; mi < modelIndex; mi++)
modelRowStart += m_profilerModelProxy->rowCount(mi);
for (int i = fromIndex; i <= toIndex; i++) {
int x, y, width, height;
x = (m_profilerModelProxy->getStartTime(modelIndex, i) - m_startTime) * m_spacing;
int rowNumber = m_profilerModelProxy->getEventRow(modelIndex, i);
y = (modelRowStart + rowNumber) * DefaultRowHeight;
width = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing;
if (width < 1)
width = 1;
height = DefaultRowHeight * m_profilerModelProxy->getHeight(modelIndex, i);
y += DefaultRowHeight - height;
// normal events
p->setBrush(m_profilerModelProxy->getColor(modelIndex, i));
p->drawRect(x, y, width, height);
}
p->restore();
}
void TimelineRenderer::drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex)
{ {
if (m_selectedItem == -1) if (m_selectedItem == -1)
return; return;
int id = m_profilerDataModel->getEventId(m_selectedItem);
p->setBrush(Qt::transparent); int id = m_profilerModelProxy->getEventId(modelIndex, m_selectedItem);
int modelRowStart = 0;
for (int mi = 0; mi < modelIndex; mi++)
modelRowStart += m_profilerModelProxy->rowCount(mi);
p->save();
QColor selectionColor = Qt::blue; QColor selectionColor = Qt::blue;
if (m_selectionLocked) if (m_selectionLocked)
selectionColor = QColor(96,0,255); selectionColor = QColor(96,0,255);
@@ -186,25 +162,18 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toInde
QPen lightPen(QBrush(selectionColor.lighter(130)), 2); QPen lightPen(QBrush(selectionColor.lighter(130)), 2);
lightPen.setJoinStyle(Qt::MiterJoin); lightPen.setJoinStyle(Qt::MiterJoin);
p->setPen(lightPen); p->setPen(lightPen);
p->setBrush(Qt::transparent);
int x, y, width, eventType; int x, y, width;
p->setPen(lightPen);
QRect selectedItemRect(0,0,0,0); QRect selectedItemRect(0,0,0,0);
for (int i = fromIndex; i <= toIndex; i++) { for (int i = fromIndex; i <= toIndex; i++) {
if (m_profilerDataModel->getEventId(i) != id) if (m_profilerModelProxy->getEventId(modelIndex, i) != id)
continue; continue;
x = (m_profilerDataModel->getStartTime(i) - m_startTime) * m_spacing; x = (m_profilerModelProxy->getStartTime(modelIndex, i) - m_startTime) * m_spacing;
eventType = m_profilerDataModel->getType(i); y = (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i)) * DefaultRowHeight;
if (m_rowsExpanded[eventType])
y = m_rowStarts[eventType] + DefaultRowHeight *
(m_profilerDataModel->eventPosInType(i) + 1);
else
y = m_rowStarts[eventType] + DefaultRowHeight *
m_profilerDataModel->getNestingLevel(i);
width = m_profilerDataModel->getDuration(i)*m_spacing; width = m_profilerModelProxy->getDuration(modelIndex, i)*m_spacing;
if (width<1) if (width<1)
width = 1; width = 1;
@@ -219,12 +188,14 @@ void TimelineRenderer::drawSelectionBoxes(QPainter *p, int fromIndex, int toInde
p->setPen(strongPen); p->setPen(strongPen);
p->drawRect(selectedItemRect); p->drawRect(selectedItemRect);
} }
p->restore();
} }
void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex) void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex)
{ {
int destindex; int destindex;
int xfrom, xto, eventType; int xfrom, xto;
int yfrom, yto; int yfrom, yto;
int radius = DefaultRowHeight / 3; int radius = DefaultRowHeight / 3;
QPen shadowPen = QPen(QColor("grey"),2); QPen shadowPen = QPen(QColor("grey"),2);
@@ -234,38 +205,24 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int to
p->save(); p->save();
for (int i = fromIndex; i <= toIndex; i++) { for (int i = fromIndex; i <= toIndex; i++) {
destindex = m_profilerDataModel->getBindingLoopDest(i); destindex = m_profilerModelProxy->getBindingLoopDest(modelIndex, i);
if (destindex >= 0) { if (destindex >= 0) {
// from // from
xfrom = (m_profilerDataModel->getStartTime(i) + xfrom = (m_profilerModelProxy->getStartTime(modelIndex, i) +
m_profilerDataModel->getDuration(i)/2 - m_profilerModelProxy->getDuration(modelIndex, i)/2 -
m_startTime) * m_spacing; m_startTime) * m_spacing;
eventType = m_profilerDataModel->getType(i); yfrom = getYPosition(modelIndex, i);
if (m_rowsExpanded[eventType])
yfrom = m_rowStarts[eventType] + DefaultRowHeight*
(m_profilerDataModel->eventPosInType(i) + 1);
else
yfrom = m_rowStarts[eventType] + DefaultRowHeight *
m_profilerDataModel->getNestingLevel(i);
yfrom += DefaultRowHeight / 2; yfrom += DefaultRowHeight / 2;
// to // to
xto = (m_profilerDataModel->getStartTime(destindex) + xto = (m_profilerModelProxy->getStartTime(modelIndex, destindex) +
m_profilerDataModel->getDuration(destindex)/2 - m_profilerModelProxy->getDuration(modelIndex, destindex)/2 -
m_startTime) * m_spacing; m_startTime) * m_spacing;
eventType = m_profilerDataModel->getType(destindex); yto = getYPosition(modelIndex, destindex);
if (m_rowsExpanded[eventType])
yto = m_rowStarts[eventType] + DefaultRowHeight *
(m_profilerDataModel->eventPosInType(destindex) + 1);
else
yto = m_rowStarts[eventType] + DefaultRowHeight *
m_profilerDataModel->getNestingLevel(destindex);
yto += DefaultRowHeight / 2; yto += DefaultRowHeight / 2;
// radius // radius
int eventWidth = m_profilerDataModel->getDuration(i) * m_spacing; int eventWidth = m_profilerModelProxy->getDuration(modelIndex, i) * m_spacing;
radius = 5; radius = 5;
if (radius * 2 > eventWidth) if (radius * 2 > eventWidth)
radius = eventWidth / 2; radius = eventWidth / 2;
@@ -292,6 +249,17 @@ void TimelineRenderer::drawBindingLoopMarkers(QPainter *p, int fromIndex, int to
p->restore(); p->restore();
} }
int TimelineRenderer::modelFromPosition(int y)
{
y = y / DefaultRowHeight;
for (int modelIndex = 0; modelIndex < m_profilerModelProxy->modelCount(); modelIndex++) {
y -= m_profilerModelProxy->rowCount(modelIndex);
if (y < 0)
return modelIndex;
}
return 0;
}
void TimelineRenderer::mousePressEvent(QGraphicsSceneMouseEvent *event) void TimelineRenderer::mousePressEvent(QGraphicsSceneMouseEvent *event)
{ {
// special case: if there is a drag area below me, don't accept the // special case: if there is a drag area below me, don't accept the
@@ -326,15 +294,17 @@ void TimelineRenderer::hoverMoveEvent(QGraphicsSceneHoverEvent *event)
void TimelineRenderer::manageClicked() void TimelineRenderer::manageClicked()
{ {
if (m_currentSelection.eventIndex != -1) { if (m_currentSelection.eventIndex != -1) {
if (m_currentSelection.eventIndex == m_selectedItem) if (m_currentSelection.eventIndex == m_selectedItem && m_currentSelection.modelIndex == m_selectedModel)
setSelectionLocked(!m_selectionLocked); setSelectionLocked(!m_selectionLocked);
else else
setSelectionLocked(true); setSelectionLocked(true);
emit itemPressed(m_currentSelection.eventIndex); emit itemPressed(m_currentSelection.modelIndex, m_currentSelection.eventIndex);
} else { } else {
// setSelectionLocked(false); setSelectionLocked(false);
} }
setSelectedModel(m_currentSelection.modelIndex);
setSelectedItem(m_currentSelection.eventIndex); setSelectedItem(m_currentSelection.eventIndex);
} }
void TimelineRenderer::manageHovered(int x, int y) void TimelineRenderer::manageHovered(int x, int y)
@@ -344,6 +314,7 @@ void TimelineRenderer::manageHovered(int x, int y)
qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime; qint64 time = x * (m_endTime - m_startTime) / width() + m_startTime;
int row = y / DefaultRowHeight; int row = y / DefaultRowHeight;
int modelIndex = modelFromPosition(y);
// already covered? nothing to do // already covered? nothing to do
if (m_currentSelection.eventIndex != -1 && if (m_currentSelection.eventIndex != -1 &&
@@ -354,34 +325,37 @@ void TimelineRenderer::manageHovered(int x, int y)
} }
// find if there's items in the time range // find if there's items in the time range
int eventFrom = m_profilerDataModel->findFirstIndex(time); int eventFrom = m_profilerModelProxy->findFirstIndex(modelIndex, time);
int eventTo = m_profilerDataModel->findLastIndex(time); int eventTo = m_profilerModelProxy->findLastIndex(modelIndex, time);
if (eventTo < eventFrom || eventTo >= m_profilerDataModel->count()) { if (eventFrom == -1 ||
eventTo < eventFrom || eventTo >= m_profilerModelProxy->count()) {
m_currentSelection.eventIndex = -1; m_currentSelection.eventIndex = -1;
return; return;
} }
int modelRowStart = 0;
for (int mi = 0; mi < modelIndex; mi++)
modelRowStart += m_profilerModelProxy->rowCount(mi);
// find if we are in the right column // find if we are in the right column
int itemRow, eventType; int itemRow;
for (int i=eventTo; i>=eventFrom; --i) { for (int i=eventTo; i>=eventFrom; --i) {
if (ceil(m_profilerDataModel->getEndTime(i)*m_spacing) < floor(time*m_spacing)) if (ceil(m_profilerModelProxy->getEndTime(modelIndex, i)*m_spacing) < floor(time*m_spacing))
continue; continue;
eventType = m_profilerDataModel->getType(i); itemRow = modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, i);
if (m_rowsExpanded[eventType])
itemRow = m_rowStarts[eventType]/DefaultRowHeight +
m_profilerDataModel->eventPosInType(i) + 1;
else
itemRow = m_rowStarts[eventType]/DefaultRowHeight +
m_profilerDataModel->getNestingLevel(i);
if (itemRow == row) { if (itemRow == row) {
// match // match
m_currentSelection.eventIndex = i; m_currentSelection.eventIndex = i;
m_currentSelection.startTime = m_profilerDataModel->getStartTime(i); m_currentSelection.startTime = m_profilerModelProxy->getStartTime(modelIndex, i);
m_currentSelection.endTime = m_profilerDataModel->getEndTime(i); m_currentSelection.endTime = m_profilerModelProxy->getEndTime(modelIndex, i);
m_currentSelection.row = row; m_currentSelection.row = row;
if (!m_selectionLocked) m_currentSelection.modelIndex = modelIndex;
if (!m_selectionLocked) {
setSelectedModel(modelIndex);
setSelectedItem(i); setSelectedItem(i);
}
return; return;
} }
} }
@@ -400,131 +374,189 @@ void TimelineRenderer::clearData()
m_currentSelection.endTime = -1; m_currentSelection.endTime = -1;
m_currentSelection.row = -1; m_currentSelection.row = -1;
m_currentSelection.eventIndex = -1; m_currentSelection.eventIndex = -1;
m_currentSelection.modelIndex = -1;
m_selectedItem = -1; m_selectedItem = -1;
m_selectedModel = -1;
m_selectionLocked = true; m_selectionLocked = true;
} }
qint64 TimelineRenderer::getDuration(int index) const int TimelineRenderer::getYPosition(int modelIndex, int index) const
{ {
Q_ASSERT(m_profilerDataModel); Q_ASSERT(m_profilerModelProxy);
return m_profilerDataModel->getEndTime(index) - if (index >= m_profilerModelProxy->count())
m_profilerDataModel->getStartTime(index);
}
QString TimelineRenderer::getFilename(int index) const
{
Q_ASSERT(m_profilerDataModel);
return m_profilerDataModel->getFilename(index);
}
int TimelineRenderer::getLine(int index) const
{
Q_ASSERT(m_profilerDataModel);
return m_profilerDataModel->getLine(index);
}
QString TimelineRenderer::getDetails(int index) const
{
Q_ASSERT(m_profilerDataModel);
return m_profilerDataModel->getDetails(index);
}
int TimelineRenderer::getYPosition(int index) const
{
Q_ASSERT(m_profilerDataModel);
if (index >= m_profilerDataModel->count() || m_rowStarts.isEmpty())
return 0; return 0;
int y, eventType = m_profilerDataModel->getType(index);
if (m_rowsExpanded[eventType])
y = m_rowStarts[eventType] + DefaultRowHeight *
(m_profilerDataModel->eventPosInType(index) + 1);
else
y = m_rowStarts[eventType] + DefaultRowHeight *
m_profilerDataModel->getNestingLevel(index);
return y;
}
void TimelineRenderer::setRowExpanded(int rowIndex, bool expanded) int modelRowStart = 0;
{ for (int mi = 0; mi < modelIndex; mi++)
m_rowsExpanded[rowIndex] = expanded; modelRowStart += m_profilerModelProxy->rowCount(mi);
update();
int y = DefaultRowHeight * (modelRowStart + m_profilerModelProxy->getEventRow(modelIndex, index));
return y;
} }
void TimelineRenderer::selectNext() void TimelineRenderer::selectNext()
{ {
if (m_profilerDataModel->count() == 0) if (m_profilerModelProxy->count() == 0)
return; return;
// select next in view or after qint64 searchTime = m_startTime;
int newIndex = m_selectedItem+1; if (m_selectedItem != -1)
if (newIndex >= m_profilerDataModel->count()) searchTime = m_profilerModelProxy->getStartTime(m_selectedModel, m_selectedItem);
newIndex = 0;
if (m_profilerDataModel->getEndTime(newIndex) < m_startTime) QVarLengthArray<int> itemIndexes(m_profilerModelProxy->modelCount());
newIndex = m_profilerDataModel->findFirstIndexNoParents(m_startTime); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
setSelectedItem(newIndex); if (m_profilerModelProxy->count(i) > 0) {
if (m_selectedModel == i) {
itemIndexes[i] = (m_selectedItem + 1) % m_profilerModelProxy->count(i);
} else {
if (m_profilerModelProxy->getStartTime(i, 0) > searchTime)
itemIndexes[i] = 0;
else
itemIndexes[i] = (m_profilerModelProxy->findLastIndex(i, searchTime) + 1) % m_profilerModelProxy->count(i);
}
} else {
itemIndexes[i] = -1;
}
}
int candidateModelIndex = -1;
qint64 candidateStartTime = m_profilerModelProxy->traceEndTime();
for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
if (itemIndexes[i] == -1)
continue;
qint64 newStartTime = m_profilerModelProxy->getStartTime(i, itemIndexes[i]);
if (newStartTime > searchTime && newStartTime < candidateStartTime) {
candidateStartTime = newStartTime;
candidateModelIndex = i;
}
}
int itemIndex;
if (candidateModelIndex != -1) {
itemIndex = itemIndexes[candidateModelIndex];
} else {
// find the first index of them all (todo: the modelproxy should do this)
itemIndex = -1;
candidateStartTime = m_profilerModelProxy->traceEndTime();
for (int i = 0; i < m_profilerModelProxy->modelCount(); i++)
if (m_profilerModelProxy->count(i) > 0 &&
m_profilerModelProxy->getStartTime(i,0) < candidateStartTime) {
candidateModelIndex = i;
itemIndex = 0;
candidateStartTime = m_profilerModelProxy->getStartTime(i,0);
}
}
setSelectedModel(candidateModelIndex);
setSelectedItem(itemIndex);
} }
void TimelineRenderer::selectPrev() void TimelineRenderer::selectPrev()
{ {
if (m_profilerDataModel->count() == 0) if (m_profilerModelProxy->count() == 0)
return; return;
// select last in view or before qint64 searchTime = m_endTime;
int newIndex = m_selectedItem-1; if (m_selectedItem != -1)
if (newIndex < 0) searchTime = m_profilerModelProxy->getEndTime(m_selectedModel, m_selectedItem);
newIndex = m_profilerDataModel->count()-1;
if (m_profilerDataModel->getStartTime(newIndex) > m_endTime) QVarLengthArray<int> itemIndexes(m_profilerModelProxy->modelCount());
newIndex = m_profilerDataModel->findLastIndex(m_endTime); for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
setSelectedItem(newIndex); if (m_selectedModel == i) {
itemIndexes[i] = m_selectedItem - 1;
if (itemIndexes[i] < 0)
itemIndexes[i] = m_profilerModelProxy->count(m_selectedModel) -1;
}
else
itemIndexes[i] = m_profilerModelProxy->findLastIndex(i, searchTime);
}
int candidateModelIndex = -1;
qint64 candidateStartTime = m_profilerModelProxy->traceStartTime();
for (int i = 0; i < m_profilerModelProxy->modelCount(); i++) {
if (itemIndexes[i] == -1
|| itemIndexes[i] >= m_profilerModelProxy->count(i))
continue;
qint64 newStartTime = m_profilerModelProxy->getStartTime(i, itemIndexes[i]);
if (newStartTime < searchTime && newStartTime > candidateStartTime) {
candidateStartTime = newStartTime;
candidateModelIndex = i;
}
}
int itemIndex = -1;
if (candidateModelIndex != -1) {
itemIndex = itemIndexes[candidateModelIndex];
} else {
// find the last index of them all (todo: the modelproxy should do this)
candidateModelIndex = 0;
candidateStartTime = m_profilerModelProxy->traceStartTime();
for (int i = 0; i < m_profilerModelProxy->modelCount(); i++)
if (m_profilerModelProxy->count(i) > 0 &&
m_profilerModelProxy->getStartTime(i,m_profilerModelProxy->count(i)-1) > candidateStartTime) {
candidateModelIndex = i;
itemIndex = m_profilerModelProxy->count(candidateModelIndex) - 1;
candidateStartTime = m_profilerModelProxy->getStartTime(i,m_profilerModelProxy->count(i)-1);
}
}
setSelectedModel(candidateModelIndex);
setSelectedItem(itemIndex);
} }
int TimelineRenderer::nextItemFromId(int eventId) const int TimelineRenderer::nextItemFromId(int modelIndex, int eventId) const
{ {
int ndx = -1; int ndx = -1;
if (m_selectedItem == -1) if (m_selectedItem == -1)
ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime); ndx = m_profilerModelProxy->findFirstIndexNoParents(modelIndex, m_startTime);
else else
ndx = m_selectedItem + 1; ndx = m_selectedItem + 1;
if (ndx >= m_profilerDataModel->count()) if (ndx < 0)
return -1;
if (ndx >= m_profilerModelProxy->count(modelIndex))
ndx = 0; ndx = 0;
int startIndex = ndx; int startIndex = ndx;
do { do {
if (m_profilerDataModel->getEventId(ndx) == eventId) if (m_profilerModelProxy->getEventId(modelIndex, ndx) == eventId)
return ndx; return ndx;
ndx = (ndx + 1) % m_profilerDataModel->count(); ndx = (ndx + 1) % m_profilerModelProxy->count(modelIndex);
} while (ndx != startIndex); } while (ndx != startIndex);
return -1; return -1;
} }
int TimelineRenderer::prevItemFromId(int eventId) const int TimelineRenderer::prevItemFromId(int modelIndex, int eventId) const
{ {
int ndx = -1; int ndx = -1;
if (m_selectedItem == -1) if (m_selectedItem == -1)
ndx = m_profilerDataModel->findFirstIndexNoParents(m_startTime); ndx = m_profilerModelProxy->findFirstIndexNoParents(modelIndex, m_startTime);
else else
ndx = m_selectedItem - 1; ndx = m_selectedItem - 1;
if (ndx < 0) if (ndx < 0)
ndx = m_profilerDataModel->count() - 1; ndx = m_profilerModelProxy->count(modelIndex) - 1;
int startIndex = ndx; int startIndex = ndx;
do { do {
if (m_profilerDataModel->getEventId(ndx) == eventId) if (m_profilerModelProxy->getEventId(modelIndex, ndx) == eventId)
return ndx; return ndx;
if (--ndx < 0) if (--ndx < 0)
ndx = m_profilerDataModel->count()-1; ndx = m_profilerModelProxy->count(modelIndex)-1;
} while (ndx != startIndex); } while (ndx != startIndex);
return -1; return -1;
} }
void TimelineRenderer::selectNextFromId(int eventId) void TimelineRenderer::selectNextFromId(int modelIndex, int eventId)
{ {
int eventIndex = nextItemFromId(eventId); int eventIndex = nextItemFromId(modelIndex, eventId);
if (eventIndex != -1) if (eventIndex != -1) {
setSelectedModel(modelIndex);
setSelectedItem(eventIndex); setSelectedItem(eventIndex);
}
} }
void TimelineRenderer::selectPrevFromId(int eventId) void TimelineRenderer::selectPrevFromId(int modelIndex, int eventId)
{ {
int eventIndex = prevItemFromId(eventId); int eventIndex = prevItemFromId(modelIndex, eventId);
if (eventIndex != -1) if (eventIndex != -1) {
setSelectedModel(modelIndex);
setSelectedItem(eventIndex); setSelectedItem(eventIndex);
}
} }

View File

@@ -32,7 +32,8 @@
#include <QDeclarativeItem> #include <QDeclarativeItem>
#include <QScriptValue> #include <QScriptValue>
#include "qmlprofilerdatamodel.h" #include "qmlprofilertimelinemodelproxy.h"
#include "timelinemodelaggregator.h"
namespace QmlProfiler { namespace QmlProfiler {
namespace Internal { namespace Internal {
@@ -42,9 +43,10 @@ class TimelineRenderer : public QDeclarativeItem
Q_OBJECT Q_OBJECT
Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) Q_PROPERTY(qint64 startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged)
Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged) Q_PROPERTY(qint64 endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged)
Q_PROPERTY(QObject* profilerDataModel READ profilerDataModel WRITE setProfilerDataModel NOTIFY profilerDataModelChanged) Q_PROPERTY(QObject *profilerModelProxy READ profilerModelProxy WRITE setProfilerModelProxy NOTIFY profilerModelProxyChanged)
Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged) Q_PROPERTY(bool selectionLocked READ selectionLocked WRITE setSelectionLocked NOTIFY selectionLockedChanged)
Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged) Q_PROPERTY(int selectedItem READ selectedItem WRITE setSelectedItem NOTIFY selectedItemChanged)
Q_PROPERTY(int selectedModel READ selectedModel WRITE setSelectedModel NOTIFY selectedModelChanged)
Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged) Q_PROPERTY(int startDragArea READ startDragArea WRITE setStartDragArea NOTIFY startDragAreaChanged)
Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged) Q_PROPERTY(int endDragArea READ endDragArea WRITE setEndDragArea NOTIFY endDragAreaChanged)
@@ -71,6 +73,11 @@ public:
return m_selectedItem; return m_selectedItem;
} }
int selectedModel() const
{
return m_selectedModel;
}
int startDragArea() const int startDragArea() const
{ {
return m_startDragArea; return m_startDragArea;
@@ -81,37 +88,28 @@ public:
return m_endDragArea; return m_endDragArea;
} }
QmlProfilerDataModel *profilerDataModel() const { return m_profilerDataModel; } TimelineModelAggregator *profilerModelProxy() const { return m_profilerModelProxy; }
void setProfilerDataModel(QObject *profilerDataModel) void setProfilerModelProxy(QObject *profilerModelProxy);
{
m_profilerDataModel = qobject_cast<QmlProfilerDataModel *>(profilerDataModel);
emit profilerDataModelChanged(m_profilerDataModel);
}
Q_INVOKABLE qint64 getDuration(int index) const; Q_INVOKABLE int getYPosition(int modelIndex, int index) const;
Q_INVOKABLE QString getFilename(int index) const;
Q_INVOKABLE int getLine(int index) const;
Q_INVOKABLE QString getDetails(int index) const;
Q_INVOKABLE int getYPosition(int index) const;
Q_INVOKABLE void setRowExpanded(int rowIndex, bool expanded);
Q_INVOKABLE void selectNext(); Q_INVOKABLE void selectNext();
Q_INVOKABLE void selectPrev(); Q_INVOKABLE void selectPrev();
Q_INVOKABLE int nextItemFromId(int eventId) const; Q_INVOKABLE int nextItemFromId(int modelIndex, int eventId) const;
Q_INVOKABLE int prevItemFromId(int eventId) const; Q_INVOKABLE int prevItemFromId(int modelIndex, int eventId) const;
Q_INVOKABLE void selectNextFromId(int eventId); Q_INVOKABLE void selectNextFromId(int modelIndex, int eventId);
Q_INVOKABLE void selectPrevFromId(int eventId); Q_INVOKABLE void selectPrevFromId(int modelIndex, int eventId);
signals: signals:
void startTimeChanged(qint64 arg); void startTimeChanged(qint64 arg);
void endTimeChanged(qint64 arg); void endTimeChanged(qint64 arg);
void profilerDataModelChanged(QmlProfilerDataModel *list); void profilerModelProxyChanged(TimelineModelAggregator *list);
void selectionLockedChanged(bool locked); void selectionLockedChanged(bool locked);
void selectedItemChanged(int itemIndex); void selectedItemChanged(int modelIndex, int itemIndex);
void selectedModelChanged(int modelIndex);
void startDragAreaChanged(int startDragArea); void startDragAreaChanged(int startDragArea);
void endDragAreaChanged(int endDragArea); void endDragAreaChanged(int endDragArea);
void itemPressed(int pressedItem); void itemPressed(int modelIndex, int pressedItem);
public slots: public slots:
void clearData(); void clearData();
@@ -148,7 +146,16 @@ public slots:
if (m_selectedItem != itemIndex) { if (m_selectedItem != itemIndex) {
m_selectedItem = itemIndex; m_selectedItem = itemIndex;
update(); update();
emit selectedItemChanged(itemIndex); emit selectedItemChanged(m_selectedModel, itemIndex);
}
}
void setSelectedModel(int modelIndex)
{
if (m_selectedModel != modelIndex) {
m_selectedModel = modelIndex;
update();
emit selectedModelChanged(modelIndex);
} }
} }
@@ -177,10 +184,10 @@ protected:
virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event); virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event);
private: private:
QColor colorForItem(int itemIndex); void drawItemsToPainter(QPainter *p, int modelIndex, int fromIndex, int toIndex);
void drawItemsToPainter(QPainter *p, int fromIndex, int toIndex); void drawSelectionBoxes(QPainter *p, int modelIndex, int fromIndex, int toIndex);
void drawSelectionBoxes(QPainter *p, int fromIndex, int toIndex); void drawBindingLoopMarkers(QPainter *p, int modelIndex, int fromIndex, int toIndex);
void drawBindingLoopMarkers(QPainter *p, int fromIndex, int toIndex); int modelFromPosition(int y);
void manageClicked(); void manageClicked();
void manageHovered(int x, int y); void manageHovered(int x, int y);
@@ -192,21 +199,18 @@ private:
qint64 m_lastStartTime; qint64 m_lastStartTime;
qint64 m_lastEndTime; qint64 m_lastEndTime;
QmlProfilerDataModel *m_profilerDataModel; TimelineModelAggregator *m_profilerModelProxy;
QList<int> m_rowLastX;
QList<int> m_rowStarts;
QList<int> m_rowWidths;
QList<bool> m_rowsExpanded;
struct { struct {
qint64 startTime; qint64 startTime;
qint64 endTime; qint64 endTime;
int row; int row;
int eventIndex; int eventIndex;
int modelIndex;
} m_currentSelection; } m_currentSelection;
int m_selectedItem; int m_selectedItem;
int m_selectedModel;
bool m_selectionLocked; bool m_selectionLocked;
int m_startDragArea; int m_startDragArea;
int m_endDragArea; int m_endDragArea;