qmlprofilertraceview.cpp 15.7 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
Christiaan Janssen's avatar
Christiaan Janssen committed
2
**
3 4
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
Christiaan Janssen's avatar
Christiaan Janssen committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
Christiaan Janssen's avatar
Christiaan Janssen committed
7
**
hjk's avatar
hjk committed
8 9 10 11
** 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
12 13 14
** 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.
Christiaan Janssen's avatar
Christiaan Janssen committed
15
**
16 17 18 19 20 21 22
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** 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-3.0.html.
Christiaan Janssen's avatar
Christiaan Janssen committed
23
**
hjk's avatar
hjk committed
24
****************************************************************************/
Christiaan Janssen's avatar
Christiaan Janssen committed
25 26 27 28

#include "qmlprofilertraceview.h"
#include "qmlprofilertool.h"
#include "qmlprofilerstatemanager.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
29
#include "qmlprofilermodelmanager.h"
30
#include "qmlprofilernotesmodel.h"
31 32 33
#include "qmlprofileranimationsmodel.h"
#include "qmlprofilerrangemodel.h"
#include "qmlprofilerplugin.h"
Christiaan Janssen's avatar
Christiaan Janssen committed
34

35 36 37 38 39 40 41
#include "inputeventsmodel.h"
#include "pixmapcachemodel.h"
#include "debugmessagesmodel.h"
#include "flamegraphview.h"
#include "memoryusagemodel.h"
#include "scenegraphtimelinemodel.h"

Christiaan Janssen's avatar
Christiaan Janssen committed
42
// Communication with the other views (limit events to range)
Christiaan Janssen's avatar
Christiaan Janssen committed
43
#include "qmlprofilerviewmanager.h"
44 45 46 47 48

#include "timeline/timelinezoomcontrol.h"
#include "timeline/timelinemodelaggregator.h"
#include "timeline/timelinerenderer.h"
#include "timeline/timelineoverviewrenderer.h"
49
#include "timeline/timelinetheme.h"
50
#include "timeline/timelineformattime.h"
51

52
#include <aggregation/aggregate.h>
53
// Needed for the load&save actions in the context menu
54
#include <debugger/analyzer/analyzermanager.h>
55
#include <coreplugin/findplaceholder.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
56
#include <utils/styledbar.h>
57
#include <utils/algorithm.h>
Christiaan Janssen's avatar
Christiaan Janssen committed
58

59
#include <QQmlContext>
Christiaan Janssen's avatar
Christiaan Janssen committed
60 61 62 63 64 65 66
#include <QToolButton>
#include <QEvent>
#include <QVBoxLayout>
#include <QGraphicsObject>
#include <QScrollBar>
#include <QSlider>
#include <QMenu>
67
#include <QQuickItem>
68
#include <QQuickWidget>
69
#include <QApplication>
Samuel Gaist's avatar
Samuel Gaist committed
70
#include <QRegExp>
71
#include <QTextCursor>
Christiaan Janssen's avatar
Christiaan Janssen committed
72 73 74 75 76 77 78 79 80 81 82 83

#include <math.h>

namespace QmlProfiler {
namespace Internal {

class QmlProfilerTraceView::QmlProfilerTraceViewPrivate
{
public:
    QmlProfilerTraceViewPrivate(QmlProfilerTraceView *qq) : q(qq) {}
    QmlProfilerTraceView *q;
    QmlProfilerViewManager *m_viewContainer;
84
    QQuickWidget *m_mainView;
Christiaan Janssen's avatar
Christiaan Janssen committed
85
    QmlProfilerModelManager *m_modelManager;
86
    QVariantList m_suspendedModels;
87 88
    Timeline::TimelineModelAggregator *m_modelProxy;
    Timeline::TimelineZoomControl *m_zoomControl;
Christiaan Janssen's avatar
Christiaan Janssen committed
89 90
};

91 92
QmlProfilerTraceView::QmlProfilerTraceView(QWidget *parent, QmlProfilerViewManager *container,
                                           QmlProfilerModelManager *modelManager)
Christiaan Janssen's avatar
Christiaan Janssen committed
93 94
    : QWidget(parent), d(new QmlProfilerTraceViewPrivate(this))
{
95 96
    setWindowTitle(tr("Timeline"));
    setObjectName("QmlProfiler.Timeline.Dock");
Christiaan Janssen's avatar
Christiaan Janssen committed
97

98
    d->m_zoomControl = new Timeline::TimelineZoomControl(this);
99
    connect(modelManager, &QmlProfilerModelManager::stateChanged, this, [modelManager, this]() {
100 101
        switch (modelManager->state()) {
        case QmlProfilerModelManager::Done: {
102 103 104 105
            qint64 start = modelManager->traceTime()->startTime();
            qint64 end = modelManager->traceTime()->endTime();
            d->m_zoomControl->setTrace(start, end);
            d->m_zoomControl->setRange(start, start + (end - start) / 10);
106 107 108 109 110
            // Fall through
        }
        case QmlProfilerModelManager::Empty:
            d->m_modelProxy->setModels(d->m_suspendedModels);
            d->m_suspendedModels.clear();
111
            d->m_modelManager->notesModel()->loadData();
112 113 114 115
            break;
        case QmlProfilerModelManager::ProcessingData:
            break;
        case QmlProfilerModelManager::ClearingData:
116
            d->m_zoomControl->clear();
117 118
            if (!d->m_suspendedModels.isEmpty())
                break; // Models are suspended already. AcquiringData was aborted.
119 120 121 122 123 124
            // Fall through
        case QmlProfilerModelManager::AcquiringData:
            // Temporarily remove the models, while we're changing them
            d->m_suspendedModels = d->m_modelProxy->models();
            d->m_modelProxy->setModels(QVariantList());
            break;
125
        }
126
    });
Christiaan Janssen's avatar
Christiaan Janssen committed
127 128 129 130 131

    QVBoxLayout *groupLayout = new QVBoxLayout;
    groupLayout->setContentsMargins(0, 0, 0, 0);
    groupLayout->setSpacing(0);

132
    qmlRegisterType<Timeline::TimelineRenderer>("TimelineRenderer", 1, 0, "TimelineRenderer");
133 134
    qmlRegisterType<Timeline::TimelineOverviewRenderer>("TimelineOverviewRenderer", 1, 0,
                                                        "TimelineOverviewRenderer");
135 136 137
    qmlRegisterType<Timeline::TimelineZoomControl>();
    qmlRegisterType<Timeline::TimelineModel>();
    qmlRegisterType<Timeline::TimelineNotesModel>();
138

139 140 141
    d->m_mainView = new QQuickWidget(this);
    d->m_mainView->setResizeMode(QQuickWidget::SizeRootObjectToView);
    d->m_mainView->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
142 143 144 145 146
    setFocusProxy(d->m_mainView);

    Aggregation::Aggregate *agg = new Aggregation::Aggregate;
    agg->add(d->m_mainView);
    agg->add(new TraceViewFindSupport(this, modelManager));
Christiaan Janssen's avatar
Christiaan Janssen committed
147

148
    groupLayout->addWidget(d->m_mainView);
149
    groupLayout->addWidget(new Core::FindToolBarPlaceHolder(this));
Christiaan Janssen's avatar
Christiaan Janssen committed
150 151 152
    setLayout(groupLayout);

    d->m_viewContainer = container;
153
    d->m_modelProxy = new Timeline::TimelineModelAggregator(modelManager->notesModel(), this);
154
    d->m_modelManager = modelManager;
155

156 157 158 159 160 161 162 163 164 165 166 167
    QVariantList models;
    models.append(QVariant::fromValue(new PixmapCacheModel(modelManager, d->m_modelProxy)));
    models.append(QVariant::fromValue(new SceneGraphTimelineModel(modelManager, d->m_modelProxy)));
    models.append(QVariant::fromValue(new MemoryUsageModel(modelManager, d->m_modelProxy)));
    models.append(QVariant::fromValue(new InputEventsModel(modelManager, d->m_modelProxy)));
    models.append(QVariant::fromValue(new DebugMessagesModel(modelManager, d->m_modelProxy)));
    models.append(QVariant::fromValue(new QmlProfilerAnimationsModel(modelManager,
                                                                     d->m_modelProxy)));
    for (int i = 0; i < MaximumRangeType; ++i) {
        models.append(QVariant::fromValue(new QmlProfilerRangeModel(modelManager, (RangeType)i,
                                                                    d->m_modelProxy)));
    }
168
    d->m_modelProxy->setModels(models);
169

170 171
    // Minimum height: 5 rows of 20 pixels + scrollbar of 50 pixels + 20 pixels margin
    setMinimumHeight(170);
Christiaan Janssen's avatar
Christiaan Janssen committed
172

173
    Timeline::TimelineTheme::setupTheme(d->m_mainView->engine());
174
    Timeline::TimeFormatter::setupTimeFormatter();
175

176 177 178 179
    d->m_mainView->rootContext()->setContextProperty(QLatin1String("timelineModelAggregator"),
                                                     d->m_modelProxy);
    d->m_mainView->rootContext()->setContextProperty(QLatin1String("zoomControl"),
                                                     d->m_zoomControl);
180
    d->m_mainView->setSource(QUrl(QLatin1String("qrc:/timeline/MainView.qml")));
181

182
    QQuickItem *rootObject = d->m_mainView->rootObject();
183
    connect(rootObject, SIGNAL(updateCursorPosition()), this, SLOT(updateCursorPosition()));
Christiaan Janssen's avatar
Christiaan Janssen committed
184 185
}

186 187 188 189 190 191
QmlProfilerTraceView::~QmlProfilerTraceView()
{
    delete d->m_mainView;
    delete d;
}

Christiaan Janssen's avatar
Christiaan Janssen committed
192 193
bool QmlProfilerTraceView::hasValidSelection() const
{
194
    QQuickItem *rootObject = d->m_mainView->rootObject();
195 196
    if (rootObject)
        return rootObject->property("selectionRangeReady").toBool();
Christiaan Janssen's avatar
Christiaan Janssen committed
197 198 199 200 201
    return false;
}

qint64 QmlProfilerTraceView::selectionStart() const
{
202
    return d->m_zoomControl->selectionStart();
Christiaan Janssen's avatar
Christiaan Janssen committed
203 204 205 206
}

qint64 QmlProfilerTraceView::selectionEnd() const
{
207
    return d->m_zoomControl->selectionEnd();
Christiaan Janssen's avatar
Christiaan Janssen committed
208 209
}

210
void QmlProfilerTraceView::clear()
Christiaan Janssen's avatar
Christiaan Janssen committed
211
{
212
    QMetaObject::invokeMethod(d->m_mainView->rootObject(), "clear");
Christiaan Janssen's avatar
Christiaan Janssen committed
213 214
}

215
void QmlProfilerTraceView::selectByTypeId(int typeId)
Christiaan Janssen's avatar
Christiaan Janssen committed
216
{
217
    QQuickItem *rootObject = d->m_mainView->rootObject();
218 219
    if (!rootObject)
        return;
220
    QMetaObject::invokeMethod(rootObject, "selectByTypeId", Q_ARG(QVariant,QVariant(typeId)));
Christiaan Janssen's avatar
Christiaan Janssen committed
221 222
}

223 224 225 226 227 228 229 230 231 232 233 234 235
void QmlProfilerTraceView::selectByEventIndex(int modelId, int eventIndex)
{
    QQuickItem *rootObject = d->m_mainView->rootObject();
    if (!rootObject)
        return;

    const int modelIndex = d->m_modelProxy->modelIndexById(modelId);
    QTC_ASSERT(modelIndex != -1, return);
    QMetaObject::invokeMethod(rootObject, "selectByIndices",
                              Q_ARG(QVariant, QVariant(modelIndex)),
                              Q_ARG(QVariant, QVariant(eventIndex)));
}

Christiaan Janssen's avatar
Christiaan Janssen committed
236 237 238
// Goto source location
void QmlProfilerTraceView::updateCursorPosition()
{
239
    QQuickItem *rootObject = d->m_mainView->rootObject();
240 241 242 243 244 245
    QString file = rootObject->property("fileName").toString();
    if (!file.isEmpty())
        emit gotoSourceLocation(file, rootObject->property("lineNumber").toInt(),
                                rootObject->property("columnNumber").toInt());

    emit typeSelected(rootObject->property("typeId").toInt());
Christiaan Janssen's avatar
Christiaan Janssen committed
246 247
}

248 249 250 251 252 253 254 255 256 257 258 259
void QmlProfilerTraceView::mousePressEvent(QMouseEvent *event)
{
    d->m_zoomControl->setWindowLocked(true);
    QWidget::mousePressEvent(event);
}

void QmlProfilerTraceView::mouseReleaseEvent(QMouseEvent *event)
{
    d->m_zoomControl->setWindowLocked(false);
    QWidget::mouseReleaseEvent(event);
}

Christiaan Janssen's avatar
Christiaan Janssen committed
260
void QmlProfilerTraceView::contextMenuEvent(QContextMenuEvent *ev)
261 262 263 264 265
{
    showContextMenu(ev->globalPos());
}

void QmlProfilerTraceView::showContextMenu(QPoint position)
Christiaan Janssen's avatar
Christiaan Janssen committed
266 267 268 269
{
    QMenu menu;
    QAction *viewAllAction = 0;

270
    menu.addActions(QmlProfilerTool::profilerContextMenuActions());
Christiaan Janssen's avatar
Christiaan Janssen committed
271
    menu.addSeparator();
272

273
    QAction *getLocalStatsAction = menu.addAction(tr("Analyze Current Range"));
274
    if (!hasValidSelection())
Christiaan Janssen's avatar
Christiaan Janssen committed
275
        getLocalStatsAction->setEnabled(false);
276

277
    QAction *getGlobalStatsAction = menu.addAction(tr("Analyze Full Range"));
278
    if (!d->m_modelManager->isRestrictedToRange())
Christiaan Janssen's avatar
Christiaan Janssen committed
279 280
        getGlobalStatsAction->setEnabled(false);

281
    if (d->m_zoomControl->traceDuration() > 0) {
Christiaan Janssen's avatar
Christiaan Janssen committed
282 283 284 285
        menu.addSeparator();
        viewAllAction = menu.addAction(tr("Reset Zoom"));
    }

286
    QAction *selectedAction = menu.exec(position);
Christiaan Janssen's avatar
Christiaan Janssen committed
287 288 289

    if (selectedAction) {
        if (selectedAction == viewAllAction) {
290 291
            d->m_zoomControl->setRange(d->m_zoomControl->traceStart(),
                                       d->m_zoomControl->traceEnd());
Christiaan Janssen's avatar
Christiaan Janssen committed
292 293
        }
        if (selectedAction == getLocalStatsAction) {
294
            d->m_modelManager->restrictToRange(selectionStart(), selectionEnd());
Christiaan Janssen's avatar
Christiaan Janssen committed
295
        }
Orgad Shaneh's avatar
Orgad Shaneh committed
296
        if (selectedAction == getGlobalStatsAction)
297
            d->m_modelManager->restrictToRange(-1, -1);
Christiaan Janssen's avatar
Christiaan Janssen committed
298 299 300
    }
}

301 302 303 304 305 306 307 308 309 310
bool QmlProfilerTraceView::isUsable() const
{
#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
    return d->m_mainView->quickWindow()->rendererInterface()->graphicsApi()
            == QSGRendererInterface::OpenGL;
#else
    return true;
#endif
}

311 312 313 314 315 316 317 318
void QmlProfilerTraceView::changeEvent(QEvent *e)
{
    if (e->type() == QEvent::EnabledChange) {
        QQuickItem *rootObject = d->m_mainView->rootObject();
        rootObject->setProperty("enabled", isEnabled());
    }
}

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
TraceViewFindSupport::TraceViewFindSupport(QmlProfilerTraceView *view,
                                           QmlProfilerModelManager *manager)
    : m_view(view), m_modelManager(manager)
{
}

bool TraceViewFindSupport::supportsReplace() const
{
    return false;
}

Core::FindFlags TraceViewFindSupport::supportedFindFlags() const
{
    return Core::FindBackward | Core::FindCaseSensitively | Core::FindRegularExpression
            | Core::FindWholeWords;
}

void TraceViewFindSupport::resetIncrementalSearch()
{
    m_incrementalStartPos = -1;
    m_incrementalWrappedState = false;
}

void TraceViewFindSupport::clearHighlights()
{
}

QString TraceViewFindSupport::currentFindString() const
{
    return QString();
}

QString TraceViewFindSupport::completedFindString() const
{
    return QString();
}

Core::IFindSupport::Result TraceViewFindSupport::findIncremental(const QString &txt,
                                                                 Core::FindFlags findFlags)
{
    if (m_incrementalStartPos < 0)
        m_incrementalStartPos = qMax(m_currentPosition, 0);
    bool wrapped = false;
    bool found = find(txt, findFlags, m_incrementalStartPos, &wrapped);
    if (wrapped != m_incrementalWrappedState && found) {
        m_incrementalWrappedState = wrapped;
        showWrapIndicator(m_view);
    }
    return found ? Core::IFindSupport::Found : Core::IFindSupport::NotFound;
}

Core::IFindSupport::Result TraceViewFindSupport::findStep(const QString &txt,
                                                          Core::FindFlags findFlags)
{
    int start = (findFlags & Core::FindBackward) ? m_currentPosition : m_currentPosition + 1;
    bool wrapped;
    bool found = find(txt, findFlags, start, &wrapped);
    if (wrapped)
        showWrapIndicator(m_view);
    if (found) {
        m_incrementalStartPos = m_currentPosition;
        m_incrementalWrappedState = false;
    }
    return found ? Core::IFindSupport::Found : Core::IFindSupport::NotFound;
}

// "start" is the model index that is searched first in a forward search, i.e. as if the
// "cursor" were between start-1 and start
bool TraceViewFindSupport::find(const QString &txt, Core::FindFlags findFlags, int start,
                                bool *wrapped)
{
    if (wrapped)
        *wrapped = false;
    if (!findOne(txt, findFlags, start)) {
        int secondStart;
        if (findFlags & Core::FindBackward)
            secondStart = m_modelManager->notesModel()->count();
        else
            secondStart = 0;
        if (!findOne(txt, findFlags, secondStart))
            return false;
        if (wrapped)
            *wrapped = true;
    }
    return true;
}

// "start" is the model index that is searched first in a forward search, i.e. as if the
// "cursor" were between start-1 and start
bool TraceViewFindSupport::findOne(const QString &txt, Core::FindFlags findFlags, int start)
{
    bool caseSensitiveSearch = (findFlags & Core::FindCaseSensitively);
    QRegExp regexp(txt);
    regexp.setPatternSyntax((findFlags & Core::FindRegularExpression) ? QRegExp::RegExp : QRegExp::FixedString);
    regexp.setCaseSensitivity(caseSensitiveSearch ? Qt::CaseSensitive : Qt::CaseInsensitive);
    QTextDocument::FindFlags flags;
    if (caseSensitiveSearch)
        flags |= QTextDocument::FindCaseSensitively;
    if (findFlags & Core::FindWholeWords)
        flags |= QTextDocument::FindWholeWords;
    bool forwardSearch = !(findFlags & Core::FindBackward);
    int increment = forwardSearch ? +1 : -1;
    int current = forwardSearch ? start : start - 1;
    QmlProfilerNotesModel *model = m_modelManager->notesModel();
    while (current >= 0 && current < model->count()) {
        QTextDocument doc(model->text(current)); // for automatic handling of WholeWords option
        if (!doc.find(regexp, 0, flags).isNull()) {
            m_currentPosition = current;
            m_view->selectByEventIndex(model->timelineModel(m_currentPosition),
                                       model->timelineIndex(m_currentPosition));
429
            QWidget *findBar = QApplication::focusWidget();
430
            m_view->updateCursorPosition(); // open file/line that belongs to event
431 432
            QTC_ASSERT(findBar, return true);
            findBar->setFocus();
433 434 435 436 437 438 439
            return true;
        }
        current += increment;
    }
    return false;
}

Christiaan Janssen's avatar
Christiaan Janssen committed
440 441
} // namespace Internal
} // namespace QmlProfiler