From 61a5f3b3ae2338edc5923be4548a7f8dd048beda Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 19 Jul 2016 18:21:01 +0200 Subject: [PATCH] QmlProfiler: Add memory and allocations modes to flame graph view Change-Id: Ic548812378cad92d931e3fa645e11f83a861e00c Reviewed-by: Simon Hausmann --- src/plugins/qmlprofiler/flamegraphmodel.cpp | 26 +++++- src/plugins/qmlprofiler/flamegraphmodel.h | 5 ++ .../qml/QmlProfilerFlameGraphView.qml | 82 ++++++++++++++++--- 3 files changed, 97 insertions(+), 16 deletions(-) diff --git a/src/plugins/qmlprofiler/flamegraphmodel.cpp b/src/plugins/qmlprofiler/flamegraphmodel.cpp index 74eb70793a1..d63c7135ced 100644 --- a/src/plugins/qmlprofiler/flamegraphmodel.cpp +++ b/src/plugins/qmlprofiler/flamegraphmodel.cpp @@ -51,7 +51,7 @@ FlameGraphModel::FlameGraphModel(QmlProfilerModelManager *modelManager, this, [this](int typeId, int, int){loadNotes(typeId, true);}); m_modelId = modelManager->registerModelProxy(); - modelManager->announceFeatures(Constants::QML_JS_RANGE_FEATURES, + modelManager->announceFeatures(Constants::QML_JS_RANGE_FEATURES | 1 << ProfileMemory, [this](const QmlEvent &event, const QmlEventType &type) { loadEvent(event, type); }, [this](){ @@ -100,7 +100,20 @@ void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type) beginResetModel(); const QmlEvent *potentialParent = &(m_callStack.top()); - if (event.rangeStage() == RangeEnd) { + if (type.message() == MemoryAllocation) { + if (type.detailType() == HeapPage) + return; // We're only interested in actual allocations, not heap pages being mmap'd + + qint64 amount = event.number(0); + if (amount < 0) + return; // We're not interested in GC runs here + + for (FlameGraphData *data = m_stackTop; data; data = data->parent) { + ++data->allocations; + data->memory += amount; + } + + } else if (event.rangeStage() == RangeEnd) { m_stackTop->duration += event.timestamp() - potentialParent->timestamp(); m_callStack.pop(); m_stackTop = m_stackTop->parent; @@ -160,6 +173,8 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const case CallCountRole: return stats.calls; case TimePerCallRole: return stats.duration / stats.calls; case TimeInPercentRole: return stats.duration * 100 / m_stackBottom.duration; + case AllocationsRole: return stats.allocations; + case MemoryRole: return stats.memory; default: break; } @@ -184,7 +199,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const } FlameGraphData::FlameGraphData(FlameGraphData *parent, int typeIndex, qint64 duration) : - duration(duration), calls(1), typeIndex(typeIndex), parent(parent) {} + duration(duration), calls(1), memory(0), allocations(0), typeIndex(typeIndex), parent(parent) {} FlameGraphData::~FlameGraphData() { @@ -262,7 +277,10 @@ QHash FlameGraphModel::roleNames() const {NoteRole, "note"}, {TimePerCallRole, "timePerCall"}, {TimeInPercentRole, "timeInPercent"}, - {RangeTypeRole, "rangeType"} + {RangeTypeRole, "rangeType"}, + {LocationRole, "location" }, + {AllocationsRole, "allocations" }, + {MemoryRole, "memory" } }; return QAbstractItemModel::roleNames().unite(extraRoles); } diff --git a/src/plugins/qmlprofiler/flamegraphmodel.h b/src/plugins/qmlprofiler/flamegraphmodel.h index ca6c2503b68..cafc2d6c4c0 100644 --- a/src/plugins/qmlprofiler/flamegraphmodel.h +++ b/src/plugins/qmlprofiler/flamegraphmodel.h @@ -44,6 +44,9 @@ struct FlameGraphData { qint64 duration; qint64 calls; + qint64 memory; + + int allocations; int typeIndex; FlameGraphData *parent; @@ -69,6 +72,8 @@ public: TimeInPercentRole, RangeTypeRole, LocationRole, + AllocationsRole, + MemoryRole, MaxRole }; diff --git a/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml b/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml index e9e351b57c6..73b28b345bd 100644 --- a/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml +++ b/src/plugins/qmlprofiler/qml/QmlProfilerFlameGraphView.qml @@ -36,6 +36,22 @@ ScrollView { property int selectedTypeId: -1 property int visibleRangeTypes: -1 + property int sizeRole: QmlProfilerFlameGraphModel.DurationRole + + readonly property var trRoleNames: [ + QmlProfilerFlameGraphModel.DurationRole, qsTr("Total Time"), + QmlProfilerFlameGraphModel.CallCountRole, qsTr("Calls"), + QmlProfilerFlameGraphModel.DetailsRole, qsTr("Details"), + QmlProfilerFlameGraphModel.TimePerCallRole, qsTr("Mean Time"), + QmlProfilerFlameGraphModel.TimeInPercentRole, qsTr("In Percent"), + QmlProfilerFlameGraphModel.LocationRole, qsTr("Location"), + QmlProfilerFlameGraphModel.AllocationsRole, qsTr("Allocations"), + QmlProfilerFlameGraphModel.MemoryRole, qsTr("Memory") + ].reduce(function(previousValue, currentValue, currentIndex, array) { + if (currentIndex % 2 === 1) + previousValue[array[currentIndex - 1]] = array[currentIndex]; + return previousValue; + }, {}) onSelectedTypeIdChanged: tooltip.hoveredNode = null @@ -59,7 +75,7 @@ ScrollView { width: parent.width height: depth * itemHeight model: flameGraphModel - sizeRole: QmlProfilerFlameGraphModel.DurationRole + sizeRole: root.sizeRole sizeThreshold: 0.002 maximumDepth: 25 y: flickable.height > height ? flickable.height - height : 0 @@ -120,7 +136,7 @@ ScrollView { return FlameGraph.data(QmlProfilerFlameGraphModel.DetailsRole) + " (" + FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) + ", " - + FlameGraph.data(QmlProfilerFlameGraphModel.TimeInPercentRole) + "%)"; + + Math.floor(width / flamegraph.width * 1000) / 10 + "%)"; } text: textVisible ? buildText() : "" FlameGraph.onDataChanged: if (textVisible) text = buildText(); @@ -155,8 +171,8 @@ ScrollView { function note() { return FlameGraph.data(QmlProfilerFlameGraphModel.NoteRole) || ""; } function details() { var model = []; - function addDetail(name, index, format) { - model.push(name); + function addDetail(index, format) { + model.push(trRoleNames[index]); model.push(format(FlameGraph.data(index))); } @@ -182,18 +198,33 @@ ScrollView { return a + "%"; } + function printMemory(a) { + var units = ["b", "kb", "Mb", "Gb"]; + var div = 1; + for (var i = 0; i < units.length; ++i, div *= 1024) { + if (a > div * 1024) + continue; + + a /= div; + var digitsAfterDot = Math.round(3 - Math.log(a) / Math.LN10); + var multiplier = Math.pow(10, digitsAfterDot); + return Math.round(a * multiplier) / multiplier + units[i]; + } + } + if (!FlameGraph.dataValid) { model.push(qsTr("Details")); model.push(qsTr("Various Events")); } else { - addDetail(qsTr("Details"), QmlProfilerFlameGraphModel.DetailsRole, noop); - addDetail(qsTr("Type"), QmlProfilerFlameGraphModel.TypeRole, noop); - addDetail(qsTr("Calls"), QmlProfilerFlameGraphModel.CallCountRole, noop); - addDetail(qsTr("Total Time"), QmlProfilerFlameGraphModel.DurationRole, printTime); - addDetail(qsTr("Mean Time"), QmlProfilerFlameGraphModel.TimePerCallRole, printTime); - addDetail(qsTr("In Percent"), QmlProfilerFlameGraphModel.TimeInPercentRole, - addPercent); - addDetail(qsTr("Location"), QmlProfilerFlameGraphModel.LocationRole, noop); + addDetail(QmlProfilerFlameGraphModel.DetailsRole, noop); + addDetail(QmlProfilerFlameGraphModel.TypeRole, noop); + addDetail(QmlProfilerFlameGraphModel.CallCountRole, noop); + addDetail(QmlProfilerFlameGraphModel.DurationRole, printTime); + addDetail(QmlProfilerFlameGraphModel.TimePerCallRole, printTime); + addDetail(QmlProfilerFlameGraphModel.TimeInPercentRole, addPercent); + addDetail(QmlProfilerFlameGraphModel.LocationRole, noop); + addDetail(QmlProfilerFlameGraphModel.MemoryRole, printMemory); + addDetail(QmlProfilerFlameGraphModel.AllocationsRole, noop); } return model; } @@ -252,5 +283,32 @@ ScrollView { } } } + + Button { + x: flickable.width - width + y: flickable.contentY + + // It won't listen to anchors.margins and by default it doesn't add any margin. Great. + width: implicitWidth + 20 + + text: qsTr("Visualize %1").arg(trRoleNames[root.sizeRole]) + + menu: Menu { + MenuItem { + text: trRoleNames[QmlProfilerFlameGraphModel.DurationRole] + onTriggered: root.sizeRole = QmlProfilerFlameGraphModel.DurationRole + } + + MenuItem { + text: trRoleNames[QmlProfilerFlameGraphModel.MemoryRole] + onTriggered: root.sizeRole = QmlProfilerFlameGraphModel.MemoryRole + } + + MenuItem { + text: trRoleNames[QmlProfilerFlameGraphModel.AllocationsRole] + onTriggered: root.sizeRole = QmlProfilerFlameGraphModel.AllocationsRole + } + } + } } }