forked from qt-creator/qt-creator
QmlProfiler: Add memory and allocations modes to flame graph view
Change-Id: Ic548812378cad92d931e3fa645e11f83a861e00c Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user