aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp
blob: 0b50ca9f0a3f5b91d1c592c8e2f3853f2df9f62d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
// 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

#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.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 QVector<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 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"