forked from qt-creator/qt-creator
Add flamegraph view
Change-Id: I001053b63d27adcbbdfd95abb2b45f5f5fa05447 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
This commit is contained in:
27
plugins/qmlprofiler/FlameGraphText.qml
Normal file
27
plugins/qmlprofiler/FlameGraphText.qml
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
|
220
plugins/qmlprofiler/FlameGraphView.qml
Normal file
220
plugins/qmlprofiler/FlameGraphView.qml
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
175
plugins/qmlprofiler/flamegraph.cpp
Normal file
175
plugins/qmlprofiler/flamegraph.cpp
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
153
plugins/qmlprofiler/flamegraph.h
Normal file
153
plugins/qmlprofiler/flamegraph.h
Normal file
@@ -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
|
6
plugins/qmlprofiler/flamegraph.qrc
Normal file
6
plugins/qmlprofiler/flamegraph.qrc
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>FlameGraphView.qml</file>
|
||||||
|
<file>FlameGraphText.qml</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
@@ -231,6 +231,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
|
|||||||
case Line: return type.location.line;
|
case Line: return type.location.line;
|
||||||
case Column: return type.location.column;
|
case Column: return type.location.column;
|
||||||
case Type: return nameForType(type.rangeType);
|
case Type: return nameForType(type.rangeType);
|
||||||
|
case RangeType: return type.rangeType;
|
||||||
case Details: return type.data.isEmpty() ?
|
case Details: return type.data.isEmpty() ?
|
||||||
FlameGraphModel::tr("Source code not available") : type.data;
|
FlameGraphModel::tr("Source code not available") : type.data;
|
||||||
default: return QVariant();
|
default: return QVariant();
|
||||||
@@ -320,6 +321,7 @@ QHash<int, QByteArray> FlameGraphModel::roleNames() const
|
|||||||
names[Note] = "note";
|
names[Note] = "note";
|
||||||
names[TimePerCall] = "timePerCall";
|
names[TimePerCall] = "timePerCall";
|
||||||
names[TimeInPercent] = "timeInPercent";
|
names[TimeInPercent] = "timeInPercent";
|
||||||
|
names[RangeType] = "rangeType";
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -72,6 +72,7 @@ public:
|
|||||||
Note,
|
Note,
|
||||||
TimePerCall,
|
TimePerCall,
|
||||||
TimeInPercent,
|
TimeInPercent,
|
||||||
|
RangeType,
|
||||||
MaxRole
|
MaxRole
|
||||||
};
|
};
|
||||||
|
|
||||||
|
109
plugins/qmlprofiler/flamegraphview.cpp
Normal file
109
plugins/qmlprofiler/flamegraphview.cpp
Normal file
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
57
plugins/qmlprofiler/flamegraphview.h
Normal file
57
plugins/qmlprofiler/flamegraphview.h
Normal file
@@ -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
|
@@ -4,6 +4,8 @@ TEMPLATE = lib
|
|||||||
PROVIDER = Digia
|
PROVIDER = Digia
|
||||||
include(../../qtcreatorplugin.pri)
|
include(../../qtcreatorplugin.pri)
|
||||||
|
|
||||||
|
QT += qml quick quickwidgets
|
||||||
|
|
||||||
DEFINES += QMLPROFILEREXTENSION_LIBRARY
|
DEFINES += QMLPROFILEREXTENSION_LIBRARY
|
||||||
CONFIG(licensechecker): DEFINES += LICENSECHECKER
|
CONFIG(licensechecker): DEFINES += LICENSECHECKER
|
||||||
|
|
||||||
@@ -16,6 +18,8 @@ SOURCES += qmlprofilerextensionplugin.cpp \
|
|||||||
inputeventsmodel.cpp \
|
inputeventsmodel.cpp \
|
||||||
debugmessagesmodel.cpp \
|
debugmessagesmodel.cpp \
|
||||||
flamegraphmodel.cpp \
|
flamegraphmodel.cpp \
|
||||||
|
flamegraphview.cpp \
|
||||||
|
flamegraph.cpp
|
||||||
|
|
||||||
HEADERS += qmlprofilerextensionplugin.h \
|
HEADERS += qmlprofilerextensionplugin.h \
|
||||||
qmlprofilerextension_global.h \
|
qmlprofilerextension_global.h \
|
||||||
@@ -26,6 +30,11 @@ HEADERS += qmlprofilerextensionplugin.h \
|
|||||||
inputeventsmodel.h \
|
inputeventsmodel.h \
|
||||||
debugmessagesmodel.h \
|
debugmessagesmodel.h \
|
||||||
flamegraphmodel.h \
|
flamegraphmodel.h \
|
||||||
|
flamegraphview.h \
|
||||||
|
flamegraph.h
|
||||||
|
|
||||||
OTHER_FILES += \
|
OTHER_FILES += \
|
||||||
QmlProfilerExtension.json.in
|
QmlProfilerExtension.json.in
|
||||||
|
|
||||||
|
RESOURCES += \
|
||||||
|
flamegraph.qrc
|
||||||
|
@@ -12,8 +12,12 @@ QtcCommercialPlugin {
|
|||||||
files: [
|
files: [
|
||||||
"debugmessagesmodel.cpp",
|
"debugmessagesmodel.cpp",
|
||||||
"debugmessagesmodel.h",
|
"debugmessagesmodel.h",
|
||||||
|
"flamegraph.cpp",
|
||||||
|
"flamegraph.h",
|
||||||
"flamegraphmodel.cpp",
|
"flamegraphmodel.cpp",
|
||||||
"flamegraphmodel.h",
|
"flamegraphmodel.h",
|
||||||
|
"flamegraphview.cpp",
|
||||||
|
"flamegraphview.h",
|
||||||
"inputeventsmodel.cpp",
|
"inputeventsmodel.cpp",
|
||||||
"inputeventsmodel.h",
|
"inputeventsmodel.h",
|
||||||
"memoryusagemodel.cpp",
|
"memoryusagemodel.cpp",
|
||||||
|
@@ -46,6 +46,7 @@
|
|||||||
#include "memoryusagemodel.h"
|
#include "memoryusagemodel.h"
|
||||||
#include "inputeventsmodel.h"
|
#include "inputeventsmodel.h"
|
||||||
#include "debugmessagesmodel.h"
|
#include "debugmessagesmodel.h"
|
||||||
|
#include "flamegraphview.h"
|
||||||
|
|
||||||
using namespace QmlProfilerExtension::Internal;
|
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()
|
QmlProfilerExtensionPlugin::QmlProfilerExtensionPlugin()
|
||||||
{
|
{
|
||||||
// Create your members
|
// Create your members
|
||||||
@@ -93,8 +106,10 @@ bool QmlProfilerExtensionPlugin::initialize(const QStringList &arguments, QStrin
|
|||||||
= ExtensionSystem::PluginManager::getObject<LicenseChecker::LicenseCheckerPlugin>();
|
= ExtensionSystem::PluginManager::getObject<LicenseChecker::LicenseCheckerPlugin>();
|
||||||
|
|
||||||
if (licenseChecker && licenseChecker->hasValidLicense()) {
|
if (licenseChecker && licenseChecker->hasValidLicense()) {
|
||||||
if (licenseChecker->enterpriseFeatures())
|
if (licenseChecker->enterpriseFeatures()) {
|
||||||
addAutoReleasedObject(new ModelFactory);
|
addAutoReleasedObject(new ModelFactory);
|
||||||
|
addAutoReleasedObject(new ViewFactory);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Invalid license, disabling QML Profiler Enterprise features";
|
qWarning() << "Invalid license, disabling QML Profiler Enterprise features";
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user