From 827d48db6eb9668d037a28e92bb2b0483fb9e513 Mon Sep 17 00:00:00 2001
From: Ulf Hermann <ulf.hermann@qt.io>
Date: Wed, 28 Dec 2016 16:45:43 +0100
Subject: [PATCH] QmlProfiler: Move the file finder into the QML model

This is the place where we need it most. In addition, this allows us to
use the DetailsRewriter's cache also on gotoSourceLocation.

Change-Id: I14e0f11ba7c8a2a3888b2e8439a375068b36e29a
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
---
 .../qmlprofiler/qmlprofilerdatamodel.cpp      | 18 +++-
 .../qmlprofiler/qmlprofilerdatamodel.h        |  7 +-
 .../qmlprofilerdetailsrewriter.cpp            | 97 +++++++++++++++----
 .../qmlprofiler/qmlprofilerdetailsrewriter.h  |  8 +-
 .../qmlprofiler/qmlprofilermodelmanager.cpp   |  4 +-
 .../qmlprofiler/qmlprofilermodelmanager.h     |  2 +-
 src/plugins/qmlprofiler/qmlprofilertool.cpp   | 52 +---------
 src/plugins/qmlprofiler/qmlprofilertool.h     |  1 -
 .../tests/flamegraphmodel_test.cpp            |  2 +-
 .../qmlprofiler/tests/flamegraphmodel_test.h  |  1 -
 .../qmlprofiler/tests/flamegraphview_test.cpp |  3 +-
 .../qmlprofiler/tests/flamegraphview_test.h   |  1 -
 12 files changed, 115 insertions(+), 81 deletions(-)

diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
index e08f4453b0e..cbd0d85dbf5 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.cpp
@@ -94,14 +94,13 @@ QString getInitialDetails(const QmlEventType &event)
     return details;
 }
 
-QmlProfilerDataModel::QmlProfilerDataModel(Utils::FileInProjectFinder *fileFinder,
-                                           QmlProfilerModelManager *parent) :
+QmlProfilerDataModel::QmlProfilerDataModel(QmlProfilerModelManager *parent) :
     QObject(parent), d_ptr(new QmlProfilerDataModelPrivate)
 {
     Q_D(QmlProfilerDataModel);
     Q_ASSERT(parent);
+    d->detailsRewriter = new QmlProfilerDetailsRewriter(this);
     d->modelManager = parent;
-    d->detailsRewriter = new QmlProfilerDetailsRewriter(fileFinder, this);
     d->modelId = d->modelManager->registerModelProxy();
     connect(d->detailsRewriter, &QmlProfilerDetailsRewriter::rewriteDetailsString,
             this, &QmlProfilerDataModel::detailsChanged);
@@ -146,6 +145,19 @@ void QmlProfilerDataModel::addEventType(const QmlEventType &type)
     d->rewriteType(typeIndex);
 }
 
+void QmlProfilerDataModel::populateFileFinder(
+        const ProjectExplorer::RunConfiguration *runConfiguration)
+{
+    Q_D(QmlProfilerDataModel);
+    d->detailsRewriter->populateFileFinder(runConfiguration);
+}
+
+QString QmlProfilerDataModel::findLocalFile(const QString &remoteFile)
+{
+    Q_D(QmlProfilerDataModel);
+    return d->detailsRewriter->getLocalFile(remoteFile);
+}
+
 void QmlProfilerDataModel::addEvent(const QmlEvent &event)
 {
     Q_D(QmlProfilerDataModel);
diff --git a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
index a9123f1ee8e..9f86d74b773 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdatamodel.h
@@ -32,6 +32,7 @@
 #include "qmlevent.h"
 
 #include <utils/fileinprojectfinder.h>
+#include <projectexplorer/runconfiguration.h>
 
 namespace QmlProfiler {
 
@@ -39,8 +40,7 @@ class QMLPROFILER_EXPORT QmlProfilerDataModel : public QObject
 {
     Q_OBJECT
 public:
-    explicit QmlProfilerDataModel(Utils::FileInProjectFinder *fileFinder,
-                                  QmlProfilerModelManager *parent);
+    explicit QmlProfilerDataModel(QmlProfilerModelManager *parent);
     ~QmlProfilerDataModel();
 
     const QmlEventType &eventType(int typeId) const;
@@ -48,6 +48,9 @@ public:
     void addEventTypes(const QVector<QmlEventType> &types);
     void addEventType(const QmlEventType &type);
 
+    void populateFileFinder(const ProjectExplorer::RunConfiguration *runConfiguration = nullptr);
+    QString findLocalFile(const QString &remoteFile);
+
     void clear();
     bool isEmpty() const;
     void addEvent(const QmlEvent &event);
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
index 49c66b2b850..a5199491f61 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.cpp
@@ -25,6 +25,12 @@
 
 #include "qmlprofilerdetailsrewriter.h"
 
+#include <projectexplorer/kit.h>
+#include <projectexplorer/kitinformation.h>
+#include <projectexplorer/project.h>
+#include <projectexplorer/session.h>
+#include <projectexplorer/target.h>
+#include <projectexplorer/runconfiguration.h>
 #include <qmljs/parser/qmljsast_p.h>
 #include <qmljs/qmljsmodelmanagerinterface.h>
 #include <qmljstools/qmljsmodelmanager.h>
@@ -85,37 +91,42 @@ protected:
     }
 };
 
-QmlProfilerDetailsRewriter::QmlProfilerDetailsRewriter(Utils::FileInProjectFinder *fileFinder,
-                                                       QObject *parent)
-    : QObject(parent), m_projectFinder(fileFinder)
+QmlProfilerDetailsRewriter::QmlProfilerDetailsRewriter(QObject *parent)
+    : QObject(parent)
 {
 }
 
 void QmlProfilerDetailsRewriter::requestDetailsForLocation(int typeId,
                                                            const QmlEventLocation &location)
 {
-    QString localFile;
-    const QString locationFile = location.filename();
-    if (!m_filesCache.contains(locationFile)) {
-        localFile = m_projectFinder->findFile(locationFile);
-        m_filesCache[locationFile] = localFile;
-    } else {
-        localFile = m_filesCache[locationFile];
-    }
-    QFileInfo fileInfo(localFile);
-    if (!fileInfo.exists() || !fileInfo.isReadable())
-        return;
-    if (!QmlJS::ModelManagerInterface::guessLanguageOfFile(localFile).isQmlLikeLanguage())
+    const QString localFile = getLocalFile(location.filename());
+    if (localFile.isEmpty())
         return;
 
-    localFile = fileInfo.canonicalFilePath();
-
     if (m_pendingEvents.isEmpty())
         connectQmlModel();
 
     m_pendingEvents.insert(localFile, {location, typeId});
 }
 
+QString QmlProfilerDetailsRewriter::getLocalFile(const QString &remoteFile)
+{
+    QString localFile;
+    if (!m_filesCache.contains(remoteFile)) {
+        localFile = m_projectFinder.findFile(remoteFile);
+        m_filesCache[remoteFile] = localFile;
+    } else {
+        localFile = m_filesCache[remoteFile];
+    }
+    QFileInfo fileInfo(localFile);
+    if (!fileInfo.exists() || !fileInfo.isReadable())
+        return QString();
+    if (!QmlJS::ModelManagerInterface::guessLanguageOfFile(localFile).isQmlLikeOrJsLanguage())
+        return QString();
+
+    return fileInfo.canonicalFilePath();
+}
+
 void QmlProfilerDetailsRewriter::reloadDocuments()
 {
     if (!m_pendingEvents.isEmpty()) {
@@ -196,5 +207,57 @@ void QmlProfilerDetailsRewriter::documentReady(QmlJS::Document::Ptr doc)
     }
 }
 
+void QmlProfilerDetailsRewriter::populateFileFinder(
+        const ProjectExplorer::RunConfiguration *runConfiguration)
+{
+    // Prefer the given runConfiguration's target if available
+    const ProjectExplorer::Target *target = runConfiguration ? runConfiguration->target() : nullptr;
+
+    // If runConfiguration given, then use the project associated with that ...
+    const ProjectExplorer::Project *startupProject = target ? target->project() : nullptr;
+
+    // ... else try the session manager's global startup project ...
+    if (!startupProject)
+        startupProject = ProjectExplorer::SessionManager::startupProject();
+
+    // ... and if that is null, use the first project available.
+    const QList<ProjectExplorer::Project *> projects = ProjectExplorer::SessionManager::projects();
+    if (!startupProject && !projects.isEmpty())
+        startupProject = projects.first();
+
+    QString projectDirectory;
+    QStringList sourceFiles;
+
+    // Sort files from startupProject to the front of the list ...
+    if (startupProject) {
+        projectDirectory = startupProject->projectDirectory().toString();
+        sourceFiles.append(startupProject->files(ProjectExplorer::Project::SourceFiles));
+    }
+
+    // ... then add all the other projects' files.
+    for (const ProjectExplorer::Project *project : projects) {
+        if (project != startupProject)
+            sourceFiles.append(project->files(ProjectExplorer::Project::SourceFiles));
+    }
+
+    // If no runConfiguration was given, but we've found a startupProject, then try to deduct a
+    // target from that.
+    if (!target && startupProject)
+        target = startupProject->activeTarget();
+
+    // ... and find the sysroot if we have any target at all.
+    QString activeSysroot;
+    if (target) {
+        const ProjectExplorer::Kit *kit = target->kit();
+        if (kit && ProjectExplorer::SysRootKitInformation::hasSysRoot(kit))
+            activeSysroot = ProjectExplorer::SysRootKitInformation::sysRoot(kit).toString();
+    }
+
+    // Finally, do populate m_projectFinder
+    m_projectFinder.setProjectDirectory(projectDirectory);
+    m_projectFinder.setProjectFiles(sourceFiles);
+    m_projectFinder.setSysroot(activeSysroot);
+}
+
 } // namespace Internal
 } // namespace QmlProfiler
diff --git a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
index f4717c90fb5..d5dcf6e773c 100644
--- a/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
+++ b/src/plugins/qmlprofiler/qmlprofilerdetailsrewriter.h
@@ -27,6 +27,7 @@
 
 #include "qmleventlocation.h"
 
+#include <projectexplorer/runconfiguration.h>
 #include <qmljs/qmljsdocument.h>
 #include <utils/fileinprojectfinder.h>
 
@@ -39,13 +40,14 @@ class QmlProfilerDetailsRewriter : public QObject
 {
     Q_OBJECT
 public:
-    explicit QmlProfilerDetailsRewriter(Utils::FileInProjectFinder *fileFinder,
-                                        QObject *parent = nullptr);
+    explicit QmlProfilerDetailsRewriter(QObject *parent = nullptr);
 
     void clearRequests();
     void requestDetailsForLocation(int typeId, const QmlEventLocation &location);
+    QString getLocalFile(const QString &remoteFile);
     void reloadDocuments();
     void documentReady(QmlJS::Document::Ptr doc);
+    void populateFileFinder(const ProjectExplorer::RunConfiguration *runConfiguration);
 
     struct PendingEvent {
         QmlEventLocation location;
@@ -58,7 +60,7 @@ signals:
 
 private:
     QMultiHash<QString, PendingEvent> m_pendingEvents;
-    Utils::FileInProjectFinder *m_projectFinder;
+    Utils::FileInProjectFinder m_projectFinder;
     QHash<QString, QString> m_filesCache;
 
     void rewriteDetailsForLocation(const QString &source, QmlJS::Document::Ptr doc, int typeId,
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
index ccb05f67497..bee5f3deb2f 100644
--- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.cpp
@@ -158,7 +158,7 @@ public:
 };
 
 
-QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent) :
+QmlProfilerModelManager::QmlProfilerModelManager(QObject *parent) :
     QObject(parent), d(new QmlProfilerModelManagerPrivate)
 {
     d->numRegisteredModels = 0;
@@ -168,7 +168,7 @@ QmlProfilerModelManager::QmlProfilerModelManager(Utils::FileInProjectFinder *fin
     d->visibleFeatures = 0;
     d->recordedFeatures = 0;
     d->aggregateTraces = false;
-    d->model = new QmlProfilerDataModel(finder, this);
+    d->model = new QmlProfilerDataModel(this);
     d->state = Empty;
     d->traceTime = new QmlProfilerTraceTime(this);
     d->notesModel = new QmlProfilerNotesModel(this);
diff --git a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
index bd05ab97f99..7ed64ef187d 100644
--- a/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
+++ b/src/plugins/qmlprofiler/qmlprofilermodelmanager.h
@@ -90,7 +90,7 @@ public:
     typedef std::function<void(const QmlEvent &, const QmlEventType &)> EventLoader;
     typedef std::function<void()> Finalizer;
 
-    explicit QmlProfilerModelManager(Utils::FileInProjectFinder *finder, QObject *parent = 0);
+    explicit QmlProfilerModelManager(QObject *parent = 0);
     ~QmlProfilerModelManager();
 
     State state() const;
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.cpp b/src/plugins/qmlprofiler/qmlprofilertool.cpp
index c5ad6a7cfca..ec9d8ed5932 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.cpp
+++ b/src/plugins/qmlprofiler/qmlprofilertool.cpp
@@ -100,7 +100,6 @@ public:
     QmlProfilerModelManager *m_profilerModelManager = 0;
 
     QmlProfilerViewManager *m_viewContainer = 0;
-    Utils::FileInProjectFinder m_projectFinder;
     QToolButton *m_recordButton = 0;
     QMenu *m_recordFeaturesMenu = 0;
 
@@ -147,7 +146,7 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
     connect(d->m_profilerConnections, &QmlProfilerClientManager::connectionClosed,
             this, &QmlProfilerTool::clientsDisconnected);
 
-    d->m_profilerModelManager = new QmlProfilerModelManager(&d->m_projectFinder, this);
+    d->m_profilerModelManager = new QmlProfilerModelManager(this);
     connect(d->m_profilerModelManager, &QmlProfilerModelManager::stateChanged,
             this, &QmlProfilerTool::profilerDataModelStateChanged);
     connect(d->m_profilerModelManager, &QmlProfilerModelManager::error,
@@ -242,7 +241,7 @@ QmlProfilerTool::QmlProfilerTool(QObject *parent)
 
     // When the widgets are requested we assume that the session data
     // is available, then we can populate the file finder
-    populateFileFinder();
+    d->m_profilerModelManager->qmlModel()->populateFileFinder();
 
     auto runControlCreator = [this](RunConfiguration *runConfiguration, Core::Id) {
         return createRunControl(runConfiguration);
@@ -309,15 +308,6 @@ void QmlProfilerTool::updateRunActions()
     }
 }
 
-static QString sysroot(RunConfiguration *runConfig)
-{
-    QTC_ASSERT(runConfig, return QString());
-    Kit *k = runConfig->target()->kit();
-    if (k && SysRootKitInformation::hasSysRoot(k))
-        return SysRootKitInformation::sysRoot(runConfig->target()->kit()).toString();
-    return QString();
-}
-
 AnalyzerRunControl *QmlProfilerTool::createRunControl(RunConfiguration *runConfiguration)
 {
     d->m_toolBusy = true;
@@ -368,10 +358,7 @@ void QmlProfilerTool::finalizeRunControl(QmlProfilerRunControl *runControl)
 
     RunConfiguration *runConfiguration = runControl->runConfiguration();
     if (runConfiguration) {
-        QString projectDirectory;
-        Project *project = runConfiguration->target()->project();
-        projectDirectory = project->projectDirectory().toString();
-        populateFileFinder(projectDirectory, sysroot(runConfiguration));
+        d->m_profilerModelManager->qmlModel()->populateFileFinder(runConfiguration);
     }
 
     if (connection.analyzerSocket.isEmpty()) {
@@ -413,35 +400,6 @@ void QmlProfilerTool::finalizeRunControl(QmlProfilerRunControl *runControl)
     });
 }
 
-void QmlProfilerTool::populateFileFinder(QString projectDirectory, QString activeSysroot)
-{
-    // Initialize filefinder with some sensible default
-    QStringList sourceFiles;
-    QList<Project *> projects = SessionManager::projects();
-    if (Project *startupProject = SessionManager::startupProject()) {
-        // startup project first
-        projects.removeOne(startupProject);
-        projects.insert(0, startupProject);
-    }
-    foreach (Project *project, projects)
-        sourceFiles << project->files(Project::SourceFiles);
-
-    if (!projects.isEmpty()) {
-        if (projectDirectory.isEmpty())
-            projectDirectory = projects.first()->projectDirectory().toString();
-
-        if (activeSysroot.isEmpty()) {
-            if (Target *target = projects.first()->activeTarget())
-                if (RunConfiguration *rc = target->activeRunConfiguration())
-                    activeSysroot = sysroot(rc);
-        }
-    }
-
-    d->m_projectFinder.setProjectDirectory(projectDirectory);
-    d->m_projectFinder.setProjectFiles(sourceFiles);
-    d->m_projectFinder.setSysroot(activeSysroot);
-}
-
 void QmlProfilerTool::recordingButtonChanged(bool recording)
 {
     // clientRecording is our intention for new sessions. That may differ from the state of the
@@ -492,7 +450,7 @@ void QmlProfilerTool::gotoSourceLocation(const QString &fileUrl, int lineNumber,
     if (lineNumber < 0 || fileUrl.isEmpty())
         return;
 
-    const QString projectFileName = d->m_projectFinder.findFile(fileUrl);
+    const QString projectFileName = d->m_profilerModelManager->qmlModel()->findLocalFile(fileUrl);
 
     QFileInfo fileInfo(projectFileName);
     if (!fileInfo.exists() || !fileInfo.isReadable())
@@ -693,7 +651,7 @@ void QmlProfilerTool::showLoadDialog()
         Debugger::enableMainWindow(false);
         connect(d->m_profilerModelManager, &QmlProfilerModelManager::recordedFeaturesChanged,
                 this, &QmlProfilerTool::setRecordedFeatures);
-        populateFileFinder();
+        d->m_profilerModelManager->qmlModel()->populateFileFinder();
         d->m_profilerModelManager->load(filename);
     }
 }
diff --git a/src/plugins/qmlprofiler/qmlprofilertool.h b/src/plugins/qmlprofiler/qmlprofilertool.h
index 3651c759e86..ec263ade2f2 100644
--- a/src/plugins/qmlprofiler/qmlprofilertool.h
+++ b/src/plugins/qmlprofiler/qmlprofilertool.h
@@ -95,7 +95,6 @@ private slots:
 private:
     void updateRunActions();
     void clearDisplay();
-    void populateFileFinder(QString projectDirectory = QString(), QString activeSysroot = QString());
     template<ProfileFeature feature>
     void updateFeatures(quint64 features);
     bool checkForUnsavedNotes();
diff --git a/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp b/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp
index 129388cfbed..6afe091b930 100644
--- a/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp
+++ b/src/plugins/qmlprofiler/tests/flamegraphmodel_test.cpp
@@ -33,7 +33,7 @@ namespace QmlProfiler {
 namespace Internal {
 
 FlameGraphModelTest::FlameGraphModelTest(QObject *parent) :
-    QObject(parent), manager(&finder), model(&manager)
+    QObject(parent), model(&manager)
 {
 }
 
diff --git a/src/plugins/qmlprofiler/tests/flamegraphmodel_test.h b/src/plugins/qmlprofiler/tests/flamegraphmodel_test.h
index 6d9793e16fd..1099ee62007 100644
--- a/src/plugins/qmlprofiler/tests/flamegraphmodel_test.h
+++ b/src/plugins/qmlprofiler/tests/flamegraphmodel_test.h
@@ -49,7 +49,6 @@ private slots:
     void cleanupTestCase();
 
 private:
-    Utils::FileInProjectFinder finder;
     QmlProfilerModelManager manager;
     FlameGraphModel model;
     int rangeModelId = -1;
diff --git a/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp b/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp
index 6dbca7b98e0..799f3c3ac15 100644
--- a/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp
+++ b/src/plugins/qmlprofiler/tests/flamegraphview_test.cpp
@@ -34,8 +34,7 @@
 namespace QmlProfiler {
 namespace Internal {
 
-FlameGraphViewTest::FlameGraphViewTest(QObject *parent) : QObject(parent), manager(&finder),
-    view(&manager)
+FlameGraphViewTest::FlameGraphViewTest(QObject *parent) : QObject(parent), view(&manager)
 {
 }
 
diff --git a/src/plugins/qmlprofiler/tests/flamegraphview_test.h b/src/plugins/qmlprofiler/tests/flamegraphview_test.h
index e16050bdbf2..c99a2177d2b 100644
--- a/src/plugins/qmlprofiler/tests/flamegraphview_test.h
+++ b/src/plugins/qmlprofiler/tests/flamegraphview_test.h
@@ -45,7 +45,6 @@ private slots:
     void cleanupTestCase();
 
 private:
-    Utils::FileInProjectFinder finder;
     QmlProfilerModelManager manager;
     FlameGraphView view;
 };
-- 
GitLab