Commit 61a5f3b3 authored by Ulf Hermann's avatar Ulf Hermann

QmlProfiler: Add memory and allocations modes to flame graph view

Change-Id: Ic548812378cad92d931e3fa645e11f83a861e00c
Reviewed-by: Simon Hausmann's avatarSimon Hausmann <simon.hausmann@qt.io>
parent 22813861
......@@ -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<qint64>(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<int, QByteArray> FlameGraphModel::roleNames() const
{NoteRole, "note"},
{TimePerCallRole, "timePerCall"},
{TimeInPercentRole, "timeInPercent"},
{RangeTypeRole, "rangeType"}
{RangeTypeRole, "rangeType"},
{LocationRole, "location" },
{AllocationsRole, "allocations" },
{MemoryRole, "memory" }
};
return QAbstractItemModel::roleNames().unite(extraRoles);
}
......
......@@ -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
};
......
......@@ -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
}
}
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment