qmlprofilertraceclient.cpp 10.3 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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.
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.
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
25 26

#include "qmlprofilertraceclient.h"
27
#include "qmltypedevent.h"
28
#include "qmlprofilerdatamodel.h"
29

30 31 32
#include <qmldebug/qmlenginecontrolclient.h>
#include <qmldebug/qdebugmessageclient.h>
#include <qmldebug/qpacketprotocol.h>
33
#include <utils/qtcassert.h>
34

35 36
#include <QQueue>

37
namespace QmlProfiler {
Kai Koehne's avatar
Kai Koehne committed
38 39 40

class QmlProfilerTraceClientPrivate {
public:
41 42
    QmlProfilerTraceClientPrivate(QmlProfilerTraceClient *_q, QmlDebug::QmlDebugConnection *client,
                                  QmlProfilerDataModel *model)
43
        : q(_q)
44
        , model(model)
Ulf Hermann's avatar
Ulf Hermann committed
45
        , engineControl(client)
Kai Koehne's avatar
Kai Koehne committed
46 47
        , maximumTime(0)
        , recording(false)
48 49
        , requestedFeatures(0)
        , recordedFeatures(0)
50
        , flushInterval(0)
Kai Koehne's avatar
Kai Koehne committed
51 52 53
    {
    }

Ulf Hermann's avatar
Ulf Hermann committed
54
    void sendRecordingStatus(int engineId);
55
    bool updateFeatures(ProfileFeature feature);
56
    int resolveType(const QmlTypedEvent &type);
57 58
    int resolveStackTop();
    void processCurrentEvent();
59 60

    QmlProfilerTraceClient *q;
61
    QmlProfilerDataModel *model;
62 63
    QmlDebug::QmlEngineControlClient engineControl;
    QScopedPointer<QmlDebug::QDebugMessageClient> messageClient;
Kai Koehne's avatar
Kai Koehne committed
64 65
    qint64 maximumTime;
    bool recording;
66 67
    quint64 requestedFeatures;
    quint64 recordedFeatures;
68
    quint32 flushInterval;
69 70 71

    // Reuse the same event, so that we don't have to constantly reallocate all the data.
    QmlTypedEvent currentEvent;
72
    QHash<QmlEventType, int> eventTypeIds;
73
    QHash<qint64, int> serverTypeIds;
74
    QStack<QmlTypedEvent> rangesInProgress;
75
    QQueue<QmlEvent> pendingMessages;
Kai Koehne's avatar
Kai Koehne committed
76 77
};

78
int QmlProfilerTraceClientPrivate::resolveType(const QmlTypedEvent &event)
79 80
{
    int typeIndex = -1;
81 82 83 84 85 86 87 88 89
    if (event.serverTypeId != 0) {
        QHash<qint64, int>::ConstIterator it = serverTypeIds.constFind(event.serverTypeId);

        if (it != serverTypeIds.constEnd()) {
            typeIndex = it.value();
        } else {
            typeIndex = model->addEventType(event.type);
            serverTypeIds[event.serverTypeId] = typeIndex;
        }
90
    } else {
91 92 93 94 95 96 97 98
        QHash<QmlEventType, int>::ConstIterator it = eventTypeIds.constFind(event.type);

        if (it != eventTypeIds.constEnd()) {
            typeIndex = it.value();
        } else {
            typeIndex = model->addEventType(event.type);
            eventTypeIds[event.type] = typeIndex;
        }
99 100 101 102 103 104 105 106 107 108 109 110 111 112
    }
    return typeIndex;
}

int QmlProfilerTraceClientPrivate::resolveStackTop()
{
    if (rangesInProgress.isEmpty())
        return -1;

    QmlTypedEvent &typedEvent = rangesInProgress.top();
    int typeIndex = typedEvent.event.typeIndex();
    if (typeIndex >= 0)
        return typeIndex;

113
    typeIndex = resolveType(typedEvent);
114
    typedEvent.event.setTypeIndex(typeIndex);
115 116 117 118
    while (!pendingMessages.isEmpty()
           && pendingMessages.head().timestamp() < typedEvent.event.timestamp()) {
        model->addEvent(pendingMessages.dequeue());
    }
119 120 121 122 123 124 125 126 127 128
    model->addEvent(typedEvent.event);
    return typeIndex;
}

void QmlProfilerTraceClientPrivate::processCurrentEvent()
{
    // RangeData and RangeLocation always apply to the range on the top of the stack. Furthermore,
    // all ranges are perfectly nested. This is why we can defer the type resolution until either
    // the range ends or a child range starts. With only the information in RangeStart we wouldn't
    // be able to uniquely identify the event type.
129 130
    Message rangeStage = currentEvent.type.rangeType() == MaximumRangeType ?
                currentEvent.type.message() : currentEvent.event.rangeStage();
131 132 133 134 135 136 137 138 139
    switch (rangeStage) {
    case RangeStart:
        resolveStackTop();
        rangesInProgress.push(currentEvent);
        break;
    case RangeEnd: {
        int typeIndex = resolveStackTop();
        QTC_ASSERT(typeIndex != -1, break);
        currentEvent.event.setTypeIndex(typeIndex);
140 141
        while (!pendingMessages.isEmpty())
            model->addEvent(pendingMessages.dequeue());
142 143 144 145 146
        model->addEvent(currentEvent.event);
        rangesInProgress.pop();
        break;
    }
    case RangeData:
147
        rangesInProgress.top().type.setData(currentEvent.type.data());
148 149
        break;
    case RangeLocation:
150
        rangesInProgress.top().type.setLocation(currentEvent.type.location());
151 152
        break;
    default: {
153
        int typeIndex = resolveType(currentEvent);
154
        currentEvent.event.setTypeIndex(typeIndex);
155 156 157 158
        if (rangesInProgress.isEmpty())
            model->addEvent(currentEvent.event);
        else
            pendingMessages.enqueue(currentEvent.event);
159 160 161 162 163
        break;
    }
    }
}

Ulf Hermann's avatar
Ulf Hermann committed
164
void QmlProfilerTraceClientPrivate::sendRecordingStatus(int engineId)
165
{
166
    QmlDebug::QPacket stream(q->connection()->currentDataStreamVersion());
167
    stream << recording << engineId; // engineId -1 is OK. It means "all of them"
168
    if (recording) {
169
        stream << requestedFeatures << flushInterval;
170 171
        stream << true; // yes, we support type IDs
    }
172
    q->sendMessage(stream.data());
173 174
}

175
QmlProfilerTraceClient::QmlProfilerTraceClient(QmlDebug::QmlDebugConnection *client,
176
                                               QmlProfilerDataModel *model,
177
                                               quint64 features)
178
    : QmlDebugClient(QLatin1String("CanvasFrameRate"), client)
179
    , d(new QmlProfilerTraceClientPrivate(this, client, model))
Kai Koehne's avatar
Kai Koehne committed
180
{
181
    setRequestedFeatures(features);
182
    connect(&d->engineControl, &QmlDebug::QmlEngineControlClient::engineAboutToBeAdded,
183
            this, &QmlProfilerTraceClient::sendRecordingStatus);
Kai Koehne's avatar
Kai Koehne committed
184 185 186 187
}

QmlProfilerTraceClient::~QmlProfilerTraceClient()
{
188 189 190 191
    //Disable profiling if started by client
    //Profiling data will be lost!!
    if (isRecording())
        setRecording(false);
Kai Koehne's avatar
Kai Koehne committed
192 193 194 195
    delete d;
}

void QmlProfilerTraceClient::clearData()
196
{
197 198
    d->eventTypeIds.clear();
    d->rangesInProgress.clear();
199 200 201 202
    if (d->recordedFeatures != 0) {
        d->recordedFeatures = 0;
        emit recordedFeaturesChanged(0);
    }
Kai Koehne's avatar
Kai Koehne committed
203
    emit cleared();
204 205
}

206
void QmlProfilerTraceClient::sendRecordingStatus(int engineId)
207
{
208
    d->sendRecordingStatus(engineId);
209 210
}

Kai Koehne's avatar
Kai Koehne committed
211
bool QmlProfilerTraceClient::isRecording() const
212
{
Kai Koehne's avatar
Kai Koehne committed
213
    return d->recording;
214 215 216 217
}

void QmlProfilerTraceClient::setRecording(bool v)
{
Kai Koehne's avatar
Kai Koehne committed
218
    if (v == d->recording)
219 220
        return;

221 222
    d->recording = v;

223
    if (state() == Enabled)
224
        sendRecordingStatus();
225 226 227 228

    emit recordingChanged(v);
}

229 230 231 232 233 234
quint64 QmlProfilerTraceClient::recordedFeatures() const
{
    return d->recordedFeatures;
}

void QmlProfilerTraceClient::setRequestedFeatures(quint64 features)
235
{
236
    d->requestedFeatures = features;
237
    if (features & static_cast<quint64>(1) << ProfileDebugMessages) {
238 239 240 241
        d->messageClient.reset(new QmlDebug::QDebugMessageClient(connection()));
        connect(d->messageClient.data(), &QmlDebug::QDebugMessageClient::message, this,
                [this](QtMsgType type, const QString &text,
                       const QmlDebug::QDebugContextInfo &context)
242
        {
243
            d->updateFeatures(ProfileDebugMessages);
244 245 246
            d->currentEvent.event.setTimestamp(context.timestamp);
            d->currentEvent.event.setTypeIndex(-1);
            d->currentEvent.event.setString(text);
247 248
            d->currentEvent.type = QmlEventType(DebugMessage, MaximumRangeType, type,
                                                QmlEventLocation(context.file, context.line, 1));
249
            d->currentEvent.serverTypeId = 0;
250
            d->processCurrentEvent();
251 252 253 254
        });
    } else {
        d->messageClient.reset();
    }
255 256
}

257 258 259 260 261
void QmlProfilerTraceClient::setFlushInterval(quint32 flushInterval)
{
    d->flushInterval = flushInterval;
}

262 263 264 265 266 267
bool QmlProfilerTraceClientPrivate::updateFeatures(ProfileFeature feature)
{
    quint64 flag = 1ULL << feature;
    if (!(requestedFeatures & flag))
        return false;
    if (!(recordedFeatures & flag)) {
268
        recordedFeatures |= flag;
269 270 271 272 273
        emit q->recordedFeaturesChanged(recordedFeatures);
    }
    return true;
}

274
void QmlProfilerTraceClient::stateChanged(State status)
275
{
276 277
    if (status == Enabled)
        sendRecordingStatus(-1);
278 279 280 281
}

void QmlProfilerTraceClient::messageReceived(const QByteArray &data)
{
282
    QmlDebug::QPacket stream(connection()->currentDataStreamVersion(), data);
283

284
    stream >> d->currentEvent;
285

286
    d->maximumTime = qMax(d->currentEvent.event.timestamp(), d->maximumTime);
287
    if (d->currentEvent.type.message() == Complete) {
288 289 290 291 292 293
        while (!d->rangesInProgress.isEmpty()) {
            d->currentEvent = d->rangesInProgress.top();
            d->currentEvent.event.setRangeStage(RangeEnd);
            d->currentEvent.event.setTimestamp(d->maximumTime);
            d->processCurrentEvent();
        }
294
        emit complete(d->maximumTime);
295 296
    } else if (d->currentEvent.type.message() == Event
               && d->currentEvent.type.detailType() == StartTrace) {
297 298
        emit traceStarted(d->currentEvent.event.timestamp(),
                          d->currentEvent.event.numbers<QList<int>, qint32>());
299 300
    } else if (d->currentEvent.type.message() == Event
               && d->currentEvent.type.detailType() == EndTrace) {
301 302 303
        emit traceFinished(d->currentEvent.event.timestamp(),
                           d->currentEvent.event.numbers<QList<int>, qint32>());
    } else if (d->updateFeatures(d->currentEvent.type.feature())) {
304
        d->processCurrentEvent();
305
    }
306
}
307 308

} // namespace QmlProfiler