diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp index 862b8bb60d2..26ecc29401e 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.cpp @@ -52,16 +52,27 @@ public: int modelId; QList acceptedTypes; - QSet eventsInBindingLoop; QHash notes; QStack callStack; QStack compileStack; - qint64 qmlTime = 0; - qint64 lastEndTime = 0; QHash > durations; }; +double QmlProfilerStatisticsModel::durationPercent(int typeId) const +{ + const QmlEventStats &global = d->data[-1]; + const QmlEventStats &stats = d->data[typeId]; + return double(stats.duration - stats.durationRecursive) / double(global.duration) * 100l; +} + +double QmlProfilerStatisticsModel::durationSelfPercent(int typeId) const +{ + const QmlEventStats &global = d->data[-1]; + const QmlEventStats &stats = d->data[typeId]; + return double(stats.durationSelf) / double(global.duration) * 100l; +} + QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager, QObject *parent) : QObject(parent), d(new QmlProfilerStatisticsModelPrivate) @@ -136,12 +147,9 @@ const QHash &QmlProfilerStatisticsModel::getNotes() const void QmlProfilerStatisticsModel::clear() { d->data.clear(); - d->eventsInBindingLoop.clear(); d->notes.clear(); d->callStack.clear(); d->compileStack.clear(); - d->qmlTime = 0; - d->lastEndTime = 0; d->durations.clear(); if (!d->childrenModel.isNull()) d->childrenModel->clear(); @@ -206,18 +214,10 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent if (!d->acceptedTypes.contains(type.rangeType())) return; + bool isRecursive = false; QStack &stack = type.rangeType() == Compiling ? d->compileStack : d->callStack; switch (event.rangeStage()) { case RangeStart: - // binding loop detection: check whether event is already in stack - if (type.rangeType() == Binding || type.rangeType() == HandlingSignal) { - for (int ii = 0; ii < stack.size(); ++ii) { - if (stack.at(ii).typeIndex() == event.typeIndex()) { - d->eventsInBindingLoop.insert(event.typeIndex()); - break; - } - } - } stack.push(event); break; case RangeEnd: { @@ -234,16 +234,21 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent stats->calls++; // for median computing d->durations[event.typeIndex()].append(duration); - // qml time computation - if (event.timestamp() > d->lastEndTime) { // assume parent event if starts before last end - d->qmlTime += duration; - d->lastEndTime = event.timestamp(); - } - stack.pop(); + // recursion detection: check whether event was already in stack + for (int ii = 0; ii < stack.size(); ++ii) { + if (stack.at(ii).typeIndex() == event.typeIndex()) { + isRecursive = true; + stats->durationRecursive += duration; + break; + } + } + if (!stack.isEmpty()) d->data[stack.top().typeIndex()].durationSelf -= duration; + else + d->data[-1].duration += duration; break; } default: @@ -251,9 +256,9 @@ void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEvent } if (!d->childrenModel.isNull()) - d->childrenModel->loadEvent(type.rangeType(), event); + d->childrenModel->loadEvent(type.rangeType(), event, isRecursive); if (!d->parentsModel.isNull()) - d->parentsModel->loadEvent(type.rangeType(), event); + d->parentsModel->loadEvent(type.rangeType(), event, isRecursive); } @@ -261,39 +266,18 @@ void QmlProfilerStatisticsModel::finalize() { // post-process: calc mean time, median time, percentoftime for (QHash::iterator it = d->data.begin(); it != d->data.end(); ++it) { - QmlEventStats* stats = &it.value(); - if (stats->calls > 0) - stats->timePerCall = stats->duration / (double)stats->calls; - QVector eventDurations = d->durations[it.key()]; if (!eventDurations.isEmpty()) { Utils::sort(eventDurations); - stats->medianTime = eventDurations.at(eventDurations.count()/2); + it->medianTime = eventDurations.at(eventDurations.count()/2); } - - stats->percentOfTime = stats->duration * 100.0 / d->qmlTime; - stats->percentSelf = stats->durationSelf * 100.0 / d->qmlTime; } - // set binding loop flag - foreach (int typeIndex, d->eventsInBindingLoop) - d->data[typeIndex].isBindingLoop = true; - // insert root event - QmlEventStats rootEvent; - rootEvent.duration = rootEvent.minTime = rootEvent.maxTime = rootEvent.timePerCall - = rootEvent.medianTime = d->qmlTime + 1; - rootEvent.durationSelf = 1; + QmlEventStats &rootEvent = d->data[-1]; + rootEvent.minTime = rootEvent.maxTime = rootEvent.medianTime = rootEvent.duration; + rootEvent.durationSelf = 0; rootEvent.calls = 1; - rootEvent.percentOfTime = 100.0; - rootEvent.percentSelf = 1.0 / rootEvent.duration; - - d->data.insert(-1, rootEvent); - - if (!d->childrenModel.isNull()) - d->childrenModel->finalize(d->eventsInBindingLoop); - if (!d->parentsModel.isNull()) - d->parentsModel->finalize(d->eventsInBindingLoop); emit dataAvailable(); } @@ -337,7 +321,8 @@ const QVector &QmlProfilerStatisticsRelativesModel::getTypes() con return m_modelManager->qmlModel()->eventTypes(); } -void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEvent &event) +void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEvent &event, + bool isRecursive) { QStack &stack = (type == Compiling) ? m_compileStack : m_callStack; @@ -355,13 +340,14 @@ void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEve QmlStatisticsRelativesMap &relativesMap = m_data[selfTypeIndex]; QmlStatisticsRelativesMap::Iterator it = relativesMap.find(relativeTypeIndex); if (it != relativesMap.end()) { - it.value().calls++; - it.value().duration += event.timestamp() - stack.top().startTime; + it->calls++; + it->duration += event.timestamp() - stack.top().startTime; + it->isRecursive = isRecursive || it->isRecursive; } else { QmlStatisticsRelativesData relative = { event.timestamp() - stack.top().startTime, 1, - false + isRecursive }; relativesMap.insert(relativeTypeIndex, relative); } @@ -373,18 +359,6 @@ void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEve } } -void QmlProfilerStatisticsRelativesModel::finalize(const QSet &eventsInBindingLoop) -{ - for (auto map = m_data.begin(), mapEnd = m_data.end(); map != mapEnd; ++map) { - auto itemEnd = map->end(); - foreach (int typeIndex, eventsInBindingLoop) { - auto item = map->find(typeIndex); - if (item != itemEnd) - item->isBindingLoop = true; - } - } -} - QmlProfilerStatisticsRelation QmlProfilerStatisticsRelativesModel::relation() const { return m_relation; diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h index 402e48841b4..d5e8186b907 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsmodel.h @@ -50,22 +50,20 @@ class QmlProfilerStatisticsModel : public QObject Q_OBJECT public: struct QmlEventStats { - QmlEventStats() : duration(0), durationSelf(0), calls(0), - minTime(std::numeric_limits::max()), maxTime(0), timePerCall(0), - percentOfTime(0), percentSelf(0), medianTime(0), isBindingLoop(false) {} + QmlEventStats() : duration(0), durationSelf(0), durationRecursive(0), calls(0), + minTime(std::numeric_limits::max()), maxTime(0), medianTime(0) {} qint64 duration; qint64 durationSelf; + qint64 durationRecursive; qint64 calls; qint64 minTime; qint64 maxTime; - qint64 timePerCall; - double percentOfTime; - double percentSelf; qint64 medianTime; - - bool isBindingLoop; }; + double durationPercent(int typeId) const; + double durationSelfPercent(int typeId) const; + QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager, QObject *parent = 0); ~QmlProfilerStatisticsModel(); @@ -107,7 +105,7 @@ public: struct QmlStatisticsRelativesData { qint64 duration; qint64 calls; - bool isBindingLoop; + bool isRecursive; }; typedef QHash QmlStatisticsRelativesMap; @@ -122,8 +120,7 @@ public: const QmlStatisticsRelativesMap &getData(int typeId) const; const QVector &getTypes() const; - void loadEvent(RangeType type, const QmlEvent &event); - void finalize(const QSet &eventsInBindingLoop); + void loadEvent(RangeType type, const QmlEvent &event, bool isRecursive); QmlProfilerStatisticsRelation relation() const; diff --git a/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp b/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp index e886082de2e..ab3e36c5399 100644 --- a/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp +++ b/src/plugins/qmlprofiler/qmlprofilerstatisticsview.cpp @@ -233,7 +233,7 @@ QString QmlProfilerStatisticsView::summary(const QVector &typeIds) const double sum = 0; for (int typeId : typeIds) { - const double percentage = d->model->getData()[typeId].percentOfTime; + const double percentage = d->model->durationPercent(typeId); if (percentage > maximum) maximum = percentage; sum += percentage; @@ -268,7 +268,7 @@ QStringList QmlProfilerStatisticsView::details(int typeId) const return QStringList({ QmlProfilerStatisticsMainView::nameForType(type.rangeType()), data, - QString::number(d->model->getData()[typeId].percentOfTime, 'f', 2) + QLatin1Char('%') + QString::number(d->model->durationPercent(typeId), 'f', 2) + QLatin1Char('%') }); } @@ -576,9 +576,11 @@ void QmlProfilerStatisticsMainView::updateNotes(int typeIndex) if (it != noteList.end()) { item->setBackground(colors()->noteBackground); item->setToolTip(it.value()); - } else if (stats.isBindingLoop) { + } else if (stats.durationRecursive > 0) { item->setBackground(colors()->noteBackground); - item->setToolTip(tr("Binding loop detected.")); + item->setToolTip(tr("%1 / %2% of total in recursive calls") + .arg(Timeline::formatTime(stats.durationRecursive)) + .arg(stats.durationRecursive * 100l / stats.duration)); } else if (!item->toolTip().isEmpty()){ item->setBackground(colors()->defaultBackground); item->setToolTip(QString()); @@ -611,8 +613,9 @@ void QmlProfilerStatisticsMainView::parseModel() } if (d->m_fieldShown[TimeInPercent]) { - newRow << new StatisticsViewItem(QString::number(stats.percentOfTime, 'f', 2) - + QLatin1String(" %"), stats.percentOfTime); + const double percent = d->model->durationPercent(typeIndex); + newRow << new StatisticsViewItem(QString::number(percent, 'f', 2) + + QLatin1String(" %"), percent); } if (d->m_fieldShown[TotalTime]) { @@ -621,8 +624,9 @@ void QmlProfilerStatisticsMainView::parseModel() } if (d->m_fieldShown[SelfTimeInPercent]) { - newRow << new StatisticsViewItem(QString::number(stats.percentSelf, 'f', 2) - + QLatin1String(" %"), stats.percentSelf); + const double percentSelf = d->model->durationSelfPercent(typeIndex); + newRow << new StatisticsViewItem(QString::number(percentSelf, 'f', 2) + + QLatin1String(" %"), percentSelf); } if (d->m_fieldShown[SelfTime]) { @@ -634,8 +638,9 @@ void QmlProfilerStatisticsMainView::parseModel() newRow << new StatisticsViewItem(QString::number(stats.calls), stats.calls); if (d->m_fieldShown[TimePerCall]) { - newRow << new StatisticsViewItem(Timeline::formatTime(stats.timePerCall), - stats.timePerCall); + const qint64 timePerCall = stats.duration / stats.calls; + newRow << new StatisticsViewItem(Timeline::formatTime(timePerCall), + timePerCall); } if (d->m_fieldShown[MedianTime]) { @@ -909,10 +914,10 @@ void QmlProfilerStatisticsRelativesView::rebuildTree( newRow.at(3)->setData(stats.calls); newRow.at(4)->setData(type.data()); - if (stats.isBindingLoop) { + if (stats.isRecursive) { foreach (QStandardItem *item, newRow) { item->setBackground(colors()->noteBackground); - item->setToolTip(tr("Part of binding loop.")); + item->setToolTip(tr("called recursively")); } }