qmlprofilerstatisticsmodel.cpp 12.7 KB
Newer Older
Christiaan Janssen's avatar
Christiaan Janssen committed
1 2
/****************************************************************************
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
Christiaan Janssen's avatar
Christiaan Janssen committed
5 6 7 8 9 10 11
**
** 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
12 13 14
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
Christiaan Janssen's avatar
Christiaan Janssen committed
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
Christiaan Janssen's avatar
Christiaan Janssen committed
23 24 25
**
****************************************************************************/

26
#include "qmlprofilerstatisticsmodel.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
27 28
#include "qmlprofilermodelmanager.h"

29
#include <utils/algorithm.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
30 31 32
#include <utils/qtcassert.h>

#include <QHash>
33
#include <QSet>
34
#include <QString>
35
#include <QPointer>
Christiaan Janssen's avatar
Christiaan Janssen committed
36

37 38
#include <functional>

Christiaan Janssen's avatar
Christiaan Janssen committed
39 40
namespace QmlProfiler {

41
class QmlProfilerStatisticsModel::QmlProfilerStatisticsModelPrivate
Christiaan Janssen's avatar
Christiaan Janssen committed
42 43
{
public:
44
    QHash<int, QmlProfilerStatisticsModel::QmlEventStats> data;
45

46 47
    QPointer<QmlProfilerStatisticsRelativesModel> childrenModel;
    QPointer<QmlProfilerStatisticsRelativesModel> parentsModel;
Christiaan Janssen's avatar
Christiaan Janssen committed
48 49 50 51 52

    QmlProfilerModelManager *modelManager;

    int modelId;

53
    QList<RangeType> acceptedTypes;
54
    QHash<int, QString> notes;
55 56

    QStack<QmlEvent> callStack;
57
    QStack<QmlEvent> compileStack;
58
    QHash <int, QVector<qint64> > durations;
Christiaan Janssen's avatar
Christiaan Janssen committed
59 60
};

61 62 63 64 65 66 67 68 69 70 71 72 73 74
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;
}

75 76
QmlProfilerStatisticsModel::QmlProfilerStatisticsModel(QmlProfilerModelManager *modelManager,
                                               QObject *parent) :
77
    QObject(parent), d(new QmlProfilerStatisticsModelPrivate)
Christiaan Janssen's avatar
Christiaan Janssen committed
78 79
{
    d->modelManager = modelManager;
80
    connect(modelManager, &QmlProfilerModelManager::stateChanged,
81
            this, &QmlProfilerStatisticsModel::dataChanged);
82
    connect(modelManager->notesModel(), &Timeline::TimelineNotesModel::changed,
83
            this, &QmlProfilerStatisticsModel::notesChanged);
Christiaan Janssen's avatar
Christiaan Janssen committed
84 85
    d->modelId = modelManager->registerModelProxy();

86
    d->acceptedTypes << Compiling << Creating << Binding << HandlingSignal << Javascript;
87

88 89 90 91 92 93
    modelManager->announceFeatures(Constants::QML_JS_RANGE_FEATURES,
                                   [this](const QmlEvent &event, const QmlEventType &type) {
        loadEvent(event, type);
    }, [this]() {
        finalize();
    });
Christiaan Janssen's avatar
Christiaan Janssen committed
94 95
}

96
QmlProfilerStatisticsModel::~QmlProfilerStatisticsModel()
Christiaan Janssen's avatar
Christiaan Janssen committed
97 98 99 100
{
    delete d;
}

101
void QmlProfilerStatisticsModel::restrictToFeatures(qint64 features)
102
{
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
    bool didChange = false;
    for (int i = 0; i < MaximumRangeType; ++i) {
        RangeType type = static_cast<RangeType>(i);
        quint64 featureFlag = 1ULL << featureFromRangeType(type);
        if (Constants::QML_JS_RANGE_FEATURES & featureFlag) {
            bool accepted = features & featureFlag;
            if (accepted && !d->acceptedTypes.contains(type)) {
                d->acceptedTypes << type;
                didChange = true;
            } else if (!accepted && d->acceptedTypes.contains(type)) {
                d->acceptedTypes.removeOne(type);
                didChange = true;
            }
        }
    }
    if (!didChange || d->modelManager->state() != QmlProfilerModelManager::Done)
        return;

    clear();
122 123 124 125 126
    if (!d->modelManager->replayEvents(d->modelManager->traceTime()->startTime(),
                                       d->modelManager->traceTime()->endTime(),
                                       std::bind(&QmlProfilerStatisticsModel::loadEvent,
                                                 this, std::placeholders::_1,
                                                 std::placeholders::_2))) {
127 128 129 130 131 132
        emit d->modelManager->error(tr("Could not re-read events from temporary trace file."));
        clear();
    } else {
        finalize();
        notesChanged(-1); // Reload notes
    }
133 134
}

135
const QHash<int, QmlProfilerStatisticsModel::QmlEventStats> &QmlProfilerStatisticsModel::getData() const
Christiaan Janssen's avatar
Christiaan Janssen committed
136
{
137 138 139
    return d->data;
}

140
const QVector<QmlEventType> &QmlProfilerStatisticsModel::getTypes() const
141
{
142
    return d->modelManager->eventTypes();
Christiaan Janssen's avatar
Christiaan Janssen committed
143 144
}

145
const QHash<int, QString> &QmlProfilerStatisticsModel::getNotes() const
146 147 148 149
{
    return d->notes;
}

150
void QmlProfilerStatisticsModel::clear()
Christiaan Janssen's avatar
Christiaan Janssen committed
151 152
{
    d->data.clear();
153
    d->notes.clear();
154
    d->callStack.clear();
155
    d->compileStack.clear();
156 157 158 159 160
    d->durations.clear();
    if (!d->childrenModel.isNull())
        d->childrenModel->clear();
    if (!d->parentsModel.isNull())
        d->parentsModel->clear();
Christiaan Janssen's avatar
Christiaan Janssen committed
161 162
}

163 164 165 166 167 168 169 170 171
void QmlProfilerStatisticsModel::setRelativesModel(QmlProfilerStatisticsRelativesModel *relative,
                                                   QmlProfilerStatisticsRelation relation)
{
    if (relation == QmlProfilerStatisticsParents)
        d->parentsModel = relative;
    else
        d->childrenModel = relative;
}

172 173 174 175 176
QmlProfilerModelManager *QmlProfilerStatisticsModel::modelManager() const
{
    return d->modelManager;
}

177
void QmlProfilerStatisticsModel::dataChanged()
Christiaan Janssen's avatar
Christiaan Janssen committed
178
{
179
    if (d->modelManager->state() == QmlProfilerModelManager::ClearingData)
180
        clear();
Christiaan Janssen's avatar
Christiaan Janssen committed
181 182
}

183
void QmlProfilerStatisticsModel::notesChanged(int typeIndex)
184
{
185
    const QmlProfilerNotesModel *notesModel = d->modelManager->notesModel();
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
    if (typeIndex == -1) {
        d->notes.clear();
        for (int noteId = 0; noteId < notesModel->count(); ++noteId) {
            int noteType = notesModel->typeId(noteId);
            if (noteType != -1) {
                QString &note = d->notes[noteType];
                if (note.isEmpty()) {
                    note = notesModel->text(noteId);
                } else {
                    note.append(QStringLiteral("\n")).append(notesModel->text(noteId));
                }
            }
        }
    } else {
        d->notes.remove(typeIndex);
201
        const QVariantList changedNotes = notesModel->byTypeId(typeIndex);
202 203 204 205 206 207 208 209 210 211 212 213 214
        if (!changedNotes.isEmpty()) {
            QStringList newNotes;
            for (QVariantList::ConstIterator it = changedNotes.begin(); it !=  changedNotes.end();
                 ++it) {
                newNotes << notesModel->text(it->toInt());
            }
            d->notes[typeIndex] = newNotes.join(QStringLiteral("\n"));
        }
    }

    emit notesAvailable(typeIndex);
}

215 216
void QmlProfilerStatisticsModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
{
217
    if (!d->acceptedTypes.contains(type.rangeType()))
218
        return;
Christiaan Janssen's avatar
Christiaan Janssen committed
219

220
    bool isRecursive = false;
221
    QStack<QmlEvent> &stack = type.rangeType() == Compiling ? d->compileStack : d->callStack;
222 223
    switch (event.rangeStage()) {
    case RangeStart:
224
        stack.push(event);
225 226 227
        break;
    case RangeEnd: {
        // update stats
228 229
        QTC_ASSERT(!stack.isEmpty(), return);
        QTC_ASSERT(stack.top().typeIndex() == event.typeIndex(), return);
230
        QmlEventStats *stats = &d->data[event.typeIndex()];
231
        qint64 duration = event.timestamp() - stack.top().timestamp();
232 233 234 235 236 237 238 239 240
        stats->duration += duration;
        stats->durationSelf += duration;
        if (duration < stats->minTime)
            stats->minTime = duration;
        if (duration > stats->maxTime)
            stats->maxTime = duration;
        stats->calls++;
        // for median computing
        d->durations[event.typeIndex()].append(duration);
241
        stack.pop();
Christiaan Janssen's avatar
Christiaan Janssen committed
242

243 244 245 246 247 248 249 250 251
        // 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;
            }
        }

252
        if (!stack.isEmpty())
253
            d->data[stack.top().typeIndex()].durationSelf -= duration;
254 255
        else
            d->data[-1].duration += duration;
256
        break;
257
    }
258
    default:
259
        return;
260 261 262
    }

    if (!d->childrenModel.isNull())
263
        d->childrenModel->loadEvent(type.rangeType(), event, isRecursive);
264
    if (!d->parentsModel.isNull())
265
        d->parentsModel->loadEvent(type.rangeType(), event, isRecursive);
266 267 268 269 270
}


void QmlProfilerStatisticsModel::finalize()
{
Christiaan Janssen's avatar
Christiaan Janssen committed
271
    // post-process: calc mean time, median time, percentoftime
272
    for (QHash<int, QmlEventStats>::iterator it = d->data.begin(); it != d->data.end(); ++it) {
273
        QVector<qint64> eventDurations = d->durations[it.key()];
Christiaan Janssen's avatar
Christiaan Janssen committed
274
        if (!eventDurations.isEmpty()) {
275
            Utils::sort(eventDurations);
276
            it->medianTime = eventDurations.at(eventDurations.count()/2);
Christiaan Janssen's avatar
Christiaan Janssen committed
277 278 279 280
        }
    }

    // insert root event
281 282 283
    QmlEventStats &rootEvent = d->data[-1];
    rootEvent.minTime = rootEvent.maxTime = rootEvent.medianTime = rootEvent.duration;
    rootEvent.durationSelf = 0;
284
    rootEvent.calls = 1;
Christiaan Janssen's avatar
Christiaan Janssen committed
285 286 287 288

    emit dataAvailable();
}

289
int QmlProfilerStatisticsModel::count() const
Christiaan Janssen's avatar
Christiaan Janssen committed
290 291 292 293
{
    return d->data.count();
}

294 295
QmlProfilerStatisticsRelativesModel::QmlProfilerStatisticsRelativesModel(
        QmlProfilerModelManager *modelManager, QmlProfilerStatisticsModel *statisticsModel,
296 297
        QmlProfilerStatisticsRelation relation, QObject *parent) :
    QObject(parent), m_relation(relation)
Christiaan Janssen's avatar
Christiaan Janssen committed
298 299 300 301
{
    QTC_CHECK(modelManager);
    m_modelManager = modelManager;

302
    QTC_CHECK(statisticsModel);
303
    statisticsModel->setRelativesModel(this, relation);
Christiaan Janssen's avatar
Christiaan Janssen committed
304

305 306
    // Load the child models whenever the parent model is done to get the filtering for JS/QML
    // right.
307 308
    connect(statisticsModel, &QmlProfilerStatisticsModel::dataAvailable,
            this, &QmlProfilerStatisticsRelativesModel::dataAvailable);
Christiaan Janssen's avatar
Christiaan Janssen committed
309 310
}

311 312
const QmlProfilerStatisticsRelativesModel::QmlStatisticsRelativesMap &
QmlProfilerStatisticsRelativesModel::getData(int typeId) const
Christiaan Janssen's avatar
Christiaan Janssen committed
313
{
314
    QHash <int, QmlStatisticsRelativesMap>::ConstIterator it = m_data.find(typeId);
315 316 317
    if (it != m_data.end()) {
        return it.value();
    } else {
318
        static const QmlStatisticsRelativesMap emptyMap;
319 320 321 322
        return emptyMap;
    }
}

323
const QVector<QmlEventType> &QmlProfilerStatisticsRelativesModel::getTypes() const
324
{
325
    return m_modelManager->eventTypes();
Christiaan Janssen's avatar
Christiaan Janssen committed
326 327
}

328 329
void QmlProfilerStatisticsRelativesModel::loadEvent(RangeType type, const QmlEvent &event,
                                                    bool isRecursive)
Christiaan Janssen's avatar
Christiaan Janssen committed
330
{
331 332
    QStack<Frame> &stack = (type == Compiling) ? m_compileStack : m_callStack;

333 334
    switch (event.rangeStage()) {
    case RangeStart:
335
        stack.push({event.timestamp(), event.typeIndex()});
336 337
        break;
    case RangeEnd: {
338
        int parentTypeIndex = stack.count() > 1 ? stack[stack.count() - 2].typeId : -1;
339 340 341 342 343 344 345 346
        int relativeTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? parentTypeIndex :
                                                                               event.typeIndex();
        int selfTypeIndex = (m_relation == QmlProfilerStatisticsParents) ? event.typeIndex() :
                                                                           parentTypeIndex;

        QmlStatisticsRelativesMap &relativesMap = m_data[selfTypeIndex];
        QmlStatisticsRelativesMap::Iterator it = relativesMap.find(relativeTypeIndex);
        if (it != relativesMap.end()) {
347 348 349
            it->calls++;
            it->duration += event.timestamp() - stack.top().startTime;
            it->isRecursive = isRecursive || it->isRecursive;
350 351
        } else {
            QmlStatisticsRelativesData relative = {
352
                event.timestamp() - stack.top().startTime,
353
                1,
354
                isRecursive
355 356 357
            };
            relativesMap.insert(relativeTypeIndex, relative);
        }
358
        stack.pop();
359
        break;
360
    }
361 362
    default:
        break;
363
    }
Christiaan Janssen's avatar
Christiaan Janssen committed
364 365
}

366 367 368 369 370
QmlProfilerStatisticsRelation QmlProfilerStatisticsRelativesModel::relation() const
{
    return m_relation;
}

371
int QmlProfilerStatisticsRelativesModel::count() const
Christiaan Janssen's avatar
Christiaan Janssen committed
372
{
373
    return m_data.count();
Christiaan Janssen's avatar
Christiaan Janssen committed
374 375
}

376 377 378
void QmlProfilerStatisticsRelativesModel::clear()
{
    m_data.clear();
379 380
    m_callStack.clear();
    m_compileStack.clear();
Christiaan Janssen's avatar
Christiaan Janssen committed
381
}
382 383

} // namespace QmlProfiler