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")
Depends { name: "cpp" }
Depends { name: "Qt"; submodules: ["gui", "network"] }
files: [

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

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

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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