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 "qv4profileradapter.h"
++ #include "qqmlprofilerservice.h"
++
++ QT_BEGIN_NAMESPACE
++
++ QV4ProfilerAdapter::QV4ProfilerAdapter(QQmlProfilerService *service, QV4::ExecutionEngine *engine) :
++ m_functionCallPos(0), m_memoryPos(0)
++ {
++ setService(service);
++ engine->setProfiler(new QV4::Profiling::Profiler(engine));
++ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabled,
++ this, &QV4ProfilerAdapter::forwardEnabled);
++ connect(this, &QQmlAbstractProfilerAdapter::profilingEnabledWhileWaiting,
++ this, &QV4ProfilerAdapter::forwardEnabledWhileWaiting, Qt::DirectConnection);
++ connect(this, &QV4ProfilerAdapter::v4ProfilingEnabled,
++ engine->profiler(), &QV4::Profiling::Profiler::startProfiling);
++ connect(this, &QV4ProfilerAdapter::v4ProfilingEnabledWhileWaiting,
++ engine->profiler(), &QV4::Profiling::Profiler::startProfiling, Qt::DirectConnection);
++ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabled,
++ engine->profiler(), &QV4::Profiling::Profiler::stopProfiling);
++ connect(this, &QQmlAbstractProfilerAdapter::profilingDisabledWhileWaiting,
++ engine->profiler(), &QV4::Profiling::Profiler::stopProfiling,
++ Qt::DirectConnection);
++ connect(this, &QQmlAbstractProfilerAdapter::dataRequested,
++ engine->profiler(), &QV4::Profiling::Profiler::reportData);
++ connect(this, &QQmlAbstractProfilerAdapter::referenceTimeKnown,
++ engine->profiler(), &QV4::Profiling::Profiler::setTimer);
++ connect(engine->profiler(), &QV4::Profiling::Profiler::dataReady,
++ this, &QV4ProfilerAdapter::receiveData);
++ }
++
++ qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &messages,
++ QQmlDebugPacket &d)
++ {
++ // Make it const, so that we cannot accidentally detach it.
++ const QList<QV4::Profiling::MemoryAllocationProperties> &memoryData = m_memoryData;
++
++ while (memoryData.size() > m_memoryPos && memoryData[m_memoryPos].timestamp <= until) {
++ const QV4::Profiling::MemoryAllocationProperties &props = memoryData[m_memoryPos];
++ d << props.timestamp << int(MemoryAllocation) << int(props.type) << props.size;
++ ++m_memoryPos;
++ messages.append(d.squeezedData());
++ d.clear();
++ }
++ return memoryData.size() == m_memoryPos ? -1 : memoryData[m_memoryPos].timestamp;
++ }
++
++ qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &messages,
++ qint64 callNext, QQmlDebugPacket &d)
++ {
++ qint64 memoryNext = -1;
++
++ if (callNext == -1) {
++ m_functionLocations.clear();
++ m_functionCallData.clear();
++ m_functionCallPos = 0;
++ memoryNext = appendMemoryEvents(until, messages, d);
++ } else {
++ memoryNext = appendMemoryEvents(qMin(callNext, until), messages, d);
++ }
++
++ if (memoryNext == -1) {
++ m_memoryData.clear();
++ m_memoryPos = 0;
++ return callNext;
++ }
++
++ return callNext == -1 ? memoryNext : qMin(callNext, memoryNext);
++ }
++
++ qint64 QV4ProfilerAdapter::sendMessages(qint64 until, QList<QByteArray> &messages)
++ {
++ QQmlDebugPacket d;
++
++ // Make it const, so that we cannot accidentally detach it.
++ const QList<QV4::Profiling::FunctionCallProperties> &functionCallData = m_functionCallData;
++
++ while (true) {
++ while (!m_stack.isEmpty() &&
++ (m_functionCallPos == functionCallData.size() ||
++ m_stack.top() <= functionCallData[m_functionCallPos].start)) {
++ if (m_stack.top() > until || messages.size() > s_numMessagesPerBatch)
++ return finalizeMessages(until, messages, m_stack.top(), d);
++
++ appendMemoryEvents(m_stack.top(), messages, d);
++ d << m_stack.pop() << int(RangeEnd) << int(Javascript);
++ messages.append(d.squeezedData());
++ d.clear();
++ }
++ while (m_functionCallPos != functionCallData.size() &&
++ (m_stack.empty() || functionCallData[m_functionCallPos].start < m_stack.top())) {
++ const QV4::Profiling::FunctionCallProperties &props =
++ functionCallData[m_functionCallPos];
++ if (props.start > until || messages.size() > s_numMessagesPerBatch)
++ return finalizeMessages(until, messages, props.start, d);
++
++ appendMemoryEvents(props.start, messages, d);
++ auto location = m_functionLocations.constFind(props.id);
++
++ d << props.start << int(RangeStart) << int(Javascript) << static_cast<qint64>(props.id);
++ if (location != m_functionLocations.cend()) {
++ messages.push_back(d.squeezedData());
++ d.clear();
++ d << props.start << int(RangeLocation) << int(Javascript) << location->file << location->line
++ << location->column << static_cast<qint64>(props.id);
++ messages.push_back(d.squeezedData());
++ d.clear();
++ d << props.start << int(RangeData) << int(Javascript) << location->name
++ << static_cast<qint64>(props.id);
++ m_functionLocations.erase(location);
++ }
++ messages.push_back(d.squeezedData());
++ d.clear();
++ m_stack.push(props.end);
++ ++m_functionCallPos;
++ }
++ if (m_stack.empty() && m_functionCallPos == functionCallData.size())
++ return finalizeMessages(until, messages, -1, d);
++ }
++ }
++
++ void QV4ProfilerAdapter::receiveData(
++ const QV4::Profiling::FunctionLocationHash &locations,
++ const QList<QV4::Profiling::FunctionCallProperties> &functionCallData,
++ const QList<QV4::Profiling::MemoryAllocationProperties> &memoryData)
++ {
++ // In rare cases it could be that another flush or stop event is processed while data from
++ // the previous one is still pending. In that case we just append the data.
++ if (m_functionLocations.isEmpty())
++ m_functionLocations = locations;
++ else
++ m_functionLocations.insert(locations);
++
++ if (m_functionCallData.isEmpty())
++ m_functionCallData = functionCallData;
++ else
++ m_functionCallData.append(functionCallData);
++
++ if (m_memoryData.isEmpty())
++ m_memoryData = memoryData;
++ else
++ m_memoryData.append(memoryData);
++
++ service->dataReady(this);
++ }
++
++ quint64 QV4ProfilerAdapter::translateFeatures(quint64 qmlFeatures)
++ {
++ quint64 v4Features = 0;
++ const quint64 one = 1;
++ if (qmlFeatures & (one << ProfileJavaScript))
++ v4Features |= (one << QV4::Profiling::FeatureFunctionCall);
++ if (qmlFeatures & (one << ProfileMemory))
++ v4Features |= (one << QV4::Profiling::FeatureMemoryAllocation);
++ return v4Features;
++ }
++
++ void QV4ProfilerAdapter::forwardEnabled(quint64 features)
++ {
++ emit v4ProfilingEnabled(translateFeatures(features));
++ }
++
++ void QV4ProfilerAdapter::forwardEnabledWhileWaiting(quint64 features)
++ {
++ emit v4ProfilingEnabledWhileWaiting(translateFeatures(features));
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qv4profileradapter.cpp"