forked from qt-creator/qt-creator
The memory profiler aggregates allocations originating from the same QML/JS constructs into one event. This change makes sure the origins are really the same calls, not only the same functions and gives some more details on hom much memory was allocated or deallocated in how many steps. Change-Id: I784c521855122f7b3b894417de0afb216766e2d4 Reviewed-by: Kai Koehne <kai.koehne@digia.com>
315 lines
10 KiB
C++
315 lines
10 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2013 Digia Plc
|
|
** All rights reserved.
|
|
** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
|
|
**
|
|
** 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 Digia.
|
|
**
|
|
** If you have questions regarding the use of this file, please use
|
|
** contact form at http://qt.digia.com <http://qt.digia.com/>
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "memoryusagemodel.h"
|
|
#include "qmldebug/qmlprofilereventtypes.h"
|
|
#include "qmlprofiler/qmlprofilermodelmanager.h"
|
|
#include "qmlprofiler/sortedtimelinemodel.h"
|
|
#include "qmlprofiler/abstracttimelinemodel_p.h"
|
|
|
|
#include <QStack>
|
|
|
|
namespace QmlProfilerExtension {
|
|
namespace Internal {
|
|
|
|
using namespace QmlProfiler;
|
|
|
|
class MemoryUsageModel::MemoryUsageModelPrivate :
|
|
public SortedTimelineModel<MemoryAllocation,
|
|
AbstractTimelineModel::AbstractTimelineModelPrivate>
|
|
{
|
|
public:
|
|
static QString memoryTypeName(int type);
|
|
|
|
qint64 maxSize;
|
|
private:
|
|
Q_DECLARE_PUBLIC(MemoryUsageModel)
|
|
};
|
|
|
|
MemoryUsageModel::MemoryUsageModel(QObject *parent)
|
|
: AbstractTimelineModel(new MemoryUsageModelPrivate(),
|
|
QLatin1String("MemoryUsageTimelineModel"),
|
|
QLatin1String("Memory Usage"), QmlDebug::MemoryAllocation,
|
|
QmlDebug::MaximumRangeType, parent)
|
|
{
|
|
}
|
|
|
|
int MemoryUsageModel::rowCount() const
|
|
{
|
|
return isEmpty() ? 1 : 3;
|
|
}
|
|
|
|
int MemoryUsageModel::rowMaxValue(int rowNumber) const
|
|
{
|
|
Q_D(const MemoryUsageModel);
|
|
Q_UNUSED(rowNumber);
|
|
return d->maxSize;
|
|
}
|
|
|
|
int MemoryUsageModel::getEventRow(int index) const
|
|
{
|
|
Q_D(const MemoryUsageModel);
|
|
QmlDebug::MemoryType type = d->range(index).type;
|
|
if (type == QmlDebug::HeapPage || type == QmlDebug::LargeItem)
|
|
return 1;
|
|
else
|
|
return 2;
|
|
}
|
|
|
|
int MemoryUsageModel::getEventId(int index) const
|
|
{
|
|
Q_D(const MemoryUsageModel);
|
|
return d->range(index).type;
|
|
}
|
|
|
|
QColor MemoryUsageModel::getColor(int index) const
|
|
{
|
|
return getEventColor(index);
|
|
}
|
|
|
|
float MemoryUsageModel::getHeight(int index) const
|
|
{
|
|
Q_D(const MemoryUsageModel);
|
|
return qMin(1.0f, (float)d->range(index).size / (float)d->maxSize);
|
|
}
|
|
|
|
const QVariantMap MemoryUsageModel::getEventLocation(int index) const
|
|
{
|
|
static const QLatin1String file("file");
|
|
static const QLatin1String line("line");
|
|
static const QLatin1String column("column");
|
|
|
|
Q_D(const MemoryUsageModel);
|
|
QVariantMap result;
|
|
|
|
int originType = d->range(index).originTypeIndex;
|
|
if (originType > -1) {
|
|
const QmlDebug::QmlEventLocation &location =
|
|
d->modelManager->qmlModel()->getEventTypes().at(originType).location;
|
|
|
|
result.insert(file, location.filename);
|
|
result.insert(line, location.line);
|
|
result.insert(column, location.column);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const QVariantList MemoryUsageModel::getLabels() const
|
|
{
|
|
Q_D(const MemoryUsageModel);
|
|
QVariantList result;
|
|
|
|
if (d->expanded && !isEmpty()) {
|
|
{
|
|
QVariantMap element;
|
|
element.insert(QLatin1String("description"), QVariant(tr("Memory Allocation")));
|
|
|
|
element.insert(QLatin1String("id"), QVariant(QmlDebug::HeapPage));
|
|
result << element;
|
|
}
|
|
|
|
{
|
|
QVariantMap element;
|
|
element.insert(QLatin1String("description"), QVariant(tr("Memory Usage")));
|
|
|
|
element.insert(QLatin1String("id"), QVariant(QmlDebug::SmallItem));
|
|
result << element;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const QVariantList MemoryUsageModel::getEventDetails(int index) const
|
|
{
|
|
Q_D(const MemoryUsageModel);
|
|
static QString title = QStringLiteral("title");
|
|
|
|
QVariantList result;
|
|
const MemoryUsageModelPrivate::Range *ev = &d->range(index);
|
|
|
|
QVariantMap res;
|
|
if (ev->allocated >= -ev->deallocated)
|
|
res.insert(title, tr("Memory Allocated"));
|
|
else
|
|
res.insert(title, tr("Memory Freed"));
|
|
result << res;
|
|
res.clear();
|
|
|
|
res.insert(tr("Total"), QVariant(QString::fromLatin1("%1 bytes").arg(ev->size)));
|
|
result << res;
|
|
res.clear();
|
|
|
|
if (ev->allocations > 0) {
|
|
res.insert(tr("Allocated"), QString::fromLatin1("%1 bytes").arg(ev->allocated));
|
|
res.insert(tr("Allocations"), QString::number(ev->allocations));
|
|
}
|
|
if (ev->deallocations > 0) {
|
|
res.insert(tr("Deallocated"), QString::fromLatin1("%1 bytes").arg(-ev->deallocated));
|
|
res.insert(tr("Deallocations"), QString::number(ev->deallocations));
|
|
}
|
|
res.insert(tr("Type"), QVariant(MemoryUsageModelPrivate::memoryTypeName(ev->type)));
|
|
if (ev->originTypeIndex != -1) {
|
|
res.insert(tr("Location"),
|
|
d->modelManager->qmlModel()->getEventTypes().at(ev->originTypeIndex).displayName);
|
|
}
|
|
result << res;
|
|
return result;
|
|
}
|
|
|
|
struct RangeStackFrame {
|
|
RangeStackFrame() : originTypeIndex(-1), startTime(-1), endTime(-1) {}
|
|
RangeStackFrame(int originTypeIndex, qint64 startTime, qint64 endTime) :
|
|
originTypeIndex(originTypeIndex), startTime(startTime), endTime(endTime) {}
|
|
int originTypeIndex;
|
|
qint64 startTime;
|
|
qint64 endTime;
|
|
};
|
|
|
|
void MemoryUsageModel::loadData()
|
|
{
|
|
Q_D(MemoryUsageModel);
|
|
clear();
|
|
QmlProfilerDataModel *simpleModel = d->modelManager->qmlModel();
|
|
if (simpleModel->isEmpty())
|
|
return;
|
|
|
|
qint64 currentSize = 0;
|
|
qint64 currentUsage = 0;
|
|
int currentUsageIndex = -1;
|
|
int currentJSHeapIndex = -1;
|
|
|
|
QStack<RangeStackFrame> rangeStack;
|
|
MemoryAllocation dummy;
|
|
|
|
const QVector<QmlProfilerDataModel::QmlEventTypeData> &types = simpleModel->getEventTypes();
|
|
foreach (const QmlProfilerDataModel::QmlEventData &event, simpleModel->getEvents()) {
|
|
const QmlProfilerDataModel::QmlEventTypeData &type = types[event.typeIndex];
|
|
while (!rangeStack.empty() && rangeStack.top().endTime < event.startTime)
|
|
rangeStack.pop();
|
|
if (!eventAccepted(type)) {
|
|
if (type.rangeType != QmlDebug::MaximumRangeType) {
|
|
rangeStack.push(RangeStackFrame(event.typeIndex, event.startTime,
|
|
event.startTime + event.duration));
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (type.detailType == QmlDebug::SmallItem || type.detailType == QmlDebug::LargeItem) {
|
|
MemoryAllocation &last = currentUsageIndex > -1 ? d->data(currentUsageIndex) : dummy;
|
|
if (!rangeStack.empty() && type.detailType == last.type &&
|
|
last.originTypeIndex == rangeStack.top().originTypeIndex &&
|
|
rangeStack.top().startTime < d->range(currentUsageIndex).start) {
|
|
last.update(event.numericData1);
|
|
currentUsage = last.size;
|
|
} else {
|
|
MemoryAllocation allocation(QmlDebug::SmallItem, currentUsage,
|
|
rangeStack.empty() ? -1 : rangeStack.top().originTypeIndex);
|
|
allocation.update(event.numericData1);
|
|
currentUsage = allocation.size;
|
|
|
|
if (currentUsageIndex != -1) {
|
|
d->insertEnd(currentUsageIndex,
|
|
event.startTime - d->range(currentUsageIndex).start - 1);
|
|
}
|
|
currentUsageIndex = d->insertStart(event.startTime, allocation);
|
|
}
|
|
}
|
|
|
|
if (type.detailType == QmlDebug::HeapPage || type.detailType == QmlDebug::LargeItem) {
|
|
MemoryAllocation &last = currentJSHeapIndex > -1 ? d->data(currentJSHeapIndex) : dummy;
|
|
if (!rangeStack.empty() && type.detailType == last.type &&
|
|
last.originTypeIndex == rangeStack.top().originTypeIndex &&
|
|
rangeStack.top().startTime < d->range(currentJSHeapIndex).start) {
|
|
last.update(event.numericData1);
|
|
currentSize = last.size;
|
|
} else {
|
|
MemoryAllocation allocation((QmlDebug::MemoryType)type.detailType, currentSize,
|
|
rangeStack.empty() ? -1 : rangeStack.top().originTypeIndex);
|
|
allocation.update(event.numericData1);
|
|
currentSize = allocation.size;
|
|
|
|
if (currentSize > d->maxSize)
|
|
d->maxSize = currentSize;
|
|
if (currentJSHeapIndex != -1)
|
|
d->insertEnd(currentJSHeapIndex,
|
|
event.startTime - d->range(currentJSHeapIndex).start - 1);
|
|
currentJSHeapIndex = d->insertStart(event.startTime, allocation);
|
|
}
|
|
}
|
|
|
|
d->modelManager->modelProxyCountUpdated(d->modelId, d->count(), simpleModel->getEvents().count());
|
|
}
|
|
|
|
if (currentJSHeapIndex != -1)
|
|
d->insertEnd(currentJSHeapIndex, traceEndTime() -
|
|
d->range(currentJSHeapIndex).start - 1);
|
|
if (currentUsageIndex != -1)
|
|
d->insertEnd(currentUsageIndex, traceEndTime() -
|
|
d->range(currentUsageIndex).start - 1);
|
|
|
|
|
|
d->computeNesting();
|
|
d->modelManager->modelProxyCountUpdated(d->modelId, 1, 1);
|
|
}
|
|
|
|
void MemoryUsageModel::clear()
|
|
{
|
|
Q_D(MemoryUsageModel);
|
|
d->SortedTimelineModel::clear();
|
|
d->expanded = false;
|
|
d->maxSize = 1;
|
|
|
|
d->modelManager->modelProxyCountUpdated(d->modelId, 0, 1);
|
|
}
|
|
|
|
QString MemoryUsageModel::MemoryUsageModelPrivate::memoryTypeName(int type)
|
|
{
|
|
switch (type) {
|
|
case QmlDebug::HeapPage: return tr("Heap Allocation");
|
|
case QmlDebug::LargeItem: return tr("Large Item Allocation");
|
|
case QmlDebug::SmallItem: return tr("Heap Usage");
|
|
case QmlDebug::MaximumMemoryType: return tr("Total");
|
|
default: return tr("Unknown");
|
|
}
|
|
}
|
|
|
|
MemoryUsageModel::MemoryAllocation::MemoryAllocation(QmlDebug::MemoryType type, qint64 baseAmount,
|
|
int originTypeIndex) :
|
|
type(type), size(baseAmount), allocated(0), deallocated(0), allocations(0), deallocations(0),
|
|
originTypeIndex(originTypeIndex)
|
|
{
|
|
}
|
|
|
|
void MemoryUsageModel::MemoryAllocation::update(qint64 amount)
|
|
{
|
|
size += amount;
|
|
if (amount < 0) {
|
|
deallocated += amount;
|
|
++deallocations;
|
|
} else {
|
|
allocated += amount;
|
|
++allocations;
|
|
}
|
|
}
|
|
|
|
|
|
} // namespace Internal
|
|
} // namespace QmlProfilerExtension
|