aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@theqtcompany.com>2015-12-11 11:09:29 +0100
committerUlf Hermann <ulf.hermann@theqtcompany.com>2016-01-06 14:20:47 +0000
commit4e29a946c786d3ca62215c473609f35ce2c35008 (patch)
tree5b7b80f469a67caaa6e3988dd160d23e3f1345f3
parentb85598ecd3c2d39e6a0f4bdf88eb6a1a186a19de (diff)
Add flamegraph view
Change-Id: I001053b63d27adcbbdfd95abb2b45f5f5fa05447 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
-rw-r--r--plugins/qmlprofiler/FlameGraphText.qml27
-rw-r--r--plugins/qmlprofiler/FlameGraphView.qml220
-rw-r--r--plugins/qmlprofiler/flamegraph.cpp175
-rw-r--r--plugins/qmlprofiler/flamegraph.h153
-rw-r--r--plugins/qmlprofiler/flamegraph.qrc6
-rw-r--r--plugins/qmlprofiler/flamegraphmodel.cpp2
-rw-r--r--plugins/qmlprofiler/flamegraphmodel.h1
-rw-r--r--plugins/qmlprofiler/flamegraphview.cpp109
-rw-r--r--plugins/qmlprofiler/flamegraphview.h57
-rw-r--r--plugins/qmlprofiler/qmlprofiler.pro9
-rw-r--r--plugins/qmlprofiler/qmlprofiler.qbs4
-rw-r--r--plugins/qmlprofiler/qmlprofilerextensionplugin.cpp17
12 files changed, 779 insertions, 1 deletions
diff --git a/plugins/qmlprofiler/FlameGraphText.qml b/plugins/qmlprofiler/FlameGraphText.qml
new file mode 100644
index 0000000000..acc0f555e1
--- /dev/null
+++ b/plugins/qmlprofiler/FlameGraphText.qml
@@ -0,0 +1,27 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Text {
+ font.pixelSize: 12
+ font.family: "sans-serif"
+ textFormat: Text.PlainText
+ renderType: Text.NativeRendering
+}
+
diff --git a/plugins/qmlprofiler/FlameGraphView.qml b/plugins/qmlprofiler/FlameGraphView.qml
new file mode 100644
index 0000000000..ecb011d2eb
--- /dev/null
+++ b/plugins/qmlprofiler/FlameGraphView.qml
@@ -0,0 +1,220 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Controls 1.4
+import FlameGraph 1.0
+import FlameGraphModel 1.0
+
+ScrollView {
+ id: root
+ signal typeSelected(int typeIndex)
+ signal gotoSourceLocation(string filename, int line, int column)
+
+ property int selectedTypeId: -1
+ property int visibleRangeTypes: -1
+
+ Flickable {
+ id: flickable
+ contentHeight: flamegraph.height
+ boundsBehavior: Flickable.StopAtBounds
+
+ FlameGraph {
+ property int itemHeight: Math.max(30, flickable.height / depth)
+ property int level: -1
+ property color blue: "blue"
+ property color blue1: Qt.lighter(blue)
+ property color blue2: Qt.rgba(0.375, 0, 1, 1)
+ property color grey1: "#B0B0B0"
+ property color grey2: "#A0A0A0"
+
+ id: flamegraph
+ width: parent.width
+ height: depth * itemHeight
+ model: flameGraphModel
+ sizeRole: FlameGraphModel.Duration
+ sizeThreshold: 0.002
+ y: flickable.height > height ? flickable.height - height : 0
+
+ delegate: Item {
+ id: flamegraphItem
+ property int level: parent.level + (rangeTypeVisible ? 1 : 0)
+ property bool isSelected: flamegraphItem.FlameGraph.data(FlameGraphModel.TypeId) ===
+ root.selectedTypeId
+ property bool rangeTypeVisible: root.visibleRangeTypes &
+ (1 << FlameGraph.data(FlameGraphModel.RangeType))
+
+ Rectangle {
+ border.color: {
+ if (flamegraphItem.isSelected)
+ return flamegraph.blue2;
+ else if (mouseArea.containsMouse)
+ return flamegraph.blue1;
+ else
+ return flamegraph.grey1
+ }
+ border.width: (mouseArea.containsMouse || flamegraphItem.isSelected) ? 2 : 1
+ color: Qt.hsla((level % 12) / 72, 0.9 + Math.random() / 10,
+ 0.45 + Math.random() / 10, 0.9 + Math.random() / 10);
+ height: flamegraphItem.rangeTypeVisible ? flamegraph.itemHeight : 0;
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+
+ FlameGraphText {
+ id: text
+ visible: width > 20
+ anchors.fill: parent
+ anchors.margins: 5
+ verticalAlignment: Text.AlignVCenter
+ horizontalAlignment: Text.AlignHCenter
+ text: visible ? buildText() : ""
+ elide: Text.ElideRight
+ wrapMode: Text.WrapAtWordBoundaryOrAnywhere
+
+ function buildText() {
+ if (!flamegraphItem.FlameGraph.dataValid)
+ return "<others>";
+
+ return flamegraphItem.FlameGraph.data(FlameGraphModel.Details) + " (" +
+ flamegraphItem.FlameGraph.data(FlameGraphModel.Type) + ", " +
+ flamegraphItem.FlameGraph.data(FlameGraphModel.TimeInPercent) + "%)";
+ }
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ hoverEnabled: true
+
+ function printTime(t)
+ {
+ if (t <= 0)
+ return "0";
+ if (t < 1000)
+ return t + " ns";
+ t = Math.floor(t / 1000);
+ if (t < 1000)
+ return t + " μs";
+ if (t < 1e6)
+ return (t / 1000) + " ms";
+ return (t / 1e6) + " s";
+ }
+
+ onEntered: {
+ var model = [];
+ function addDetail(name, index, format) {
+ model.push(name + ":");
+ model.push(format(flamegraphItem.FlameGraph.data(index)));
+ }
+
+ function noop(a) {
+ return a;
+ }
+
+ function addPercent(a) {
+ return a + "%";
+ }
+
+ if (!flamegraphItem.FlameGraph.dataValid) {
+ model.push(qsTr("Details") + ":");
+ model.push(qsTr("Various Events"));
+ } else {
+ addDetail(qsTr("Details"), FlameGraphModel.Details, noop);
+ addDetail(qsTr("Type"), FlameGraphModel.Type, noop);
+ addDetail(qsTr("Calls"), FlameGraphModel.CallCount, noop);
+ addDetail(qsTr("Total Time"), FlameGraphModel.Duration, printTime);
+ addDetail(qsTr("Mean Time"), FlameGraphModel.TimePerCall, printTime);
+ addDetail(qsTr("In Percent"), FlameGraphModel.TimeInPercent,
+ addPercent);
+
+ }
+ tooltip.model = model;
+ }
+
+ onExited: {
+ tooltip.model = [];
+ }
+
+ onClicked: {
+ if (flamegraphItem.FlameGraph.dataValid) {
+ root.typeSelected(
+ flamegraphItem.FlameGraph.data(FlameGraphModel.TypeId));
+ root.gotoSourceLocation(
+ flamegraphItem.FlameGraph.data(FlameGraphModel.Filename),
+ flamegraphItem.FlameGraph.data(FlameGraphModel.Line),
+ flamegraphItem.FlameGraph.data(FlameGraphModel.Column));
+ }
+ }
+ }
+ }
+
+ FlameGraph.onDataChanged: if (text.visible) text.text = text.buildText();
+
+ height: flamegraph.height - level * flamegraph.itemHeight;
+ width: parent === null ? flamegraph.width : parent.width * FlameGraph.relativeSize
+ x: parent === null ? 0 : parent.width * FlameGraph.relativePosition
+ }
+ }
+
+ Rectangle {
+ color: "white"
+ border.width: 1
+ border.color: flamegraph.grey2
+ width: tooltip.model.length > 0 ? tooltip.width + 10 : 0
+ height: tooltip.model.length > 0 ? tooltip.height + 10 : 0
+ y: flickable.contentY
+ x: anchorRight ? parent.width - width : 0
+ property bool anchorRight: true
+
+ Grid {
+ id: tooltip
+ anchors.margins: 5
+ anchors.top: parent.top
+ anchors.left: parent.left
+ spacing: 5
+ columns: 2
+ property var model: [ qsTr("No data available") ]
+
+ Connections {
+ target: flameGraphModel
+ onModelReset: {
+ tooltip.model = (flameGraphModel.rowCount() === 0) ?
+ [ qsTr("No data available") ] : [];
+ }
+ }
+
+ Repeater {
+ model: parent.model
+ FlameGraphText {
+ text: modelData
+ font.bold: (index % 2) === 0
+ width: Math.min(implicitWidth, 200)
+ elide: Text.ElideRight
+ }
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ hoverEnabled: true
+ onEntered: parent.anchorRight = !parent.anchorRight
+ }
+ }
+ }
+}
diff --git a/plugins/qmlprofiler/flamegraph.cpp b/plugins/qmlprofiler/flamegraph.cpp
new file mode 100644
index 0000000000..77c176272d
--- /dev/null
+++ b/plugins/qmlprofiler/flamegraph.cpp
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+#include "flamegraph.h"
+
+namespace QmlProfilerExtension {
+namespace Internal {
+
+FlameGraph::FlameGraph(QQuickItem *parent) :
+ QQuickItem(parent), m_delegate(0), m_model(0), m_depth(0), m_sizeThreshold(0)
+{
+}
+
+QQmlComponent *FlameGraph::delegate() const
+{
+ return m_delegate;
+}
+
+void FlameGraph::setDelegate(QQmlComponent *delegate)
+{
+ if (delegate != m_delegate) {
+ m_delegate = delegate;
+ emit delegateChanged(delegate);
+ }
+}
+
+QAbstractItemModel *FlameGraph::model() const
+{
+ return m_model;
+}
+
+void FlameGraph::setModel(QAbstractItemModel *model)
+{
+ if (model != m_model) {
+ if (m_model)
+ disconnect(m_model, &QAbstractItemModel::modelReset, this, &FlameGraph::rebuild);
+
+ m_model = model;
+ connect(m_model, &QAbstractItemModel::modelReset, this, &FlameGraph::rebuild);
+ emit modelChanged(model);
+ rebuild();
+ }
+}
+
+int FlameGraph::sizeRole() const
+{
+ return m_sizeRole;
+}
+
+void FlameGraph::setSizeRole(int sizeRole)
+{
+ if (sizeRole != m_sizeRole) {
+ m_sizeRole = sizeRole;
+ emit sizeRoleChanged(sizeRole);
+ rebuild();
+ }
+}
+
+qreal FlameGraph::sizeThreshold() const
+{
+ return m_sizeThreshold;
+}
+
+void FlameGraph::setSizeThreshold(qreal sizeThreshold)
+{
+ if (sizeThreshold != m_sizeThreshold) {
+ m_sizeThreshold = sizeThreshold;
+ emit sizeThresholdChanged(sizeThreshold);
+ rebuild();
+ }
+}
+
+int FlameGraph::depth() const
+{
+ return m_depth;
+}
+
+FlameGraphAttached *FlameGraph::qmlAttachedProperties(QObject *object)
+{
+ FlameGraphAttached *attached =
+ object->findChild<FlameGraphAttached *>(QString(), Qt::FindDirectChildrenOnly);
+ if (!attached)
+ attached = new FlameGraphAttached(object);
+ return attached;
+}
+
+QObject *FlameGraph::appendChild(QObject *parentObject, QQuickItem *parentItem,
+ QQmlContext *context, const QModelIndex &childIndex,
+ qreal position, qreal size)
+{
+ QObject *childObject = m_delegate->beginCreate(context);
+ if (parentItem) {
+ QQuickItem *childItem = qobject_cast<QQuickItem *>(childObject);
+ if (childItem)
+ childItem->setParentItem(parentItem);
+ }
+ childObject->setParent(parentObject);
+ FlameGraphAttached *attached = FlameGraph::qmlAttachedProperties(childObject);
+ attached->setRelativePosition(position);
+ attached->setRelativeSize(size);
+ attached->setModelIndex(childIndex);
+ m_delegate->completeCreate();
+ return childObject;
+}
+
+
+int FlameGraph::buildNode(const QModelIndex &parentIndex, QObject *parentObject, int depth)
+{
+ qreal position = 0;
+ qreal skipped = 0;
+ qreal parentSize = m_model->data(parentIndex, m_sizeRole).toReal();
+ QQuickItem *parentItem = qobject_cast<QQuickItem *>(parentObject);
+ QQmlContext *context = qmlContext(this);
+ int rowCount = m_model->rowCount(parentIndex);
+ int childrenDepth = depth;
+ for (int row = 0; row < rowCount; ++row) {
+ QModelIndex childIndex = m_model->index(row, 0, parentIndex);
+ qreal size = m_model->data(childIndex, m_sizeRole).toReal();
+ if (size / m_model->data(QModelIndex(), m_sizeRole).toReal() < m_sizeThreshold) {
+ skipped += size;
+ continue;
+ }
+
+ QObject *childObject = appendChild(parentObject, parentItem, context, childIndex,
+ position / parentSize, size / parentSize);
+ position += size;
+ childrenDepth = qMax(childrenDepth, buildNode(childIndex, childObject, depth + 1));
+ }
+
+ if (skipped > 0) {
+ appendChild(parentObject, parentItem, context, QModelIndex(), position / parentSize,
+ skipped / parentSize);
+ childrenDepth = qMax(childrenDepth, depth + 1);
+ }
+
+ return childrenDepth;
+}
+
+void FlameGraph::rebuild()
+{
+ qDeleteAll(childItems());
+ childItems().clear();
+ m_depth = 0;
+
+ if (!m_model) {
+ emit depthChanged(m_depth);
+ return;
+ }
+
+ m_depth = buildNode(QModelIndex(), this, 0);
+ emit depthChanged(m_depth);
+}
+
+QVariant FlameGraphAttached::data(int role) const
+{
+ return m_data.isValid() ? m_data.data(role) : QVariant();
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/flamegraph.h b/plugins/qmlprofiler/flamegraph.h
new file mode 100644
index 0000000000..193fe2346d
--- /dev/null
+++ b/plugins/qmlprofiler/flamegraph.h
@@ -0,0 +1,153 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+#ifndef FLAMEGRAPH_H
+#define FLAMEGRAPH_H
+
+#include <QQuickItem>
+#include <QAbstractItemModel>
+
+namespace QmlProfilerExtension {
+namespace Internal {
+
+class FlameGraphAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal relativeSize READ relativeSize WRITE setRelativeSize
+ NOTIFY relativeSizeChanged)
+ Q_PROPERTY(qreal relativePosition READ relativePosition WRITE setRelativePosition
+ NOTIFY relativePositionChanged)
+ Q_PROPERTY(bool dataValid READ isDataValid NOTIFY dataValidChanged)
+
+public:
+ FlameGraphAttached(QObject *parent = 0) :
+ QObject(parent), m_relativeSize(0), m_relativePosition(0) {}
+
+ Q_INVOKABLE QVariant data(int role) const;
+
+ bool isDataValid() const
+ {
+ return m_data.isValid();
+ }
+
+ qreal relativeSize() const
+ {
+ return m_relativeSize;
+ }
+
+ void setRelativeSize(qreal relativeSize)
+ {
+ if (relativeSize != m_relativeSize) {
+ m_relativeSize = relativeSize;
+ emit relativeSizeChanged();
+ }
+ }
+
+ qreal relativePosition() const
+ {
+ return m_relativePosition;
+ }
+
+ void setRelativePosition(qreal relativePosition)
+ {
+ if (relativePosition != m_relativePosition) {
+ m_relativePosition = relativePosition;
+ emit relativePositionChanged();
+ }
+ }
+
+ void setModelIndex(const QModelIndex &data)
+ {
+ if (data != m_data) {
+ bool validChanged = (data.isValid() != m_data.isValid());
+ m_data = data;
+ if (validChanged)
+ emit dataValidChanged();
+ emit dataChanged();
+ }
+ }
+
+signals:
+ void dataChanged();
+ void dataValidChanged();
+ void relativeSizeChanged();
+ void relativePositionChanged();
+
+private:
+ QPersistentModelIndex m_data;
+ qreal m_relativeSize;
+ qreal m_relativePosition;
+};
+
+class FlameGraph : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
+ Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(int sizeRole READ sizeRole WRITE setSizeRole NOTIFY sizeRoleChanged)
+ Q_PROPERTY(qreal sizeThreshold READ sizeThreshold WRITE setSizeThreshold
+ NOTIFY sizeThresholdChanged)
+ Q_PROPERTY(int depth READ depth NOTIFY depthChanged)
+
+public:
+ FlameGraph(QQuickItem *parent = 0);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *delegate);
+
+ QAbstractItemModel *model() const;
+ void setModel(QAbstractItemModel *model);
+
+ int sizeRole() const;
+ void setSizeRole(int sizeRole);
+
+ qreal sizeThreshold() const;
+ void setSizeThreshold(qreal sizeThreshold);
+
+ int depth() const;
+
+ static FlameGraphAttached *qmlAttachedProperties(QObject *object);
+
+signals:
+ void delegateChanged(QQmlComponent *delegate);
+ void modelChanged(QAbstractItemModel *model);
+ void sizeRoleChanged(int role);
+ void sizeThresholdChanged(qreal threshold);
+ void depthChanged(int depth);
+
+private slots:
+ void rebuild();
+
+private:
+ QQmlComponent *m_delegate;
+ QAbstractItemModel *m_model;
+ int m_sizeRole;
+ int m_depth;
+ qreal m_sizeThreshold;
+
+ int buildNode(const QModelIndex &parentIndex, QObject *parentObject, int depth);
+ QObject *appendChild(QObject *parentObject, QQuickItem *parentItem, QQmlContext *context,
+ const QModelIndex &childIndex, qreal position, qreal size);
+};
+
+}
+}
+
+QML_DECLARE_TYPEINFO(QmlProfilerExtension::Internal::FlameGraph, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // FLAMEGRAPH_H
diff --git a/plugins/qmlprofiler/flamegraph.qrc b/plugins/qmlprofiler/flamegraph.qrc
new file mode 100644
index 0000000000..abf7393f4e
--- /dev/null
+++ b/plugins/qmlprofiler/flamegraph.qrc
@@ -0,0 +1,6 @@
+<RCC>
+ <qresource prefix="/">
+ <file>FlameGraphView.qml</file>
+ <file>FlameGraphText.qml</file>
+ </qresource>
+</RCC>
diff --git a/plugins/qmlprofiler/flamegraphmodel.cpp b/plugins/qmlprofiler/flamegraphmodel.cpp
index 16783913e7..b31ad74938 100644
--- a/plugins/qmlprofiler/flamegraphmodel.cpp
+++ b/plugins/qmlprofiler/flamegraphmodel.cpp
@@ -231,6 +231,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
case Line: return type.location.line;
case Column: return type.location.column;
case Type: return nameForType(type.rangeType);
+ case RangeType: return type.rangeType;
case Details: return type.data.isEmpty() ?
FlameGraphModel::tr("Source code not available") : type.data;
default: return QVariant();
@@ -320,6 +321,7 @@ QHash<int, QByteArray> FlameGraphModel::roleNames() const
names[Note] = "note";
names[TimePerCall] = "timePerCall";
names[TimeInPercent] = "timeInPercent";
+ names[RangeType] = "rangeType";
return names;
}
diff --git a/plugins/qmlprofiler/flamegraphmodel.h b/plugins/qmlprofiler/flamegraphmodel.h
index 6f41bf21d1..df0081df8b 100644
--- a/plugins/qmlprofiler/flamegraphmodel.h
+++ b/plugins/qmlprofiler/flamegraphmodel.h
@@ -72,6 +72,7 @@ public:
Note,
TimePerCall,
TimeInPercent,
+ RangeType,
MaxRole
};
diff --git a/plugins/qmlprofiler/flamegraphview.cpp b/plugins/qmlprofiler/flamegraphview.cpp
new file mode 100644
index 0000000000..1f28e46999
--- /dev/null
+++ b/plugins/qmlprofiler/flamegraphview.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+#include "flamegraphview.h"
+#include "flamegraph.h"
+
+#include <qmlprofiler/qmlprofilerconstants.h>
+#include <qmlprofiler/qmlprofilertool.h>
+
+#include <QQmlContext>
+#include <QVBoxLayout>
+#include <QMenu>
+
+namespace QmlProfilerExtension {
+namespace Internal {
+
+FlameGraphView::FlameGraphView(QWidget *parent, QmlProfiler::QmlProfilerModelManager *manager) :
+ QmlProfilerEventsView(parent), m_content(new QQuickWidget(this)),
+ m_model(new FlameGraphModel(manager, this)), m_isRestrictedToRange(false)
+{
+ setWindowTitle(QStringLiteral("Flamegraph"));
+ setObjectName(QStringLiteral("QmlProfilerFlamegraph"));
+
+ qmlRegisterType<FlameGraph>("FlameGraph", 1, 0, "FlameGraph");
+ qmlRegisterUncreatableType<FlameGraphModel>("FlameGraphModel", 1, 0, "FlameGraphModel",
+ QLatin1String("use the context property"));
+
+ m_content->rootContext()->setContextProperty(QStringLiteral("flameGraphModel"), m_model);
+ m_content->setSource(QUrl(QStringLiteral("qrc:/FlameGraphView.qml")));
+ m_content->setClearColor(QColor(0xdc, 0xdc, 0xdc));
+
+ m_content->setResizeMode(QQuickWidget::SizeRootObjectToView);
+ m_content->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+ QLayout *layout = new QVBoxLayout(this);
+ layout->setContentsMargins(0, 0, 0, 0);
+ layout->setSpacing(0);
+ layout->addWidget(m_content);
+ setLayout(layout);
+
+ connect(m_content->rootObject(), SIGNAL(typeSelected(int)),
+ this, SIGNAL(typeSelected(int)));
+ connect(m_content->rootObject(), SIGNAL(gotoSourceLocation(QString,int,int)),
+ this, SIGNAL(gotoSourceLocation(QString,int,int)));
+}
+
+void FlameGraphView::clear()
+{
+}
+
+void FlameGraphView::restrictToRange(qint64 rangeStart, qint64 rangeEnd)
+{
+ m_isRestrictedToRange = (rangeStart != -1 || rangeEnd != -1);
+ m_model->loadData(rangeStart, rangeEnd);
+}
+
+bool FlameGraphView::isRestrictedToRange() const
+{
+ return m_isRestrictedToRange;
+}
+
+void FlameGraphView::selectByTypeId(int typeIndex)
+{
+ m_content->rootObject()->setProperty("selectedTypeId", typeIndex);
+}
+
+void FlameGraphView::onVisibleFeaturesChanged(quint64 features)
+{
+ int rangeTypeMask;
+ for (int rangeType = 0; rangeType < QmlDebug::MaximumRangeType; ++rangeType) {
+ if (features & (1 << QmlDebug::featureFromRangeType(QmlDebug::RangeType(rangeType))))
+ rangeTypeMask |= (1 << rangeType);
+ }
+ m_content->rootObject()->setProperty("visibleRangeTypes", rangeTypeMask);
+}
+
+void FlameGraphView::contextMenuEvent(QContextMenuEvent *ev)
+{
+ QMenu menu;
+ QAction *getGlobalStatsAction = 0;
+
+ QPoint position = ev->globalPos();
+
+ menu.addActions(QmlProfiler::QmlProfilerTool::profilerContextMenuActions());
+ menu.addSeparator();
+ getGlobalStatsAction = menu.addAction(tr("Show Full Range"));
+ if (!isRestrictedToRange())
+ getGlobalStatsAction->setEnabled(false);
+
+ if (menu.exec(position) == getGlobalStatsAction)
+ emit showFullRange();
+}
+
+}
+}
diff --git a/plugins/qmlprofiler/flamegraphview.h b/plugins/qmlprofiler/flamegraphview.h
new file mode 100644
index 0000000000..6cc63eb37c
--- /dev/null
+++ b/plugins/qmlprofiler/flamegraphview.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd
+** All rights reserved.
+** For any questions to The Qt Company, please use contact form at http://www.qt.io/contact-us
+**
+** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
+**
+** Licensees holding valid Qt Enterprise licenses may use this file in
+** accordance with the Qt Enterprise License Agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company.
+**
+** If you have questions regarding the use of this file, please use
+** contact form at http://www.qt.io/contact-us
+**
+****************************************************************************/
+
+#ifndef FLAMEGRAPHVIEW_H
+#define FLAMEGRAPHVIEW_H
+
+#include "flamegraphmodel.h"
+
+#include "qmlprofiler/qmlprofilereventsview.h"
+#include <QWidget>
+#include <QQuickWidget>
+
+namespace QmlProfilerExtension {
+namespace Internal {
+
+class FlameGraphView : public QmlProfiler::QmlProfilerEventsView
+{
+ Q_OBJECT
+public:
+ FlameGraphView(QWidget *parent, QmlProfiler::QmlProfilerModelManager *manager);
+
+ void clear() override;
+ void restrictToRange(qint64 rangeStart, qint64 rangeEnd) override;
+ bool isRestrictedToRange() const override;
+
+public slots:
+ void selectByTypeId(int typeIndex) override;
+ void onVisibleFeaturesChanged(quint64 features) override;
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *ev) override;
+
+private:
+ QQuickWidget *m_content;
+ FlameGraphModel *m_model;
+ bool m_isRestrictedToRange;
+};
+
+}
+}
+
+#endif // FLAMEGRAPHVIEW_H
diff --git a/plugins/qmlprofiler/qmlprofiler.pro b/plugins/qmlprofiler/qmlprofiler.pro
index ec7591fbee..b1af0ea190 100644
--- a/plugins/qmlprofiler/qmlprofiler.pro
+++ b/plugins/qmlprofiler/qmlprofiler.pro
@@ -4,6 +4,8 @@ TEMPLATE = lib
PROVIDER = Digia
include(../../qtcreatorplugin.pri)
+QT += qml quick quickwidgets
+
DEFINES += QMLPROFILEREXTENSION_LIBRARY
CONFIG(licensechecker): DEFINES += LICENSECHECKER
@@ -16,6 +18,8 @@ SOURCES += qmlprofilerextensionplugin.cpp \
inputeventsmodel.cpp \
debugmessagesmodel.cpp \
flamegraphmodel.cpp \
+ flamegraphview.cpp \
+ flamegraph.cpp
HEADERS += qmlprofilerextensionplugin.h \
qmlprofilerextension_global.h \
@@ -26,6 +30,11 @@ HEADERS += qmlprofilerextensionplugin.h \
inputeventsmodel.h \
debugmessagesmodel.h \
flamegraphmodel.h \
+ flamegraphview.h \
+ flamegraph.h
OTHER_FILES += \
QmlProfilerExtension.json.in
+
+RESOURCES += \
+ flamegraph.qrc
diff --git a/plugins/qmlprofiler/qmlprofiler.qbs b/plugins/qmlprofiler/qmlprofiler.qbs
index 223698bbf3..1c1a3ccf3b 100644
--- a/plugins/qmlprofiler/qmlprofiler.qbs
+++ b/plugins/qmlprofiler/qmlprofiler.qbs
@@ -12,8 +12,12 @@ QtcCommercialPlugin {
files: [
"debugmessagesmodel.cpp",
"debugmessagesmodel.h",
+ "flamegraph.cpp",
+ "flamegraph.h",
"flamegraphmodel.cpp",
"flamegraphmodel.h",
+ "flamegraphview.cpp",
+ "flamegraphview.h",
"inputeventsmodel.cpp",
"inputeventsmodel.h",
"memoryusagemodel.cpp",
diff --git a/plugins/qmlprofiler/qmlprofilerextensionplugin.cpp b/plugins/qmlprofiler/qmlprofilerextensionplugin.cpp
index b4ab349d05..1eaacd7356 100644
--- a/plugins/qmlprofiler/qmlprofilerextensionplugin.cpp
+++ b/plugins/qmlprofiler/qmlprofilerextensionplugin.cpp
@@ -46,6 +46,7 @@
#include "memoryusagemodel.h"
#include "inputeventsmodel.h"
#include "debugmessagesmodel.h"
+#include "flamegraphview.h"
using namespace QmlProfilerExtension::Internal;
@@ -65,6 +66,18 @@ public:
}
};
+class ViewFactory : public QmlProfiler::QmlProfilerEventsViewFactory {
+ Q_OBJECT
+public:
+ QList<QmlProfiler::QmlProfilerEventsView *> create(
+ QWidget *parent, QmlProfiler::QmlProfilerModelManager *manager) override
+ {
+ QList<QmlProfiler::QmlProfilerEventsView *> views;
+ views << new FlameGraphView(parent, manager);
+ return views;
+ }
+};
+
QmlProfilerExtensionPlugin::QmlProfilerExtensionPlugin()
{
// Create your members
@@ -93,8 +106,10 @@ bool QmlProfilerExtensionPlugin::initialize(const QStringList &arguments, QStrin
= ExtensionSystem::PluginManager::getObject<LicenseChecker::LicenseCheckerPlugin>();
if (licenseChecker && licenseChecker->hasValidLicense()) {
- if (licenseChecker->enterpriseFeatures())
+ if (licenseChecker->enterpriseFeatures()) {
addAutoReleasedObject(new ModelFactory);
+ addAutoReleasedObject(new ViewFactory);
+ }
} else {
qWarning() << "Invalid license, disabling QML Profiler Enterprise features";
}