1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "qmlprofilerrangemodel.h"
27 #include "qmlprofilermodelmanager.h"
28 #include "qmlprofilerbindingloopsrenderpass.h"
29
30 #include <tracing/timelinenotesrenderpass.h>
31 #include <tracing/timelineitemsrenderpass.h>
32 #include <tracing/timelineselectionrenderpass.h>
33 #include <tracing/timelineformattime.h>
34
35 #include <QCoreApplication>
36 #include <QVector>
37 #include <QHash>
38 #include <QUrl>
39 #include <QString>
40 #include <QStack>
41
42 namespace QmlProfiler {
43 namespace Internal {
44
QmlProfilerRangeModel(QmlProfilerModelManager * manager,RangeType range,Timeline::TimelineModelAggregator * parent)45 QmlProfilerRangeModel::QmlProfilerRangeModel(QmlProfilerModelManager *manager, RangeType range,
46 Timeline::TimelineModelAggregator *parent) :
47 QmlProfilerTimelineModel(manager, MaximumMessage, range, featureFromRangeType(range), parent)
48 {
49 m_expandedRowTypes << -1;
50 }
51
clear()52 void QmlProfilerRangeModel::clear()
53 {
54 m_expandedRowTypes.clear();
55 m_expandedRowTypes << -1;
56 m_data.clear();
57 m_stack.clear();
58 QmlProfilerTimelineModel::clear();
59 }
60
supportsBindingLoops() const61 bool QmlProfilerRangeModel::supportsBindingLoops() const
62 {
63 return rangeType() == Binding || rangeType() == HandlingSignal;
64 }
65
loadEvent(const QmlEvent & event,const QmlEventType & type)66 void QmlProfilerRangeModel::loadEvent(const QmlEvent &event, const QmlEventType &type)
67 {
68 Q_UNUSED(type)
69 // store starttime-based instance
70 if (event.rangeStage() == RangeStart) {
71 int index = insertStart(event.timestamp(), event.typeIndex());
72 m_stack.append(index);
73 m_data.insert(index, Item());
74 } else if (event.rangeStage() == RangeEnd) {
75 if (!m_stack.isEmpty()) {
76 int index = m_stack.pop();
77 insertEnd(index, event.timestamp() - startTime(index));
78 } else {
79 qWarning() << "Received inconsistent trace data from application.";
80 }
81 }
82 }
83
finalize()84 void QmlProfilerRangeModel::finalize()
85 {
86 if (!m_stack.isEmpty()) {
87 qWarning() << "End times for some events are missing.";
88 const qint64 endTime = modelManager()->traceEnd();
89 do {
90 int index = m_stack.pop();
91 insertEnd(index, endTime - startTime(index));
92 } while (!m_stack.isEmpty());
93 }
94
95 // compute range nesting
96 computeNesting();
97
98 // compute nestingLevel - nonexpanded
99 computeNestingContracted();
100
101 // compute nestingLevel - expanded
102 computeExpandedLevels();
103
104 if (supportsBindingLoops())
105 findBindingLoops();
106
107 QmlProfilerTimelineModel::finalize();
108 }
109
computeNestingContracted()110 void QmlProfilerRangeModel::computeNestingContracted()
111 {
112 int i;
113 int eventCount = count();
114
115 int nestingLevels = Constants::QML_MIN_LEVEL;
116 int collapsedRowCount = nestingLevels + 1;
117 QVector<qint64> nestingEndTimes;
118 nestingEndTimes.fill(0, nestingLevels + 1);
119
120 for (i = 0; i < eventCount; i++) {
121 qint64 st = startTime(i);
122
123 // per type
124 if (nestingEndTimes[nestingLevels] > st) {
125 if (++nestingLevels == nestingEndTimes.size())
126 nestingEndTimes << 0;
127 if (nestingLevels == collapsedRowCount)
128 ++collapsedRowCount;
129 } else {
130 while (nestingLevels > Constants::QML_MIN_LEVEL &&
131 nestingEndTimes[nestingLevels-1] <= st)
132 nestingLevels--;
133 }
134 nestingEndTimes[nestingLevels] = st + duration(i);
135
136 m_data[i].displayRowCollapsed = nestingLevels;
137 }
138 setCollapsedRowCount(collapsedRowCount);
139 }
140
computeExpandedLevels()141 void QmlProfilerRangeModel::computeExpandedLevels()
142 {
143 QHash<int, int> eventRow;
144 int eventCount = count();
145 for (int i = 0; i < eventCount; i++) {
146 int eventTypeId = typeId(i);
147 if (!eventRow.contains(eventTypeId)) {
148 eventRow[eventTypeId] = m_expandedRowTypes.size();
149 m_expandedRowTypes << eventTypeId;
150 }
151 m_data[i].displayRowExpanded = eventRow[eventTypeId];
152 }
153 setExpandedRowCount(m_expandedRowTypes.size());
154 }
155
findBindingLoops()156 void QmlProfilerRangeModel::findBindingLoops()
157 {
158 using CallStackEntry = QPair<int, int>;
159 QStack<CallStackEntry> callStack;
160
161 for (int i = 0; i < count(); ++i) {
162 int potentialParent = callStack.isEmpty() ? -1 : callStack.top().second;
163
164 while (potentialParent != -1 && !(endTime(potentialParent) > startTime(i))) {
165 callStack.pop();
166 potentialParent = callStack.isEmpty() ? -1 : callStack.top().second;
167 }
168
169 // check whether event is already in stack
170 for (int ii = 0; ii < callStack.size(); ++ii) {
171 if (callStack.at(ii).first == typeId(i)) {
172 m_data[i].bindingLoopHead = callStack.at(ii).second;
173 break;
174 }
175 }
176
177 CallStackEntry newEntry(typeId(i), i);
178 callStack.push(newEntry);
179 }
180
181 }
182
expandedRow(int index) const183 int QmlProfilerRangeModel::expandedRow(int index) const
184 {
185 return m_data[index].displayRowExpanded;
186 }
187
collapsedRow(int index) const188 int QmlProfilerRangeModel::collapsedRow(int index) const
189 {
190 return m_data[index].displayRowCollapsed;
191 }
192
bindingLoopDest(int index) const193 int QmlProfilerRangeModel::bindingLoopDest(int index) const
194 {
195 return m_data[index].bindingLoopHead;
196 }
197
color(int index) const198 QRgb QmlProfilerRangeModel::color(int index) const
199 {
200 return colorBySelectionId(index);
201 }
202
labels() const203 QVariantList QmlProfilerRangeModel::labels() const
204 {
205 QVariantList result;
206
207 const QmlProfilerModelManager *manager = modelManager();
208 for (int i = 1; i < expandedRowCount(); i++) { // Ignore the -1 for the first row
209 QVariantMap element;
210 const int typeId = m_expandedRowTypes[i];
211 const QmlEventType &type = manager->eventType(typeId);
212 element.insert(QLatin1String("displayName"), type.displayName());
213 element.insert(QLatin1String("description"), type.data());
214 element.insert(QLatin1String("id"), typeId);
215 result << element;
216 }
217
218 return result;
219 }
220
details(int index) const221 QVariantMap QmlProfilerRangeModel::details(int index) const
222 {
223 QVariantMap result;
224 int id = selectionId(index);
225
226 result.insert(QStringLiteral("displayName"),
227 tr(QmlProfilerModelManager::featureName(mainFeature())));
228 result.insert(tr("Duration"), Timeline::formatTime(duration(index)));
229
230 const QmlEventType &type = modelManager()->eventType(id);
231 result.insert(tr("Details"), type.data());
232 result.insert(tr("Location"), type.displayName());
233 return result;
234 }
235
location(int index) const236 QVariantMap QmlProfilerRangeModel::location(int index) const
237 {
238 return locationFromTypeId(index);
239 }
240
typeId(int index) const241 int QmlProfilerRangeModel::typeId(int index) const
242 {
243 return selectionId(index);
244 }
245
supportedRenderPasses() const246 QList<const Timeline::TimelineRenderPass *> QmlProfilerRangeModel::supportedRenderPasses() const
247 {
248 if (supportsBindingLoops()) {
249 QList<const Timeline::TimelineRenderPass *> passes;
250 passes << Timeline::TimelineItemsRenderPass::instance()
251 << QmlProfilerBindingLoopsRenderPass::instance()
252 << Timeline::TimelineSelectionRenderPass::instance()
253 << Timeline::TimelineNotesRenderPass::instance();
254 return passes;
255 } else {
256 return QmlProfilerTimelineModel::supportedRenderPasses();
257 }
258
259 }
260
261 } // namespace Internal
262 } // namespaec QmlProfiler
263