Source code

Revision control

Copy as Markdown

// 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"