/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms and ** conditions see http://www.qt.io/terms-conditions. For further information ** use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "callgrinddatamodel.h" #include "callgrindparsedata.h" #include "callgrindfunction.h" #include "callgrindcostitem.h" #include #include #include #include #include #include #include namespace Valgrind { namespace Callgrind { //BEGIN DataModel::Private class DataModel::Private { public: Private() : m_data(0) , m_event(0) , m_verboseToolTips(true) , m_cycleDetection(false) , m_shortenTemplates(false) { } void updateFunctions(); const ParseData *m_data; int m_event; bool m_verboseToolTips; bool m_cycleDetection; bool m_shortenTemplates; QVector m_functions; }; void DataModel::Private::updateFunctions() { if (m_data) { m_functions = m_data->functions(m_cycleDetection); Utils::sort(m_functions, [this](const Function *l, const Function *r) { return l->inclusiveCost(m_event) > r->inclusiveCost(m_event); }); } else { m_functions.clear(); } } //BEGIN DataModel DataModel::DataModel(QObject *parent) : QAbstractItemModel(parent), d(new Private) { } DataModel::~DataModel() { delete d; } void DataModel::setParseData(const ParseData *data) { if (d->m_data == data) return; beginResetModel(); d->m_data = data; d->m_event = 0; d->updateFunctions(); endResetModel(); } void DataModel::setVerboseToolTipsEnabled(bool enabled) { d->m_verboseToolTips = enabled; } bool DataModel::verboseToolTipsEnabled() const { return d->m_verboseToolTips; } const ParseData *DataModel::parseData() const { return d->m_data; } void DataModel::setCostEvent(int event) { if (!d->m_data) return; QTC_ASSERT(event >= 0 && d->m_data->events().size() > event, return); beginResetModel(); d->m_event = event; d->updateFunctions(); endResetModel(); emit dataChanged(index(0, SelfCostColumn), index(qMax(0, rowCount() - 1), InclusiveCostColumn)); } int DataModel::costEvent() const { return d->m_event; } int DataModel::rowCount(const QModelIndex &parent) const { QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); if (!d->m_data || parent.isValid()) return 0; return d->m_functions.size(); } int DataModel::columnCount(const QModelIndex &parent) const { QTC_ASSERT(!parent.isValid() || parent.model() == this, return 0); if (parent.isValid()) return 0; return ColumnCount; } QModelIndex DataModel::index(int row, int column, const QModelIndex &parent) const { QTC_ASSERT(!parent.isValid() || parent.model() == this, return QModelIndex()); if (row == 0 && rowCount(parent) == 0) // happens with empty models return QModelIndex(); QTC_ASSERT(row >= 0 && row < rowCount(parent), return QModelIndex()); return createIndex(row, column); } QModelIndex DataModel::parent(const QModelIndex &child) const { QTC_ASSERT(!child.isValid() || child.model() == this, return QModelIndex()); return QModelIndex(); } QModelIndex DataModel::indexForObject(const Function *function) const { if (!function) return QModelIndex(); const int row = d->m_functions.indexOf(function); if (row < 0) return QModelIndex(); return createIndex(row, 0); } /** * Evil workaround for https://bugreports.qt.io/browse/QTBUG-1135 * Just replace the bad hyphens by a 'NON-BREAKING HYPHEN' unicode char */ static QString noWrap(const QString &str) { QString escapedStr = str; return escapedStr.replace(QLatin1Char('-'), QLatin1String("‑")); } static QString shortenTemplate(QString str) { int depth = 0; int j = 0; for (int i = 0, n = str.size(); i != n; ++i) { int c = str.at(i).unicode(); if (c == '>') --depth; if (depth == 0) str[j++] = str.at(i); if (c == '<') ++depth; } str.truncate(j); return str; } QVariant DataModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); const Function *func = d->m_functions.at(index.row()); if (role == Qt::DisplayRole) { if (index.column() == NameColumn) return d->m_shortenTemplates ? shortenTemplate(func->name()) : func->name(); if (index.column() == LocationColumn) return func->location(); if (index.column() == CalledColumn) return func->called(); if (index.column() == SelfCostColumn) return func->selfCost(d->m_event); if (index.column() == InclusiveCostColumn) return func->inclusiveCost(d->m_event); return QVariant(); } if (role == Qt::ToolTipRole) { if (!d->m_verboseToolTips) return data(index, Qt::DisplayRole); QString ret = QLatin1String("\n
"); QString entry = QLatin1String("
%1
%2
\n"); // body, function info first ret += entry.arg(tr("Function:")).arg(func->name().toHtmlEscaped()); ret += entry.arg(tr("File:")).arg(func->file()); if (!func->costItems().isEmpty()) { const CostItem *firstItem = func->costItems().first(); for (int i = 0; i < d->m_data->positions().size(); ++i) { ret += entry.arg(ParseData::prettyStringForPosition(d->m_data->positions().at(i))) .arg(firstItem->position(i)); } } ret += entry.arg(tr("Object:")).arg(func->object()); ret += entry.arg(tr("Called:")).arg(tr("%n time(s)", 0, func->called())); ret += QLatin1String("

"); // self/inclusive costs entry = QLatin1String("%1%2"); ret += QLatin1String(""); ret += QLatin1String(""); ret += QLatin1String(""); ret += entry.arg(tr("Self costs")).arg(tr("(%)")); ret += entry.arg(tr("Incl. costs")).arg(tr("(%)")); ret += QLatin1String(""); ret += QLatin1String(""); for (int i = 0; i < d->m_data->events().size(); ++i) { quint64 selfCost = func->selfCost(i); quint64 inclCost = func->inclusiveCost(i); quint64 totalCost = d->m_data->totalCost(i); // 0.00% format const float relSelfCost = (float)qRound((float)selfCost / totalCost * 10000) / 100; const float relInclCost = (float)qRound((float)inclCost / totalCost * 10000) / 100; ret += QLatin1String(""); ret += QLatin1String(""); ret += entry.arg(selfCost).arg(tr("(%1%)").arg(relSelfCost)); ret += entry.arg(inclCost).arg(tr("(%1%)").arg(relInclCost)); ret += QLatin1String(""); } ret += QLatin1String("
") + tr("Events") + QLatin1String("
") + noWrap(ParseData::prettyStringForEvent(d->m_data->events().at(i))) + QLatin1String("
"); ret += QLatin1String(""); return ret; } if (role == FunctionRole) return QVariant::fromValue(func); if (role == ParentCostRole) { const quint64 totalCost = d->m_data->totalCost(d->m_event); return totalCost; } // the data model does not know about parent<->child relationship if (role == RelativeParentCostRole || role == RelativeTotalCostRole) { const quint64 totalCost = d->m_data->totalCost(d->m_event); if (index.column() == SelfCostColumn) return double(func->selfCost(d->m_event)) / totalCost; if (index.column() == InclusiveCostColumn) return double(func->inclusiveCost(d->m_event)) / totalCost; } if (role == LineNumberRole) return func->lineNumber(); if (role == FileNameRole) return func->file(); if (role == Qt::TextAlignmentRole) { if (index.column() == CalledColumn) return Qt::AlignRight; } return QVariant(); } QVariant DataModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Vertical || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) return QVariant(); QTC_ASSERT(section >= 0 && section < columnCount(), return QVariant()); if (role == Qt::ToolTipRole) { if (!d->m_data) return QVariant(); const QString prettyCostStr = ParseData::prettyStringForEvent(d->m_data->events().at(d->m_event)); if (section == SelfCostColumn) return tr("%1 cost spent in a given function excluding costs from called functions.").arg(prettyCostStr); if (section == InclusiveCostColumn) return tr("%1 cost spent in a given function including costs from called functions.").arg(prettyCostStr); return QVariant(); } if (section == NameColumn) return tr("Function"); if (section == LocationColumn) return tr("Location"); if (section == CalledColumn) return tr("Called"); if (section == SelfCostColumn) return tr("Self Cost: %1").arg(d->m_data ? d->m_data->events().value(d->m_event) : QString()); if (section == InclusiveCostColumn) return tr("Incl. Cost: %1").arg(d->m_data ? d->m_data->events().value(d->m_event) : QString()); return QVariant(); } void DataModel::enableCycleDetection(bool enabled) { beginResetModel(); d->m_cycleDetection = enabled; d->updateFunctions(); endResetModel(); } void DataModel::setShortenTemplates(bool enabled) { beginResetModel(); d->m_shortenTemplates = enabled; d->updateFunctions(); endResetModel(); } } // namespace Valgrind } // namespace Callgrind