Revision control

++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qqmlprofilerclient_p_p.h"
++ #include "qqmldebugconnection_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ QQmlProfilerClientPrivate::~QQmlProfilerClientPrivate()
++ {
++ }
++
++ int QQmlProfilerClientPrivate::resolveType(const QQmlProfilerTypedEvent &event)
++ {
++ int typeIndex = -1;
++ if (event.serverTypeId != 0) {
++ QHash<qint64, int>::ConstIterator it = serverTypeIds.constFind(event.serverTypeId);
++
++ if (it != serverTypeIds.constEnd()) {
++ typeIndex = it.value();
++ } else {
++ typeIndex = eventReceiver->numLoadedEventTypes();
++ eventReceiver->addEventType(event.type);
++ serverTypeIds[event.serverTypeId] = typeIndex;
++ }
++ } else {
++ QHash<QQmlProfilerEventType, int>::ConstIterator it = eventTypeIds.constFind(event.type);
++
++ if (it != eventTypeIds.constEnd()) {
++ typeIndex = it.value();
++ } else {
++ typeIndex = eventReceiver->numLoadedEventTypes();
++ eventReceiver->addEventType(event.type);
++ eventTypeIds[event.type] = typeIndex;
++ }
++ }
++ return typeIndex;
++ }
++
++ int QQmlProfilerClientPrivate::resolveStackTop()
++ {
++ if (rangesInProgress.isEmpty())
++ return -1;
++
++ QQmlProfilerTypedEvent &typedEvent = rangesInProgress.top();
++ int typeIndex = typedEvent.event.typeIndex();
++ if (typeIndex >= 0)
++ return typeIndex;
++
++ typeIndex = resolveType(typedEvent);
++ typedEvent.event.setTypeIndex(typeIndex);
++ while (!pendingMessages.isEmpty()
++ && pendingMessages.head().timestamp() < typedEvent.event.timestamp()) {
++ forwardEvents(pendingMessages.dequeue());
++ }
++ forwardEvents(typedEvent.event);
++ return typeIndex;
++ }
++
++ void QQmlProfilerClientPrivate::forwardEvents(const QQmlProfilerEvent &last)
++ {
++ forwardDebugMessages(last.timestamp());
++ eventReceiver->addEvent(last);
++ }
++
++ void QQmlProfilerClientPrivate::forwardDebugMessages(qint64 untilTimestamp)
++ {
++ while (!pendingDebugMessages.isEmpty()
++ && pendingDebugMessages.front().timestamp() <= untilTimestamp) {
++ eventReceiver->addEvent(pendingDebugMessages.dequeue());
++ }
++ }
++
++ void QQmlProfilerClientPrivate::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.
++ Message rangeStage = currentEvent.type.rangeType() == MaximumRangeType ?
++ currentEvent.type.message() : currentEvent.event.rangeStage();
++ switch (rangeStage) {
++ case RangeStart:
++ resolveStackTop();
++ rangesInProgress.push(currentEvent);
++ break;
++ case RangeEnd: {
++ int typeIndex = resolveStackTop();
++ if (typeIndex == -1)
++ break;
++ currentEvent.event.setTypeIndex(typeIndex);
++ while (!pendingMessages.isEmpty())
++ forwardEvents(pendingMessages.dequeue());
++ forwardEvents(currentEvent.event);
++ rangesInProgress.pop();
++ break;
++ }
++ case RangeData:
++ if (!rangesInProgress.isEmpty())
++ rangesInProgress.top().type.setData(currentEvent.type.data());
++ break;
++ case RangeLocation:
++ if (!rangesInProgress.isEmpty())
++ rangesInProgress.top().type.setLocation(currentEvent.type.location());
++ break;
++ case DebugMessage:
++ currentEvent.event.setTypeIndex(resolveType(currentEvent));
++ pendingDebugMessages.enqueue(currentEvent.event);
++ break;
++ default: {
++ int typeIndex = resolveType(currentEvent);
++ currentEvent.event.setTypeIndex(typeIndex);
++ if (rangesInProgress.isEmpty())
++ forwardEvents(currentEvent.event);
++ else
++ pendingMessages.enqueue(currentEvent.event);
++ break;
++ }
++ }
++ }
++
++ void QQmlProfilerClientPrivate::sendRecordingStatus(int engineId)
++ {
++ Q_Q(QQmlProfilerClient);
++ QPacket stream(connection->currentDataStreamVersion());
++ stream << recording << engineId; // engineId -1 is OK. It means "all of them"
++ if (recording) {
++ stream << requestedFeatures << flushInterval;
++ stream << true; // yes, we support type IDs
++ }
++ q->sendMessage(stream.data());
++ }
++
++ QQmlProfilerClient::QQmlProfilerClient(QQmlDebugConnection *connection,
++ QQmlProfilerEventReceiver *eventReceiver,
++ quint64 features)
++ : QQmlDebugClient(*(new QQmlProfilerClientPrivate(connection, eventReceiver)))
++ {
++ Q_D(QQmlProfilerClient);
++ setRequestedFeatures(features);
++ connect(this, &QQmlDebugClient::stateChanged, this, &QQmlProfilerClient::onStateChanged);
++ connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded,
++ this, &QQmlProfilerClient::sendRecordingStatus);
++ connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeRemoved,
++ this, [d](int engineId) {
++ // We may already be done with that engine. Then we don't need to block it.
++ if (d->trackedEngines.contains(engineId))
++ d->engineControl->blockEngine(engineId);
++ });
++ connect(this, &QQmlProfilerClient::traceFinished,
++ d->engineControl.data(), [d](qint64 timestamp, const QList<int> &engineIds) {
++ Q_UNUSED(timestamp);
++ // The engines might not be blocked because the trace can get finished before engine control
++ // sees them.
++ for (int blocked : d->engineControl->blockedEngines()) {
++ if (engineIds.contains(blocked))
++ d->engineControl->releaseEngine(blocked);
++ }
++ });
++ }
++
++ QQmlProfilerClient::~QQmlProfilerClient()
++ {
++ //Disable profiling if started by client
++ //Profiling data will be lost!!
++ if (isRecording())
++ setRecording(false);
++ }
++
++ void QQmlProfilerClient::clearEvents()
++ {
++ Q_D(QQmlProfilerClient);
++ d->rangesInProgress.clear();
++ d->pendingMessages.clear();
++ d->pendingDebugMessages.clear();
++ if (d->recordedFeatures != 0) {
++ d->recordedFeatures = 0;
++ emit recordedFeaturesChanged(0);
++ }
++ emit cleared();
++ }
++
++ void QQmlProfilerClient::clearAll()
++ {
++ Q_D(QQmlProfilerClient);
++ d->serverTypeIds.clear();
++ d->eventTypeIds.clear();
++ d->trackedEngines.clear();
++ clearEvents();
++ }
++
++ void QQmlProfilerClientPrivate::finalize()
++ {
++ while (!rangesInProgress.isEmpty()) {
++ currentEvent = rangesInProgress.top();
++ currentEvent.event.setRangeStage(RangeEnd);
++ currentEvent.event.setTimestamp(maximumTime);
++ processCurrentEvent();
++ }
++ forwardDebugMessages(std::numeric_limits<qint64>::max());
++ }
++
++
++ void QQmlProfilerClient::sendRecordingStatus(int engineId)
++ {
++ Q_D(QQmlProfilerClient);
++ d->sendRecordingStatus(engineId);
++ }
++
++ bool QQmlProfilerClient::isRecording() const
++ {
++ Q_D(const QQmlProfilerClient);
++ return d->recording;
++ }
++
++ void QQmlProfilerClient::setRecording(bool v)
++ {
++ Q_D(QQmlProfilerClient);
++ if (v == d->recording)
++ return;
++
++ d->recording = v;
++
++ if (state() == Enabled)
++ sendRecordingStatus();
++
++ emit recordingChanged(v);
++ }
++
++ quint64 QQmlProfilerClient::recordedFeatures() const
++ {
++ Q_D(const QQmlProfilerClient);
++ return d->recordedFeatures;
++ }
++
++ void QQmlProfilerClient::receiveDebugMessage(
++ QtMsgType type, const QString &text, const QQmlDebugContextInfo &context)
++ {
++ Q_D(QQmlProfilerClient);
++ d->updateFeatures(ProfileDebugMessages);
++ d->currentEvent.event.setTimestamp(context.timestamp > 0 ? context.timestamp : 0);
++ d->currentEvent.event.setTypeIndex(-1);
++ d->currentEvent.event.setString(text);
++ d->currentEvent.type = QQmlProfilerEventType(
++ DebugMessage, MaximumRangeType, type,
++ QQmlProfilerEventLocation(context.file, context.line, 1));
++ d->currentEvent.serverTypeId = 0;
++ d->processCurrentEvent();
++ }
++
++ void QQmlProfilerClient::setRequestedFeatures(quint64 features)
++ {
++ Q_D(QQmlProfilerClient);
++
++ const quint64 messagesFlag = 1ull << ProfileDebugMessages;
++ const bool hadMessagesBefore = d->requestedFeatures & messagesFlag;
++ const bool hasMessagesNow = features & messagesFlag;
++
++ d->requestedFeatures = features;
++
++ if (!hadMessagesBefore && hasMessagesNow) {
++ connect(d->messageClient.data(), &QQmlDebugMessageClient::message,
++ this, &QQmlProfilerClient::receiveDebugMessage);
++ } else if (hadMessagesBefore && !hasMessagesNow) {
++ disconnect(d->messageClient.data(), &QQmlDebugMessageClient::message,
++ this, &QQmlProfilerClient::receiveDebugMessage);
++ }
++ }
++
++ void QQmlProfilerClient::setFlushInterval(quint32 flushInterval)
++ {
++ Q_D(QQmlProfilerClient);
++ d->flushInterval = flushInterval;
++ }
++
++ QQmlProfilerClient::QQmlProfilerClient(QQmlProfilerClientPrivate &dd) :
++ QQmlDebugClient(dd)
++ {
++ Q_D(QQmlProfilerClient);
++ connect(d->engineControl.data(), &QQmlEngineControlClient::engineAboutToBeAdded,
++ this, &QQmlProfilerClient::sendRecordingStatus);
++ }
++
++ bool QQmlProfilerClientPrivate::updateFeatures(ProfileFeature feature)
++ {
++ Q_Q(QQmlProfilerClient);
++ quint64 flag = 1ULL << feature;
++ if (!(requestedFeatures & flag))
++ return false;
++ if (!(recordedFeatures & flag)) {
++ recordedFeatures |= flag;
++ emit q->recordedFeaturesChanged(recordedFeatures);
++ }
++ return true;
++ }
++
++ void QQmlProfilerClient::onStateChanged(State status)
++ {
++ if (status == Enabled) {
++ sendRecordingStatus(-1);
++ } else {
++ Q_D(QQmlProfilerClient);
++ d->finalize();
++ }
++
++ }
++
++ void QQmlProfilerClient::messageReceived(const QByteArray &data)
++ {
++ Q_D(QQmlProfilerClient);
++ QPacket stream(d->connection->currentDataStreamVersion(), data);
++
++ stream >> d->currentEvent;
++
++ d->maximumTime = qMax(d->currentEvent.event.timestamp(), d->maximumTime);
++ if (d->currentEvent.type.message() == Complete) {
++ d->finalize();
++ emit complete(d->maximumTime);
++ } else if (d->currentEvent.type.message() == Event
++ && d->currentEvent.type.detailType() == StartTrace) {
++ const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>();
++ d->trackedEngines.append(engineIds);
++ d->forwardDebugMessages(d->currentEvent.event.timestamp());
++ emit traceStarted(d->currentEvent.event.timestamp(), engineIds);
++ } else if (d->currentEvent.type.message() == Event
++ && d->currentEvent.type.detailType() == EndTrace) {
++ const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>();
++ for (int engineId : engineIds)
++ d->trackedEngines.removeAll(engineId);
++ d->forwardDebugMessages(d->currentEvent.event.timestamp());
++ emit traceFinished(d->currentEvent.event.timestamp(), engineIds);
++ } else if (d->updateFeatures(d->currentEvent.type.feature())) {
++ d->processCurrentEvent();
++ }
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qqmlprofilerclient_p.cpp"