forked from qt-creator/qt-creator
QmlProfiler: Eliminate QStandardItemModel from statistics
This resolves the data duplication between model manager and statistics models, that gets in the way of updating the details strings on the fly. Task-number: QTCREATORBUG-20106 Change-Id: I1c4881f903402dff936267de03b84d05ab7c39ca Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
@@ -105,11 +105,13 @@ void QmlProfilerStatisticsModel::restrictToFeatures(quint64 features)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
|
beginResetModel();
|
||||||
if (!m_modelManager->replayEvents(m_modelManager->traceTime()->startTime(),
|
if (!m_modelManager->replayEvents(m_modelManager->traceTime()->startTime(),
|
||||||
m_modelManager->traceTime()->endTime(),
|
m_modelManager->traceTime()->endTime(),
|
||||||
std::bind(&QmlProfilerStatisticsModel::loadEvent,
|
std::bind(&QmlProfilerStatisticsModel::loadEvent,
|
||||||
this, std::placeholders::_1,
|
this, std::placeholders::_1,
|
||||||
std::placeholders::_2))) {
|
std::placeholders::_2))) {
|
||||||
|
endResetModel();
|
||||||
emit m_modelManager->error(tr("Could not re-read events from temporary trace file."));
|
emit m_modelManager->error(tr("Could not re-read events from temporary trace file."));
|
||||||
clear();
|
clear();
|
||||||
} else {
|
} else {
|
||||||
@@ -195,6 +197,7 @@ QString QmlProfilerStatisticsModel::summary(const QVector<int> &typeIds) const
|
|||||||
|
|
||||||
void QmlProfilerStatisticsModel::clear()
|
void QmlProfilerStatisticsModel::clear()
|
||||||
{
|
{
|
||||||
|
beginResetModel();
|
||||||
m_rootDuration = 0;
|
m_rootDuration = 0;
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
m_notes.clear();
|
m_notes.clear();
|
||||||
@@ -204,6 +207,7 @@ void QmlProfilerStatisticsModel::clear()
|
|||||||
m_calleesModel->clear();
|
m_calleesModel->clear();
|
||||||
if (!m_callersModel.isNull())
|
if (!m_callersModel.isNull())
|
||||||
m_callersModel->clear();
|
m_callersModel->clear();
|
||||||
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsModel::setRelativesModel(QmlProfilerStatisticsRelativesModel *relative,
|
void QmlProfilerStatisticsModel::setRelativesModel(QmlProfilerStatisticsRelativesModel *relative,
|
||||||
@@ -400,12 +404,21 @@ QVariant QmlProfilerStatisticsModel::headerData(int section, Qt::Orientation ori
|
|||||||
|
|
||||||
void QmlProfilerStatisticsModel::modelManagerStateChanged()
|
void QmlProfilerStatisticsModel::modelManagerStateChanged()
|
||||||
{
|
{
|
||||||
if (m_modelManager->state() == QmlProfilerModelManager::ClearingData)
|
switch (m_modelManager->state()) {
|
||||||
|
case QmlProfilerModelManager::ClearingData:
|
||||||
clear();
|
clear();
|
||||||
|
break;
|
||||||
|
case QmlProfilerModelManager::AcquiringData:
|
||||||
|
beginResetModel();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsModel::notesChanged(int typeIndex)
|
void QmlProfilerStatisticsModel::notesChanged(int typeIndex)
|
||||||
{
|
{
|
||||||
|
static const QVector<int> noteRoles({Qt::ToolTipRole, Qt::TextColorRole});
|
||||||
const QmlProfilerNotesModel *notesModel = m_modelManager->notesModel();
|
const QmlProfilerNotesModel *notesModel = m_modelManager->notesModel();
|
||||||
if (typeIndex == -1) {
|
if (typeIndex == -1) {
|
||||||
m_notes.clear();
|
m_notes.clear();
|
||||||
@@ -418,6 +431,7 @@ void QmlProfilerStatisticsModel::notesChanged(int typeIndex)
|
|||||||
} else {
|
} else {
|
||||||
note.append(QStringLiteral("\n")).append(notesModel->text(noteId));
|
note.append(QStringLiteral("\n")).append(notesModel->text(noteId));
|
||||||
}
|
}
|
||||||
|
emit dataChanged(index(noteType, 0), index(noteType, MainDetails), noteRoles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -430,6 +444,7 @@ void QmlProfilerStatisticsModel::notesChanged(int typeIndex)
|
|||||||
newNotes << notesModel->text(it->toInt());
|
newNotes << notesModel->text(it->toInt());
|
||||||
}
|
}
|
||||||
m_notes[typeIndex] = newNotes.join(QStringLiteral("\n"));
|
m_notes[typeIndex] = newNotes.join(QStringLiteral("\n"));
|
||||||
|
emit dataChanged(index(typeIndex, 0), index(typeIndex, MainDetails), noteRoles);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +507,7 @@ void QmlProfilerStatisticsModel::finalize()
|
|||||||
{
|
{
|
||||||
for (QmlEventStats &stats : m_data)
|
for (QmlEventStats &stats : m_data)
|
||||||
stats.finalize();
|
stats.finalize();
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
emit dataAvailable();
|
emit dataAvailable();
|
||||||
}
|
}
|
||||||
@@ -711,6 +727,21 @@ QVariant QmlProfilerStatisticsRelativesModel::headerData(int section, Qt::Orient
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool QmlProfilerStatisticsRelativesModel::setData(const QModelIndex &index, const QVariant &value,
|
||||||
|
int role)
|
||||||
|
{
|
||||||
|
bool ok = false;
|
||||||
|
const int typeIndex = value.toInt(&ok);
|
||||||
|
if (index.isValid() || !ok || role != TypeIdRole) {
|
||||||
|
return QAbstractTableModel::setData(index, value, role);
|
||||||
|
} else {
|
||||||
|
beginResetModel();
|
||||||
|
m_relativeTypeIndex = typeIndex;
|
||||||
|
endResetModel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int QmlProfilerStatisticsRelativesModel::count() const
|
int QmlProfilerStatisticsRelativesModel::count() const
|
||||||
{
|
{
|
||||||
return m_data.count();
|
return m_data.count();
|
||||||
@@ -718,10 +749,12 @@ int QmlProfilerStatisticsRelativesModel::count() const
|
|||||||
|
|
||||||
void QmlProfilerStatisticsRelativesModel::clear()
|
void QmlProfilerStatisticsRelativesModel::clear()
|
||||||
{
|
{
|
||||||
|
beginResetModel();
|
||||||
m_relativeTypeIndex = std::numeric_limits<int>::max();
|
m_relativeTypeIndex = std::numeric_limits<int>::max();
|
||||||
m_data.clear();
|
m_data.clear();
|
||||||
m_callStack.clear();
|
m_callStack.clear();
|
||||||
m_compileStack.clear();
|
m_compileStack.clear();
|
||||||
|
endResetModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace QmlProfiler
|
} // namespace QmlProfiler
|
||||||
|
@@ -218,6 +218,9 @@ public:
|
|||||||
int columnCount(const QModelIndex &parent) const override;
|
int columnCount(const QModelIndex &parent) const override;
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
|
||||||
|
|
||||||
|
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void dataAvailable();
|
void dataAvailable();
|
||||||
|
|
||||||
|
@@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QStandardItem>
|
|
||||||
#include <QHeaderView>
|
#include <QHeaderView>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QClipboard>
|
#include <QClipboard>
|
||||||
@@ -41,6 +40,7 @@
|
|||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QHBoxLayout>
|
#include <QHBoxLayout>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
@@ -49,64 +49,6 @@ namespace Internal {
|
|||||||
|
|
||||||
const int DEFAULT_SORT_COLUMN = MainTimeInPercent;
|
const int DEFAULT_SORT_COLUMN = MainTimeInPercent;
|
||||||
|
|
||||||
struct SortPreserver {
|
|
||||||
SortPreserver(Utils::TreeView *view) : view(view)
|
|
||||||
{
|
|
||||||
const QHeaderView *header = view->header();
|
|
||||||
column = header->sortIndicatorSection();
|
|
||||||
order = header->sortIndicatorOrder();
|
|
||||||
view->setSortingEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
~SortPreserver()
|
|
||||||
{
|
|
||||||
view->setSortingEnabled(true);
|
|
||||||
view->sortByColumn(column, order);
|
|
||||||
}
|
|
||||||
|
|
||||||
int column = DEFAULT_SORT_COLUMN;
|
|
||||||
Qt::SortOrder order = Qt::DescendingOrder;
|
|
||||||
Utils::TreeView *view = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Colors {
|
|
||||||
QColor noteBackground = QColor("orange");
|
|
||||||
QColor defaultBackground = QColor("white");
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RootEventType : public QmlEventType {
|
|
||||||
RootEventType() : QmlEventType(MaximumMessage, MaximumRangeType, -1,
|
|
||||||
QmlEventLocation("<program>", 1, 1),
|
|
||||||
QmlProfilerStatisticsMainView::tr("Main Program"),
|
|
||||||
QmlProfilerStatisticsMainView::tr("<program>"))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_GLOBAL_STATIC(Colors, colors)
|
|
||||||
Q_GLOBAL_STATIC(RootEventType, rootEventType)
|
|
||||||
|
|
||||||
class StatisticsViewItem : public QStandardItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
StatisticsViewItem(const QString &text, const QVariant &sort) : QStandardItem(text)
|
|
||||||
{
|
|
||||||
setData(sort, SortRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool operator<(const QStandardItem &other) const
|
|
||||||
{
|
|
||||||
if (data(SortRole).type() == QVariant::String) {
|
|
||||||
// Strings should be case-insensitive compared
|
|
||||||
return data(SortRole).toString().compare(other.data(SortRole).toString(),
|
|
||||||
Qt::CaseInsensitive) < 0;
|
|
||||||
} else {
|
|
||||||
// For everything else the standard comparison should be OK
|
|
||||||
return QStandardItem::operator<(other);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static void setViewDefaults(Utils::TreeView *view)
|
static void setViewDefaults(Utils::TreeView *view)
|
||||||
{
|
{
|
||||||
view->setFrameStyle(QFrame::NoFrame);
|
view->setFrameStyle(QFrame::NoFrame);
|
||||||
@@ -116,68 +58,12 @@ static void setViewDefaults(Utils::TreeView *view)
|
|||||||
header->setMinimumSectionSize(50);
|
header->setMinimumSectionSize(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString displayHeader(MainField header)
|
static void getSourceLocation(const QModelIndex &index,
|
||||||
{
|
|
||||||
switch (header) {
|
|
||||||
case MainCallCount:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Calls");
|
|
||||||
case MainDetails:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Details");
|
|
||||||
case MainLocation:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Location");
|
|
||||||
case MainMaxTime:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Longest Time");
|
|
||||||
case MainTimePerCall:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Mean Time");
|
|
||||||
case MainSelfTime:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Self Time");
|
|
||||||
case MainSelfTimeInPercent:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Self Time in Percent");
|
|
||||||
case MainMinTime:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Shortest Time");
|
|
||||||
case MainTimeInPercent:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Time in Percent");
|
|
||||||
case MainTotalTime:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Total Time");
|
|
||||||
case MainType:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Type");
|
|
||||||
case MainMedianTime:
|
|
||||||
return QmlProfilerStatisticsMainView::tr("Median Time");
|
|
||||||
case MaxMainField:
|
|
||||||
QTC_ASSERT(false, break);
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString displayHeader(RelativeField header, QmlProfilerStatisticsRelation relation)
|
|
||||||
{
|
|
||||||
switch (header) {
|
|
||||||
case RelativeLocation:
|
|
||||||
return relation == QmlProfilerStatisticsCallees
|
|
||||||
? QmlProfilerStatisticsMainView::tr("Callee")
|
|
||||||
: QmlProfilerStatisticsMainView::tr("Caller");
|
|
||||||
case RelativeType:
|
|
||||||
return displayHeader(MainType);
|
|
||||||
case RelativeTotalTime:
|
|
||||||
return displayHeader(MainTotalTime);
|
|
||||||
case RelativeCallCount:
|
|
||||||
return displayHeader(MainCallCount);
|
|
||||||
case RelativeDetails:
|
|
||||||
return relation == QmlProfilerStatisticsCallees
|
|
||||||
? QmlProfilerStatisticsMainView::tr("Callee Description")
|
|
||||||
: QmlProfilerStatisticsMainView::tr("Caller Description");
|
|
||||||
case MaxRelativeField:
|
|
||||||
QTC_ASSERT(false, break);
|
|
||||||
}
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void getSourceLocation(QStandardItem *infoItem,
|
|
||||||
std::function<void (const QString &, int, int)> receiver)
|
std::function<void (const QString &, int, int)> receiver)
|
||||||
{
|
{
|
||||||
int line = infoItem->data(LineRole).toInt();
|
const int line = index.data(LineRole).toInt();
|
||||||
int column = infoItem->data(ColumnRole).toInt();
|
const int column = index.data(ColumnRole).toInt();
|
||||||
QString fileName = infoItem->data(FilenameRole).toString();
|
const QString fileName = index.data(FilenameRole).toString();
|
||||||
if (line != -1 && !fileName.isEmpty())
|
if (line != -1 && !fileName.isEmpty())
|
||||||
receiver(fileName, line, column);
|
receiver(fileName, line, column);
|
||||||
}
|
}
|
||||||
@@ -234,13 +120,6 @@ QmlProfilerStatisticsView::QmlProfilerStatisticsView(QmlProfilerModelManager *pr
|
|||||||
setLayout(groupLayout);
|
setLayout(groupLayout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsView::clear()
|
|
||||||
{
|
|
||||||
m_mainView->clear();
|
|
||||||
m_calleesView->clear();
|
|
||||||
m_callersView->clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString QmlProfilerStatisticsView::summary(const QVector<int> &typeIds) const
|
QString QmlProfilerStatisticsView::summary(const QVector<int> &typeIds) const
|
||||||
{
|
{
|
||||||
return m_mainView->summary(typeIds);
|
return m_mainView->summary(typeIds);
|
||||||
@@ -344,33 +223,30 @@ QmlProfilerStatisticsMainView::QmlProfilerStatisticsMainView(QmlProfilerStatisti
|
|||||||
setViewDefaults(this);
|
setViewDefaults(this);
|
||||||
setObjectName(QLatin1String("QmlProfilerEventsTable"));
|
setObjectName(QLatin1String("QmlProfilerEventsTable"));
|
||||||
|
|
||||||
m_standardItemModel.reset(new QStandardItemModel);
|
auto sortModel = new QSortFilterProxyModel(this);
|
||||||
m_standardItemModel->setSortRole(SortRole);
|
sortModel->setSourceModel(model);
|
||||||
setModel(m_standardItemModel.get());
|
sortModel->setSortRole(SortRole);
|
||||||
connect(this, &QAbstractItemView::activated, this, &QmlProfilerStatisticsMainView::jumpToItem);
|
sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
sortModel->setFilterRole(FilterRole);
|
||||||
|
sortModel->setFilterKeyColumn(MainType);
|
||||||
|
sortModel->setFilterFixedString("+");
|
||||||
|
|
||||||
connect(m_model.get(), &QmlProfilerStatisticsModel::dataAvailable,
|
setModel(sortModel);
|
||||||
this, &QmlProfilerStatisticsMainView::buildModel);
|
|
||||||
connect(m_model.get(), &QmlProfilerStatisticsModel::notesAvailable,
|
connect(this, &QAbstractItemView::activated, this, &QmlProfilerStatisticsMainView::jumpToItem);
|
||||||
this, &QmlProfilerStatisticsMainView::updateNotes);
|
|
||||||
|
|
||||||
setSortingEnabled(true);
|
setSortingEnabled(true);
|
||||||
sortByColumn(DEFAULT_SORT_COLUMN, Qt::DescendingOrder);
|
sortByColumn(DEFAULT_SORT_COLUMN, Qt::DescendingOrder);
|
||||||
|
|
||||||
buildModel();
|
setShowExtendedStatistics(m_showExtendedStatistics);
|
||||||
|
setRootIsDecorated(false);
|
||||||
|
|
||||||
|
resizeColumnToContents(MainLocation);
|
||||||
|
resizeColumnToContents(MainType);
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlProfilerStatisticsMainView::~QmlProfilerStatisticsMainView()
|
QmlProfilerStatisticsMainView::~QmlProfilerStatisticsMainView()
|
||||||
{
|
{
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::setHeaderLabels()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < MaxMainField; ++i) {
|
|
||||||
m_standardItemModel->setHeaderData(i, Qt::Horizontal,
|
|
||||||
displayHeader(static_cast<MainField>(i)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::setShowExtendedStatistics(bool show)
|
void QmlProfilerStatisticsMainView::setShowExtendedStatistics(bool show)
|
||||||
@@ -393,59 +269,6 @@ bool QmlProfilerStatisticsMainView::showExtendedStatistics() const
|
|||||||
return m_showExtendedStatistics;
|
return m_showExtendedStatistics;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::clear()
|
|
||||||
{
|
|
||||||
SortPreserver sorter(this);
|
|
||||||
m_standardItemModel->clear();
|
|
||||||
m_standardItemModel->setColumnCount(MaxMainField);
|
|
||||||
setHeaderLabels();
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::buildModel()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
|
|
||||||
{
|
|
||||||
SortPreserver sorter(this);
|
|
||||||
parseModel();
|
|
||||||
setShowExtendedStatistics(m_showExtendedStatistics);
|
|
||||||
setRootIsDecorated(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
resizeColumnToContents(MainLocation);
|
|
||||||
resizeColumnToContents(MainType);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::updateNotes(int typeIndex)
|
|
||||||
{
|
|
||||||
const QVector<QmlProfilerStatisticsModel::QmlEventStats> &eventList = m_model->getData();
|
|
||||||
const QHash<int, QString> ¬eList = m_model->getNotes();
|
|
||||||
QStandardItem *rootItem = m_standardItemModel->invisibleRootItem();
|
|
||||||
|
|
||||||
for (int rowIndex = 0; rowIndex < rootItem->rowCount(); ++rowIndex) {
|
|
||||||
int rowType = rootItem->child(rowIndex)->data(TypeIdRole).toInt();
|
|
||||||
if (rowType == -1 || (rowType != typeIndex && typeIndex != -1))
|
|
||||||
continue;
|
|
||||||
const QmlProfilerStatisticsModel::QmlEventStats &stats = eventList[rowType];
|
|
||||||
|
|
||||||
for (int columnIndex = 0; columnIndex < rootItem->columnCount(); ++columnIndex) {
|
|
||||||
QStandardItem *item = rootItem->child(rowIndex, columnIndex);
|
|
||||||
QHash<int, QString>::ConstIterator it = noteList.find(rowType);
|
|
||||||
if (it != noteList.end()) {
|
|
||||||
item->setBackground(colors()->noteBackground);
|
|
||||||
item->setToolTip(it.value());
|
|
||||||
} else if (stats.recursive > 0) {
|
|
||||||
item->setBackground(colors()->noteBackground);
|
|
||||||
item->setToolTip(tr("+%1 in recursive calls")
|
|
||||||
.arg(Timeline::formatTime(stats.recursive)));
|
|
||||||
} else if (!item->toolTip().isEmpty()){
|
|
||||||
item->setBackground(colors()->defaultBackground);
|
|
||||||
item->setToolTip(QString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::restrictToFeatures(quint64 features)
|
void QmlProfilerStatisticsMainView::restrictToFeatures(quint64 features)
|
||||||
{
|
{
|
||||||
m_model->restrictToFeatures(features);
|
m_model->restrictToFeatures(features);
|
||||||
@@ -466,132 +289,40 @@ QStringList QmlProfilerStatisticsMainView::details(int typeId) const
|
|||||||
return m_model->details(typeId);
|
return m_model->details(typeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::parseModel()
|
|
||||||
{
|
|
||||||
const QVector<QmlProfilerStatisticsModel::QmlEventStats> &eventList = m_model->getData();
|
|
||||||
const QVector<QmlEventType> &typeList = m_model->getTypes();
|
|
||||||
|
|
||||||
QmlProfilerStatisticsModel::QmlEventStats rootEventStats;
|
|
||||||
rootEventStats.total = rootEventStats.maximum = rootEventStats.minimum = rootEventStats.median
|
|
||||||
= m_model->rootDuration();
|
|
||||||
rootEventStats.calls = rootEventStats.total > 0 ? 1 : 0;
|
|
||||||
|
|
||||||
for (int typeIndex = -1; typeIndex < eventList.size(); ++typeIndex) {
|
|
||||||
const QmlProfilerStatisticsModel::QmlEventStats &stats = typeIndex >= 0
|
|
||||||
? eventList[typeIndex] : rootEventStats;
|
|
||||||
if (stats.calls == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
const QmlEventType &type = typeIndex >= 0 ? typeList[typeIndex] : *rootEventType();
|
|
||||||
QStandardItem *rootItem = m_standardItemModel->invisibleRootItem();
|
|
||||||
QList<QStandardItem *> newRow;
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(
|
|
||||||
type.displayName().isEmpty() ? tr("<bytecode>") : type.displayName(),
|
|
||||||
type.displayName());
|
|
||||||
|
|
||||||
QString typeString = QmlProfilerStatisticsModel::nameForType(type.rangeType());
|
|
||||||
newRow << new StatisticsViewItem(typeString, typeString);
|
|
||||||
|
|
||||||
const double percent = m_model->durationPercent(typeIndex);
|
|
||||||
newRow << new StatisticsViewItem(QString::number(percent, 'f', 2)
|
|
||||||
+ QLatin1String(" %"), percent);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(
|
|
||||||
Timeline::formatTime(stats.totalNonRecursive()),
|
|
||||||
stats.totalNonRecursive());
|
|
||||||
|
|
||||||
const double percentSelf = m_model->durationSelfPercent(typeIndex);
|
|
||||||
newRow << new StatisticsViewItem(QString::number(percentSelf, 'f', 2)
|
|
||||||
+ QLatin1String(" %"), percentSelf);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(Timeline::formatTime(stats.self),
|
|
||||||
stats.self);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(QString::number(stats.calls), stats.calls);
|
|
||||||
|
|
||||||
const qint64 timePerCall = stats.average();
|
|
||||||
newRow << new StatisticsViewItem(Timeline::formatTime(timePerCall),
|
|
||||||
timePerCall);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(Timeline::formatTime(stats.median),
|
|
||||||
stats.median);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(Timeline::formatTime(stats.maximum),
|
|
||||||
stats.maximum);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(Timeline::formatTime(stats.minimum),
|
|
||||||
stats.minimum);
|
|
||||||
|
|
||||||
newRow << new StatisticsViewItem(type.data().isEmpty() ? tr("Source code not available")
|
|
||||||
: type.data(), type.data());
|
|
||||||
|
|
||||||
// no edit
|
|
||||||
foreach (QStandardItem *item, newRow)
|
|
||||||
item->setEditable(false);
|
|
||||||
|
|
||||||
// metadata
|
|
||||||
QStandardItem *first = newRow.at(MainLocation);
|
|
||||||
first->setData(typeIndex, TypeIdRole);
|
|
||||||
const QmlEventLocation location(type.location());
|
|
||||||
first->setData(location.filename(), FilenameRole);
|
|
||||||
first->setData(location.line(), LineRole);
|
|
||||||
first->setData(location.column(), ColumnRole);
|
|
||||||
|
|
||||||
// append
|
|
||||||
rootItem->appendRow(newRow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStandardItem *QmlProfilerStatisticsMainView::itemFromIndex(const QModelIndex &index) const
|
|
||||||
{
|
|
||||||
QStandardItem *indexItem = m_standardItemModel->itemFromIndex(index);
|
|
||||||
if (indexItem->parent())
|
|
||||||
return indexItem->parent()->child(indexItem->row());
|
|
||||||
else
|
|
||||||
return m_standardItemModel->item(index.row());
|
|
||||||
}
|
|
||||||
|
|
||||||
int QmlProfilerStatisticsMainView::selectedTypeId() const
|
int QmlProfilerStatisticsMainView::selectedTypeId() const
|
||||||
{
|
{
|
||||||
QModelIndex index = selectedModelIndex();
|
return model()->data(selectedModelIndex(), TypeIdRole).toInt();
|
||||||
if (!index.isValid())
|
|
||||||
return -1;
|
|
||||||
QStandardItem *item = m_standardItemModel->item(index.row());
|
|
||||||
return item->data(TypeIdRole).toInt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::jumpToItem(const QModelIndex &index)
|
void QmlProfilerStatisticsMainView::jumpToItem(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
QStandardItem *infoItem = itemFromIndex(index);
|
|
||||||
|
|
||||||
// show in editor
|
// show in editor
|
||||||
getSourceLocation(infoItem, [this](const QString &fileName, int line, int column) {
|
getSourceLocation(index, [this](const QString &fileName, int line, int column) {
|
||||||
emit gotoSourceLocation(fileName, line, column);
|
emit gotoSourceLocation(fileName, line, column);
|
||||||
});
|
});
|
||||||
|
|
||||||
// show in callers/callees subwindow
|
// show in callers/callees subwindow
|
||||||
emit typeSelected(infoItem->data(TypeIdRole).toInt());
|
emit typeSelected(model()->data(index, TypeIdRole).toInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::selectItem(const QStandardItem *item)
|
void QmlProfilerStatisticsMainView::selectItem(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
// If the same item is already selected, don't reselect it.
|
// If the same item is already selected, don't reselect it.
|
||||||
QModelIndex index = m_standardItemModel->indexFromItem(item);
|
|
||||||
if (index != currentIndex()) {
|
if (index != currentIndex()) {
|
||||||
setCurrentIndex(index);
|
setCurrentIndex(index);
|
||||||
|
|
||||||
// show in callers/callees subwindow
|
// show in callers/callees subwindow
|
||||||
emit typeSelected(itemFromIndex(index)->data(TypeIdRole).toInt());
|
emit typeSelected(index.data(TypeIdRole).toInt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsMainView::selectType(int typeIndex)
|
void QmlProfilerStatisticsMainView::selectType(int typeIndex)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < m_standardItemModel->rowCount(); i++) {
|
QAbstractItemModel *itemModel = model();
|
||||||
QStandardItem *infoItem = m_standardItemModel->item(i);
|
for (int i = 0; i < itemModel->rowCount(); i++) {
|
||||||
if (infoItem->data(TypeIdRole).toInt() == typeIndex) {
|
QModelIndex index = itemModel->index(i, MainLocation);
|
||||||
selectItem(infoItem);
|
if (itemModel->data(index, TypeIdRole).toInt() == typeIndex) {
|
||||||
|
selectItem(index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -606,16 +337,15 @@ QModelIndex QmlProfilerStatisticsMainView::selectedModelIndex() const
|
|||||||
return sel.first();
|
return sel.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString QmlProfilerStatisticsMainView::textForItem(QStandardItem *item) const
|
QString QmlProfilerStatisticsMainView::textForItem(const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
QString str;
|
QString str;
|
||||||
|
|
||||||
// item's data
|
// item's data
|
||||||
int colCount = m_standardItemModel->columnCount();
|
const int colCount = model()->columnCount();
|
||||||
for (int j = 0; j < colCount; ++j) {
|
for (int j = 0; j < colCount; ++j) {
|
||||||
QStandardItem *colItem = item->parent() ? item->parent()->child(item->row(),j) :
|
const QModelIndex cellIndex = model()->index(index.row(), j);
|
||||||
m_standardItemModel->item(item->row(),j);
|
str += cellIndex.data(Qt::DisplayRole).toString();
|
||||||
str += colItem->data(Qt::DisplayRole).toString();
|
|
||||||
if (j < colCount-1) str += QLatin1Char('\t');
|
if (j < colCount-1) str += QLatin1Char('\t');
|
||||||
}
|
}
|
||||||
str += QLatin1Char('\n');
|
str += QLatin1Char('\n');
|
||||||
@@ -626,20 +356,24 @@ QString QmlProfilerStatisticsMainView::textForItem(QStandardItem *item) const
|
|||||||
void QmlProfilerStatisticsMainView::copyTableToClipboard() const
|
void QmlProfilerStatisticsMainView::copyTableToClipboard() const
|
||||||
{
|
{
|
||||||
QString str;
|
QString str;
|
||||||
|
|
||||||
|
const QAbstractItemModel *itemModel = model();
|
||||||
|
|
||||||
// headers
|
// headers
|
||||||
int columnCount = m_standardItemModel->columnCount();
|
const int columnCount = itemModel->columnCount();
|
||||||
for (int i = 0; i < columnCount; ++i) {
|
for (int i = 0; i < columnCount; ++i) {
|
||||||
str += m_standardItemModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
|
str += itemModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString();
|
||||||
if (i < columnCount - 1)
|
if (i < columnCount - 1)
|
||||||
str += QLatin1Char('\t');
|
str += QLatin1Char('\t');
|
||||||
else
|
else
|
||||||
str += QLatin1Char('\n');
|
str += QLatin1Char('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
// data
|
// data
|
||||||
int rowCount = m_standardItemModel->rowCount();
|
const int rowCount = itemModel->rowCount();
|
||||||
for (int i = 0; i != rowCount; ++i) {
|
for (int i = 0; i != rowCount; ++i)
|
||||||
str += textForItem(m_standardItemModel->item(i));
|
str += textForItem(itemModel->index(i, 0));
|
||||||
}
|
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
clipboard->setText(str, QClipboard::Selection);
|
clipboard->setText(str, QClipboard::Selection);
|
||||||
clipboard->setText(str, QClipboard::Clipboard);
|
clipboard->setText(str, QClipboard::Clipboard);
|
||||||
@@ -647,7 +381,7 @@ void QmlProfilerStatisticsMainView::copyTableToClipboard() const
|
|||||||
|
|
||||||
void QmlProfilerStatisticsMainView::copyRowToClipboard() const
|
void QmlProfilerStatisticsMainView::copyRowToClipboard() const
|
||||||
{
|
{
|
||||||
QString str = textForItem(m_standardItemModel->itemFromIndex(selectedModelIndex()));
|
QString str = textForItem(selectedModelIndex());
|
||||||
QClipboard *clipboard = QApplication::clipboard();
|
QClipboard *clipboard = QApplication::clipboard();
|
||||||
clipboard->setText(str, QClipboard::Selection);
|
clipboard->setText(str, QClipboard::Selection);
|
||||||
clipboard->setText(str, QClipboard::Clipboard);
|
clipboard->setText(str, QClipboard::Clipboard);
|
||||||
@@ -658,21 +392,18 @@ QmlProfilerStatisticsRelativesView::QmlProfilerStatisticsRelativesView(
|
|||||||
m_model(model)
|
m_model(model)
|
||||||
{
|
{
|
||||||
setViewDefaults(this);
|
setViewDefaults(this);
|
||||||
QStandardItemModel *itemModel = new QStandardItemModel(this);
|
auto sortModel = new QSortFilterProxyModel(this);
|
||||||
itemModel->setSortRole(SortRole);
|
sortModel->setSourceModel(model);
|
||||||
setModel(itemModel);
|
sortModel->setSortRole(SortRole);
|
||||||
|
sortModel->setSortCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
setModel(sortModel);
|
||||||
setRootIsDecorated(false);
|
setRootIsDecorated(false);
|
||||||
updateHeader();
|
|
||||||
|
|
||||||
setSortingEnabled(true);
|
setSortingEnabled(true);
|
||||||
sortByColumn(DEFAULT_SORT_COLUMN, Qt::DescendingOrder);
|
sortByColumn(DEFAULT_SORT_COLUMN, Qt::DescendingOrder);
|
||||||
|
|
||||||
connect(this, &QAbstractItemView::activated,
|
connect(this, &QAbstractItemView::activated,
|
||||||
this, &QmlProfilerStatisticsRelativesView::jumpToItem);
|
this, &QmlProfilerStatisticsRelativesView::jumpToItem);
|
||||||
|
|
||||||
// Clear when new data available as the selection may be invalid now.
|
|
||||||
connect(m_model.get(), &QmlProfilerStatisticsRelativesModel::dataAvailable,
|
|
||||||
this, &QmlProfilerStatisticsRelativesView::clear);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QmlProfilerStatisticsRelativesView::~QmlProfilerStatisticsRelativesView()
|
QmlProfilerStatisticsRelativesView::~QmlProfilerStatisticsRelativesView()
|
||||||
@@ -681,100 +412,18 @@ QmlProfilerStatisticsRelativesView::~QmlProfilerStatisticsRelativesView()
|
|||||||
|
|
||||||
void QmlProfilerStatisticsRelativesView::displayType(int typeIndex)
|
void QmlProfilerStatisticsRelativesView::displayType(int typeIndex)
|
||||||
{
|
{
|
||||||
SortPreserver sorter(this);
|
model()->setData(QModelIndex(), typeIndex, TypeIdRole);
|
||||||
rebuildTree(m_model->getData(typeIndex));
|
|
||||||
|
|
||||||
updateHeader();
|
|
||||||
resizeColumnToContents(RelativeLocation);
|
resizeColumnToContents(RelativeLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
void QmlProfilerStatisticsRelativesView::rebuildTree(
|
|
||||||
const QVector<QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData> &data)
|
|
||||||
{
|
|
||||||
Q_ASSERT(treeModel());
|
|
||||||
treeModel()->clear();
|
|
||||||
|
|
||||||
QStandardItem *topLevelItem = treeModel()->invisibleRootItem();
|
|
||||||
const QVector<QmlEventType> &typeList = m_model->getTypes();
|
|
||||||
|
|
||||||
for (auto it = data.constBegin(); it != data.constEnd(); ++it) {
|
|
||||||
const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData &stats = *it;
|
|
||||||
const QmlEventType &type = stats.typeIndex != -1 ? typeList[stats.typeIndex]
|
|
||||||
: *rootEventType();
|
|
||||||
QList<QStandardItem *> newRow;
|
|
||||||
|
|
||||||
// ToDo: here we were going to search for the data in the other model
|
|
||||||
// maybe we should store the data in this model and get it here
|
|
||||||
// no indirections at this level of abstraction!
|
|
||||||
newRow << new StatisticsViewItem(
|
|
||||||
type.displayName().isEmpty() ? tr("<bytecode>") : type.displayName(),
|
|
||||||
type.displayName());
|
|
||||||
const QString typeName = QmlProfilerStatisticsModel::nameForType(type.rangeType());
|
|
||||||
newRow << new StatisticsViewItem(typeName, typeName);
|
|
||||||
newRow << new StatisticsViewItem(Timeline::formatTime(stats.duration),
|
|
||||||
stats.duration);
|
|
||||||
newRow << new StatisticsViewItem(QString::number(stats.calls), stats.calls);
|
|
||||||
newRow << new StatisticsViewItem(type.data().isEmpty() ? tr("Source code not available") :
|
|
||||||
type.data(), type.data());
|
|
||||||
|
|
||||||
QStandardItem *first = newRow.at(RelativeLocation);
|
|
||||||
first->setData(stats.typeIndex, TypeIdRole);
|
|
||||||
const QmlEventLocation location(type.location());
|
|
||||||
first->setData(location.filename(), FilenameRole);
|
|
||||||
first->setData(location.line(), LineRole);
|
|
||||||
first->setData(location.column(), ColumnRole);
|
|
||||||
|
|
||||||
if (stats.isRecursive) {
|
|
||||||
foreach (QStandardItem *item, newRow) {
|
|
||||||
item->setBackground(colors()->noteBackground);
|
|
||||||
item->setToolTip(tr("called recursively"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (QStandardItem *item, newRow)
|
|
||||||
item->setEditable(false);
|
|
||||||
|
|
||||||
topLevelItem->appendRow(newRow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsRelativesView::clear()
|
|
||||||
{
|
|
||||||
if (treeModel()) {
|
|
||||||
SortPreserver sorter(this);
|
|
||||||
treeModel()->clear();
|
|
||||||
updateHeader();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsRelativesView::updateHeader()
|
|
||||||
{
|
|
||||||
const QmlProfilerStatisticsRelation relation = m_model->relation();
|
|
||||||
if (QStandardItemModel *model = treeModel()) {
|
|
||||||
model->setColumnCount(MaxRelativeField);
|
|
||||||
for (int i = 0; i < MaxRelativeField; ++i) {
|
|
||||||
model->setHeaderData(i, Qt::Horizontal,
|
|
||||||
displayHeader(static_cast<RelativeField>(i), relation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStandardItemModel *QmlProfilerStatisticsRelativesView::treeModel()
|
|
||||||
{
|
|
||||||
return qobject_cast<QStandardItemModel *>(model());
|
|
||||||
}
|
|
||||||
|
|
||||||
void QmlProfilerStatisticsRelativesView::jumpToItem(const QModelIndex &index)
|
void QmlProfilerStatisticsRelativesView::jumpToItem(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
if (treeModel()) {
|
|
||||||
QStandardItem *infoItem = treeModel()->item(index.row());
|
|
||||||
// show in editor
|
// show in editor
|
||||||
getSourceLocation(infoItem, [this](const QString &fileName, int line, int column) {
|
getSourceLocation(index, [this](const QString &fileName, int line, int column) {
|
||||||
emit gotoSourceLocation(fileName, line, column);
|
emit gotoSourceLocation(fileName, line, column);
|
||||||
});
|
});
|
||||||
|
|
||||||
emit typeClicked(infoItem->data(TypeIdRole).toInt());
|
emit typeClicked(index.data(TypeIdRole).toInt());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -34,7 +34,6 @@
|
|||||||
#include <utils/itemviews.h>
|
#include <utils/itemviews.h>
|
||||||
|
|
||||||
#include <QPointer>
|
#include <QPointer>
|
||||||
#include <QStandardItemModel>
|
|
||||||
|
|
||||||
namespace QmlProfiler {
|
namespace QmlProfiler {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
@@ -49,7 +48,6 @@ public:
|
|||||||
explicit QmlProfilerStatisticsView(QmlProfilerModelManager *profilerModelManager,
|
explicit QmlProfilerStatisticsView(QmlProfilerModelManager *profilerModelManager,
|
||||||
QWidget *parent = nullptr);
|
QWidget *parent = nullptr);
|
||||||
~QmlProfilerStatisticsView() override = default;
|
~QmlProfilerStatisticsView() override = default;
|
||||||
void clear() override;
|
|
||||||
|
|
||||||
QString summary(const QVector<int> &typeIds) const;
|
QString summary(const QVector<int> &typeIds) const;
|
||||||
QStringList details(int typeId) const;
|
QStringList details(int typeId) const;
|
||||||
@@ -89,11 +87,8 @@ public:
|
|||||||
void setShowExtendedStatistics(bool);
|
void setShowExtendedStatistics(bool);
|
||||||
bool showExtendedStatistics() const;
|
bool showExtendedStatistics() const;
|
||||||
|
|
||||||
void clear();
|
|
||||||
void jumpToItem(const QModelIndex &index);
|
void jumpToItem(const QModelIndex &index);
|
||||||
void selectType(int typeIndex);
|
void selectType(int typeIndex);
|
||||||
void buildModel();
|
|
||||||
void updateNotes(int typeIndex);
|
|
||||||
|
|
||||||
void restrictToFeatures(quint64 features);
|
void restrictToFeatures(quint64 features);
|
||||||
bool isRestrictedToRange() const;
|
bool isRestrictedToRange() const;
|
||||||
@@ -106,14 +101,10 @@ signals:
|
|||||||
void typeSelected(int typeIndex);
|
void typeSelected(int typeIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void selectItem(const QStandardItem *item);
|
void selectItem(const QModelIndex &index);
|
||||||
void setHeaderLabels();
|
QString textForItem(const QModelIndex &index) const;
|
||||||
void parseModel();
|
|
||||||
QStandardItem *itemFromIndex(const QModelIndex &index) const;
|
|
||||||
QString textForItem(QStandardItem *item) const;
|
|
||||||
|
|
||||||
std::unique_ptr<QmlProfilerStatisticsModel> m_model;
|
std::unique_ptr<QmlProfilerStatisticsModel> m_model;
|
||||||
std::unique_ptr<QStandardItemModel> m_standardItemModel;
|
|
||||||
bool m_showExtendedStatistics = false;
|
bool m_showExtendedStatistics = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -126,17 +117,12 @@ public:
|
|||||||
|
|
||||||
void displayType(int typeIndex);
|
void displayType(int typeIndex);
|
||||||
void jumpToItem(const QModelIndex &);
|
void jumpToItem(const QModelIndex &);
|
||||||
void clear();
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void typeClicked(int typeIndex);
|
void typeClicked(int typeIndex);
|
||||||
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
|
void gotoSourceLocation(const QString &fileName, int lineNumber, int columnNumber);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void rebuildTree(
|
|
||||||
const QVector<QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesData> &data);
|
|
||||||
void updateHeader();
|
|
||||||
QStandardItemModel *treeModel();
|
|
||||||
|
|
||||||
std::unique_ptr<QmlProfilerStatisticsRelativesModel> m_model;
|
std::unique_ptr<QmlProfilerStatisticsRelativesModel> m_model;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user