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