qmlprofilerdatamodel.cpp 10.3 KB
Newer Older
Christiaan Janssen's avatar
Christiaan Janssen committed
1
2
/****************************************************************************
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
Christiaan Janssen's avatar
Christiaan Janssen committed
4
5
6
7
8
9
10
11
12
** Contact: http://www.qt-project.org/legal
**
** 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 Digia.  For licensing terms and
Eike Ziller's avatar
Eike Ziller committed
13
14
** conditions see http://www.qt.io/licensing.  For further information
** use the contact form at http://www.qt.io/contact-us.
Christiaan Janssen's avatar
Christiaan Janssen committed
15
16
17
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
Eike Ziller's avatar
Eike Ziller committed
18
19
20
21
22
23
** 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.
Christiaan Janssen's avatar
Christiaan Janssen committed
24
25
26
27
28
29
30
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/

31
#include "qmlprofilerdatamodel.h"
32
#include "qmlprofilerbasemodel_p.h"
33
#include "qmlprofilermodelmanager.h"
34
#include "notesmodel.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
35
36
37
38
#include <qmldebug/qmlprofilereventtypes.h>
#include <utils/qtcassert.h>
#include <QUrl>
#include <QDebug>
39
#include <algorithm>
Christiaan Janssen's avatar
Christiaan Janssen committed
40
41
42

namespace QmlProfiler {

43
44
45
46
47
class QmlProfilerDataModel::QmlProfilerDataModelPrivate :
        public QmlProfilerBaseModel::QmlProfilerBaseModelPrivate
{
public:
    QmlProfilerDataModelPrivate(QmlProfilerDataModel *qq) : QmlProfilerBaseModelPrivate(qq) {}
48
    QVector<QmlEventTypeData> eventTypes;
49
    QVector<QmlEventData> eventList;
50
    QVector<QmlEventNoteData> eventNotes;
51
    QHash<QmlEventTypeData, int> eventTypeIds;
52
53
54
55
private:
    Q_DECLARE_PUBLIC(QmlProfilerDataModel)
};

56
QString getDisplayName(const QmlProfilerDataModel::QmlEventTypeData &event)
Christiaan Janssen's avatar
Christiaan Janssen committed
57
{
58
59
60
61
62
63
    if (event.location.filename.isEmpty()) {
        return QmlProfilerDataModel::tr("<bytecode>");
    } else {
        const QString filePath = QUrl(event.location.filename).path();
        return filePath.mid(filePath.lastIndexOf(QLatin1Char('/')) + 1) + QLatin1Char(':') +
                QString::number(event.location.line);
Christiaan Janssen's avatar
Christiaan Janssen committed
64
65
66
    }
}

67
QString getInitialDetails(const QmlProfilerDataModel::QmlEventTypeData &event)
Christiaan Janssen's avatar
Christiaan Janssen committed
68
69
70
{
    QString details;
    // generate details string
71
    if (!event.data.isEmpty()) {
72
        details = event.data;
73
        details = details.replace(QLatin1Char('\n'),QLatin1Char(' ')).simplified();
74
        if (details.isEmpty()) {
75
76
            if (event.rangeType == QmlDebug::Javascript)
                details = QmlProfilerDataModel::tr("anonymous function");
77
78
79
80
81
        } else {
            QRegExp rewrite(QLatin1String("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)"));
            bool match = rewrite.exactMatch(details);
            if (match)
                details = rewrite.cap(1) + QLatin1String(": ") + rewrite.cap(3);
82
83
            if (details.startsWith(QLatin1String("file://")) ||
                    details.startsWith(QLatin1String("qrc:/")))
84
85
                details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1);
        }
86
87
88
    } else if (event.rangeType == QmlDebug::Painting) {
        // QtQuick1 animations always run in GUI thread.
        details = QmlProfilerDataModel::tr("GUI Thread");
Christiaan Janssen's avatar
Christiaan Janssen committed
89
90
91
92
93
94
95
96
    }

    return details;
}


//////////////////////////////////////////////////////////////////////////////

97
98
QmlProfilerDataModel::QmlProfilerDataModel(Utils::FileInProjectFinder *fileFinder,
                                                     QmlProfilerModelManager *parent)
99
    : QmlProfilerBaseModel(fileFinder, parent, new QmlProfilerDataModelPrivate(this))
Christiaan Janssen's avatar
Christiaan Janssen committed
100
{
101
    Q_D(QmlProfilerDataModel);
102
    // The document loading is very expensive.
103
    d->modelManager->setProxyCountWeight(d->modelId, 4);
104
}
105

106
107
const QVector<QmlProfilerDataModel::QmlEventData> &QmlProfilerDataModel::getEvents() const
{
108
109
    Q_D(const QmlProfilerDataModel);
    return d->eventList;
Christiaan Janssen's avatar
Christiaan Janssen committed
110
111
}

112
113
114
115
116
117
const QVector<QmlProfilerDataModel::QmlEventTypeData> &QmlProfilerDataModel::getEventTypes() const
{
    Q_D(const QmlProfilerDataModel);
    return d->eventTypes;
}

118
119
120
121
122
123
const QVector<QmlProfilerDataModel::QmlEventNoteData> &QmlProfilerDataModel::getEventNotes() const
{
    Q_D(const QmlProfilerDataModel);
    return d->eventNotes;
}

124
125
126
127
128
129
130
131
132
133
134
135
void QmlProfilerDataModel::setData(const QVector<QmlProfilerDataModel::QmlEventTypeData> &types,
                                   const QVector<QmlProfilerDataModel::QmlEventData> &events)
{
    Q_D(QmlProfilerDataModel);
    d->eventList = events;
    d->eventTypes = types;
    for (int id = 0; id < types.count(); ++id)
        d->eventTypeIds[types[id]] = id;
    // Half the work is done. complete() will do the rest.
    d->modelManager->modelProxyCountUpdated(d->modelId, 1, 2);
}

136
137
138
139
140
141
void QmlProfilerDataModel::setNoteData(const QVector<QmlProfilerDataModel::QmlEventNoteData> &notes)
{
    Q_D(QmlProfilerDataModel);
    d->eventNotes = notes;
}

142
int QmlProfilerDataModel::count() const
Christiaan Janssen's avatar
Christiaan Janssen committed
143
{
144
145
    Q_D(const QmlProfilerDataModel);
    return d->eventList.count();
Christiaan Janssen's avatar
Christiaan Janssen committed
146
147
}

148
void QmlProfilerDataModel::clear()
Christiaan Janssen's avatar
Christiaan Janssen committed
149
{
150
151
    Q_D(QmlProfilerDataModel);
    d->eventList.clear();
152
153
    d->eventTypes.clear();
    d->eventTypeIds.clear();
154
    d->eventNotes.clear();
155
    // This call emits changed(). Don't emit it again here.
156
157
158
159
160
    QmlProfilerBaseModel::clear();
}

bool QmlProfilerDataModel::isEmpty() const
{
161
162
    Q_D(const QmlProfilerDataModel);
    return d->eventList.isEmpty();
Christiaan Janssen's avatar
Christiaan Janssen committed
163
164
}

165
166
167
168
169
170
inline static bool operator<(const QmlProfilerDataModel::QmlEventData &t1,
                             const QmlProfilerDataModel::QmlEventData &t2)
{
    return t1.startTime < t2.startTime;
}

171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
inline static uint qHash(const QmlProfilerDataModel::QmlEventTypeData &type)
{
    return qHash(type.location.filename) ^
            ((type.location.line & 0xfff) |             // 12 bits of line number
            ((type.message << 12) & 0xf000) |           // 4 bits of message
            ((type.location.column << 16) & 0xff0000) | // 8 bits of column
            ((type.rangeType << 24) & 0xf000000) |      // 4 bits of rangeType
            ((type.detailType << 28) & 0xf0000000));    // 4 bits of detailType
}

inline static bool operator==(const QmlProfilerDataModel::QmlEventTypeData &type1,
                              const QmlProfilerDataModel::QmlEventTypeData &type2)
{
    return type1.message == type2.message && type1.rangeType == type2.rangeType &&
            type1.detailType == type2.detailType && type1.location.line == type2.location.line &&
            type1.location.column == type2.location.column &&
            // compare filename last as it's expensive.
            type1.location.filename == type2.location.filename;
}

191
void QmlProfilerDataModel::complete()
Christiaan Janssen's avatar
Christiaan Janssen committed
192
{
193
    Q_D(QmlProfilerDataModel);
Christiaan Janssen's avatar
Christiaan Janssen committed
194
195
    // post-processing

196
197
    // sort events by start time, using above operator<
    std::sort(d->eventList.begin(), d->eventList.end());
Christiaan Janssen's avatar
Christiaan Janssen committed
198
199

    // rewrite strings
200
    int n = d->eventTypes.count();
Christiaan Janssen's avatar
Christiaan Janssen committed
201
    for (int i = 0; i < n; i++) {
202
        QmlEventTypeData *event = &d->eventTypes[i];
Christiaan Janssen's avatar
Christiaan Janssen committed
203
        event->displayName = getDisplayName(*event);
204
        event->data = getInitialDetails(*event);
Christiaan Janssen's avatar
Christiaan Janssen committed
205
206
207
208
209

        //
        // request further details from files
        //

210
        if (event->rangeType != QmlDebug::Binding && event->rangeType != QmlDebug::HandlingSignal)
Christiaan Janssen's avatar
Christiaan Janssen committed
211
212
213
214
215
216
217
218
219
220
            continue;

        // This skips anonymous bindings in Qt4.8 (we don't have valid location data for them)
        if (event->location.filename.isEmpty())
            continue;

        // Skip non-anonymous bindings from Qt4.8 (we already have correct details for them)
        if (event->location.column == -1)
            continue;

221
222
        d->detailsRewriter->requestDetailsForLocation(i, event->location);
        d->modelManager->modelProxyCountUpdated(d->modelId, i + n, n * 2);
Christiaan Janssen's avatar
Christiaan Janssen committed
223
224
    }

225
    // Allow changed() event only after documents have been reloaded to avoid
226
    // unnecessary updates of child models.
227
    QmlProfilerBaseModel::complete();
Christiaan Janssen's avatar
Christiaan Janssen committed
228
229
}

230
231
void QmlProfilerDataModel::addQmlEvent(QmlDebug::Message message, QmlDebug::RangeType rangeType,
                                            int detailType, qint64 startTime,
232
                                            qint64 duration, const QString &data,
233
234
235
                                            const QmlDebug::QmlEventLocation &location,
                                            qint64 ndata1, qint64 ndata2, qint64 ndata3,
                                            qint64 ndata4, qint64 ndata5)
Christiaan Janssen's avatar
Christiaan Janssen committed
236
{
237
    Q_D(QmlProfilerDataModel);
238
    QString displayName;
Christiaan Janssen's avatar
Christiaan Janssen committed
239

240
    QmlEventTypeData typeData = {displayName, location, message, rangeType, detailType, data};
241
242
243
244
245
246
247
248

    // Special case for QtQuick 1 Compiling and Creating events: filename is in the "data" field.
    if ((rangeType == QmlDebug::Compiling || rangeType == QmlDebug::Creating) &&
            location.filename.isEmpty()) {
        typeData.location.filename = data;
        typeData.location.line = typeData.location.column = 1;
    }

249
250
251
252
253
254
255
256
257
258
259
    QmlEventData eventData = {-1, startTime, duration, ndata1, ndata2, ndata3, ndata4, ndata5};

    QHash<QmlEventTypeData, int>::Iterator it = d->eventTypeIds.find(typeData);
    if (it != d->eventTypeIds.end()) {
        eventData.typeIndex = it.value();
    } else {
        eventData.typeIndex = d->eventTypes.size();
        d->eventTypeIds[typeData] = eventData.typeIndex;
        d->eventTypes.append(typeData);
    }

260
    d->eventList.append(eventData);
261

262
263
    d->modelManager->modelProxyCountUpdated(d->modelId, startTime,
                                            d->modelManager->estimatedProfilingTime() * 2);
Christiaan Janssen's avatar
Christiaan Janssen committed
264
265
}

266
267
qint64 QmlProfilerDataModel::lastTimeMark() const
{
268
269
    Q_D(const QmlProfilerDataModel);
    if (d->eventList.isEmpty())
270
271
        return 0;

272
    return d->eventList.last().startTime + d->eventList.last().duration;
Christiaan Janssen's avatar
Christiaan Janssen committed
273
}
274
275
276

void QmlProfilerDataModel::detailsChanged(int requestId, const QString &newString)
{
277
    Q_D(QmlProfilerDataModel);
278
    QTC_ASSERT(requestId < d->eventTypes.count(), return);
279

280
    QmlEventTypeData *event = &d->eventTypes[requestId];
281
    event->data = newString;
282
283
}

Christiaan Janssen's avatar
Christiaan Janssen committed
284
}