blob: fb2a28ea019b567e1cadf105a3c56395a0b7e4b1 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 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.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#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 QVector<QV4::Profiling::MemoryAllocationProperties> &memoryData = m_memoryData;
while (memoryData.length() > 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.length() == 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 QVector<QV4::Profiling::FunctionCallProperties> &functionCallData = m_functionCallData;
while (true) {
while (!m_stack.isEmpty() &&
(m_functionCallPos == functionCallData.length() ||
m_stack.top() <= functionCallData[m_functionCallPos].start)) {
if (m_stack.top() > until || messages.length() > 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.length() &&
(m_stack.empty() || functionCallData[m_functionCallPos].start < m_stack.top())) {
const QV4::Profiling::FunctionCallProperties &props =
functionCallData[m_functionCallPos];
if (props.start > until || messages.length() > s_numMessagesPerBatch)
return finalizeMessages(until, messages, props.start, d);
appendMemoryEvents(props.start, messages, d);
auto location = m_functionLocations.find(props.id);
d << props.start << int(RangeStart) << int(Javascript) << static_cast<qint64>(props.id);
if (location != m_functionLocations.end()) {
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.length())
return finalizeMessages(until, messages, -1, d);
}
}
void QV4ProfilerAdapter::receiveData(
const QV4::Profiling::FunctionLocationHash &locations,
const QVector<QV4::Profiling::FunctionCallProperties> &functionCallData,
const QVector<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"