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);});
|
this, [this](int typeId, int, int){loadNotes(typeId, true);});
|
||||||
m_modelId = modelManager->registerModelProxy();
|
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) {
|
[this](const QmlEvent &event, const QmlEventType &type) {
|
||||||
loadEvent(event, type);
|
loadEvent(event, type);
|
||||||
}, [this](){
|
}, [this](){
|
||||||
@@ -100,7 +100,20 @@ void FlameGraphModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
|
|||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|
||||||
const QmlEvent *potentialParent = &(m_callStack.top());
|
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_stackTop->duration += event.timestamp() - potentialParent->timestamp();
|
||||||
m_callStack.pop();
|
m_callStack.pop();
|
||||||
m_stackTop = m_stackTop->parent;
|
m_stackTop = m_stackTop->parent;
|
||||||
@@ -160,6 +173,8 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
|
|||||||
case CallCountRole: return stats.calls;
|
case CallCountRole: return stats.calls;
|
||||||
case TimePerCallRole: return stats.duration / stats.calls;
|
case TimePerCallRole: return stats.duration / stats.calls;
|
||||||
case TimeInPercentRole: return stats.duration * 100 / m_stackBottom.duration;
|
case TimeInPercentRole: return stats.duration * 100 / m_stackBottom.duration;
|
||||||
|
case AllocationsRole: return stats.allocations;
|
||||||
|
case MemoryRole: return stats.memory;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +199,7 @@ QVariant FlameGraphModel::lookup(const FlameGraphData &stats, int role) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
FlameGraphData::FlameGraphData(FlameGraphData *parent, int typeIndex, qint64 duration) :
|
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()
|
FlameGraphData::~FlameGraphData()
|
||||||
{
|
{
|
||||||
@@ -262,7 +277,10 @@ QHash<int, QByteArray> FlameGraphModel::roleNames() const
|
|||||||
{NoteRole, "note"},
|
{NoteRole, "note"},
|
||||||
{TimePerCallRole, "timePerCall"},
|
{TimePerCallRole, "timePerCall"},
|
||||||
{TimeInPercentRole, "timeInPercent"},
|
{TimeInPercentRole, "timeInPercent"},
|
||||||
{RangeTypeRole, "rangeType"}
|
{RangeTypeRole, "rangeType"},
|
||||||
|
{LocationRole, "location" },
|
||||||
|
{AllocationsRole, "allocations" },
|
||||||
|
{MemoryRole, "memory" }
|
||||||
};
|
};
|
||||||
return QAbstractItemModel::roleNames().unite(extraRoles);
|
return QAbstractItemModel::roleNames().unite(extraRoles);
|
||||||
}
|
}
|
||||||
|
@@ -44,6 +44,9 @@ struct FlameGraphData {
|
|||||||
|
|
||||||
qint64 duration;
|
qint64 duration;
|
||||||
qint64 calls;
|
qint64 calls;
|
||||||
|
qint64 memory;
|
||||||
|
|
||||||
|
int allocations;
|
||||||
int typeIndex;
|
int typeIndex;
|
||||||
|
|
||||||
FlameGraphData *parent;
|
FlameGraphData *parent;
|
||||||
@@ -69,6 +72,8 @@ public:
|
|||||||
TimeInPercentRole,
|
TimeInPercentRole,
|
||||||
RangeTypeRole,
|
RangeTypeRole,
|
||||||
LocationRole,
|
LocationRole,
|
||||||
|
AllocationsRole,
|
||||||
|
MemoryRole,
|
||||||
MaxRole
|
MaxRole
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -36,6 +36,22 @@ ScrollView {
|
|||||||
|
|
||||||
property int selectedTypeId: -1
|
property int selectedTypeId: -1
|
||||||
property int visibleRangeTypes: -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
|
onSelectedTypeIdChanged: tooltip.hoveredNode = null
|
||||||
|
|
||||||
@@ -59,7 +75,7 @@ ScrollView {
|
|||||||
width: parent.width
|
width: parent.width
|
||||||
height: depth * itemHeight
|
height: depth * itemHeight
|
||||||
model: flameGraphModel
|
model: flameGraphModel
|
||||||
sizeRole: QmlProfilerFlameGraphModel.DurationRole
|
sizeRole: root.sizeRole
|
||||||
sizeThreshold: 0.002
|
sizeThreshold: 0.002
|
||||||
maximumDepth: 25
|
maximumDepth: 25
|
||||||
y: flickable.height > height ? flickable.height - height : 0
|
y: flickable.height > height ? flickable.height - height : 0
|
||||||
@@ -120,7 +136,7 @@ ScrollView {
|
|||||||
|
|
||||||
return FlameGraph.data(QmlProfilerFlameGraphModel.DetailsRole) + " ("
|
return FlameGraph.data(QmlProfilerFlameGraphModel.DetailsRole) + " ("
|
||||||
+ FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) + ", "
|
+ FlameGraph.data(QmlProfilerFlameGraphModel.TypeRole) + ", "
|
||||||
+ FlameGraph.data(QmlProfilerFlameGraphModel.TimeInPercentRole) + "%)";
|
+ Math.floor(width / flamegraph.width * 1000) / 10 + "%)";
|
||||||
}
|
}
|
||||||
text: textVisible ? buildText() : ""
|
text: textVisible ? buildText() : ""
|
||||||
FlameGraph.onDataChanged: if (textVisible) text = buildText();
|
FlameGraph.onDataChanged: if (textVisible) text = buildText();
|
||||||
@@ -155,8 +171,8 @@ ScrollView {
|
|||||||
function note() { return FlameGraph.data(QmlProfilerFlameGraphModel.NoteRole) || ""; }
|
function note() { return FlameGraph.data(QmlProfilerFlameGraphModel.NoteRole) || ""; }
|
||||||
function details() {
|
function details() {
|
||||||
var model = [];
|
var model = [];
|
||||||
function addDetail(name, index, format) {
|
function addDetail(index, format) {
|
||||||
model.push(name);
|
model.push(trRoleNames[index]);
|
||||||
model.push(format(FlameGraph.data(index)));
|
model.push(format(FlameGraph.data(index)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,18 +198,33 @@ ScrollView {
|
|||||||
return a + "%";
|
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) {
|
if (!FlameGraph.dataValid) {
|
||||||
model.push(qsTr("Details"));
|
model.push(qsTr("Details"));
|
||||||
model.push(qsTr("Various Events"));
|
model.push(qsTr("Various Events"));
|
||||||
} else {
|
} else {
|
||||||
addDetail(qsTr("Details"), QmlProfilerFlameGraphModel.DetailsRole, noop);
|
addDetail(QmlProfilerFlameGraphModel.DetailsRole, noop);
|
||||||
addDetail(qsTr("Type"), QmlProfilerFlameGraphModel.TypeRole, noop);
|
addDetail(QmlProfilerFlameGraphModel.TypeRole, noop);
|
||||||
addDetail(qsTr("Calls"), QmlProfilerFlameGraphModel.CallCountRole, noop);
|
addDetail(QmlProfilerFlameGraphModel.CallCountRole, noop);
|
||||||
addDetail(qsTr("Total Time"), QmlProfilerFlameGraphModel.DurationRole, printTime);
|
addDetail(QmlProfilerFlameGraphModel.DurationRole, printTime);
|
||||||
addDetail(qsTr("Mean Time"), QmlProfilerFlameGraphModel.TimePerCallRole, printTime);
|
addDetail(QmlProfilerFlameGraphModel.TimePerCallRole, printTime);
|
||||||
addDetail(qsTr("In Percent"), QmlProfilerFlameGraphModel.TimeInPercentRole,
|
addDetail(QmlProfilerFlameGraphModel.TimeInPercentRole, addPercent);
|
||||||
addPercent);
|
addDetail(QmlProfilerFlameGraphModel.LocationRole, noop);
|
||||||
addDetail(qsTr("Location"), QmlProfilerFlameGraphModel.LocationRole, noop);
|
addDetail(QmlProfilerFlameGraphModel.MemoryRole, printMemory);
|
||||||
|
addDetail(QmlProfilerFlameGraphModel.AllocationsRole, noop);
|
||||||
}
|
}
|
||||||
return model;
|
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