From 4e29a946c786d3ca62215c473609f35ce2c35008 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 11 Dec 2015 11:09:29 +0100 Subject: Add flamegraph view Change-Id: I001053b63d27adcbbdfd95abb2b45f5f5fa05447 Reviewed-by: Joerg Bornemann --- plugins/qmlprofiler/FlameGraphText.qml | 27 +++ plugins/qmlprofiler/FlameGraphView.qml | 220 +++++++++++++++++++++ plugins/qmlprofiler/flamegraph.cpp | 175 ++++++++++++++++ plugins/qmlprofiler/flamegraph.h | 153 ++++++++++++++ plugins/qmlprofiler/flamegraph.qrc | 6 + plugins/qmlprofiler/flamegraphmodel.cpp | 2 + plugins/qmlprofiler/flamegraphmodel.h | 1 + plugins/qmlprofiler/flamegraphview.cpp | 109 ++++++++++ plugins/qmlprofiler/flamegraphview.h | 57 ++++++ plugins/qmlprofiler/qmlprofiler.pro | 9 + plugins/qmlprofiler/qmlprofiler.qbs | 4 + plugins/qmlprofiler/qmlprofilerextensionplugin.cpp | 17 +- 12 files changed, 779 insertions(+), 1 deletion(-) create mode 100644 plugins/qmlprofiler/FlameGraphText.qml create mode 100644 plugins/qmlprofiler/FlameGraphView.qml create mode 100644 plugins/qmlprofiler/flamegraph.cpp create mode 100644 plugins/qmlprofiler/flamegraph.h create mode 100644 plugins/qmlprofiler/flamegraph.qrc create mode 100644 plugins/qmlprofiler/flamegraphview.cpp create mode 100644 plugins/qmlprofiler/flamegraphview.h 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 ""; + + 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(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(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(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 +#include + +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 @@ + + + FlameGraphView.qml + FlameGraphText.qml + + 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 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 +#include + +#include +#include +#include + +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", 1, 0, "FlameGraph"); + qmlRegisterUncreatableType("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 +#include + +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 create( + QWidget *parent, QmlProfiler::QmlProfilerModelManager *manager) override + { + QList 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(); 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"; } -- cgit v1.2.3