diff options
author | Jarek Kobus <[email protected]> | 2023-09-27 16:16:43 +0200 |
---|---|---|
committer | Jarek Kobus <[email protected]> | 2023-09-27 15:09:56 +0000 |
commit | c05f9cacc6be1038a1a9ecee0af07ac84c01738c (patch) | |
tree | e2d841a1a7ac4427f119da5b566cb2fcd38e674b | |
parent | c32022f0857dfca38359c7cc32a578a677af18d5 (diff) |
CtfVisualizer: Fix multithreading issues
Simplify the CtfVisualizerTool::loadJson(). Don't create a QThread
manually, but use TaskTree with AsyncTask instead.
Move pure parsing into the separate thread, and leave the
nlohmann::json event handling in the main thread.
Avoid moving m_modelAggregator between threads.
Fixes: QTCREATORBUG-29657
Change-Id: I0c6a9a4ea8298dbbdbafcddd338d39ad73c3f82b
Reviewed-by: Alessandro Portale <[email protected]>
-rw-r--r-- | src/plugins/ctfvisualizer/ctftracemanager.cpp | 71 | ||||
-rw-r--r-- | src/plugins/ctfvisualizer/ctftracemanager.h | 14 | ||||
-rw-r--r-- | src/plugins/ctfvisualizer/ctfvisualizertool.cpp | 130 | ||||
-rw-r--r-- | src/plugins/ctfvisualizer/ctfvisualizertool.h | 10 |
4 files changed, 104 insertions, 121 deletions
diff --git a/src/plugins/ctfvisualizer/ctftracemanager.cpp b/src/plugins/ctfvisualizer/ctftracemanager.cpp index 82e5b54b762..29dbed73c28 100644 --- a/src/plugins/ctfvisualizer/ctftracemanager.cpp +++ b/src/plugins/ctfvisualizer/ctftracemanager.cpp @@ -8,17 +8,11 @@ #include "ctfvisualizertr.h" #include <coreplugin/icore.h> + #include <tracing/timelinemodelaggregator.h> -#include <QByteArray> -#include <QDebug> -#include <QFile> -#include <QList> #include <QMessageBox> -#include <fstream> - - namespace CtfVisualizer { namespace Internal { @@ -26,48 +20,6 @@ using json = nlohmann::json; using namespace Constants; - -class CtfJsonParserCallback -{ - -public: - - explicit CtfJsonParserCallback(CtfTraceManager *traceManager) - : m_traceManager(traceManager) - {} - - bool callback(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed) - { - if ((event == json::parse_event_t::array_start && depth == 0) - || (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) { - m_isInTraceArray = true; - m_traceArrayDepth = depth; - return true; - } - if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) { - m_isInTraceArray = false; - return false; - } - if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) { - m_traceManager->addEvent(parsed); - return false; - } - if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) { - // keep outer object and values in trace objects: - return true; - } - // discard any objects outside of trace array: - // TODO: parse other data, e.g. stack frames - return false; - } - -protected: - CtfTraceManager *m_traceManager; - - bool m_isInTraceArray = false; - int m_traceArrayDepth = 0; -}; - CtfTraceManager::CtfTraceManager(QObject *parent, Timeline::TimelineModelAggregator *modelAggregator, CtfStatisticsModel *statisticsModel) @@ -75,7 +27,6 @@ CtfTraceManager::CtfTraceManager(QObject *parent, , m_modelAggregator(modelAggregator) , m_statisticsModel(statisticsModel) { - } qint64 CtfTraceManager::traceDuration() const @@ -142,26 +93,6 @@ void CtfTraceManager::addEvent(const json &event) } } -void CtfTraceManager::load(const QString &filename) -{ - clearAll(); - - std::ifstream file(filename.toStdString()); - if (!file.is_open()) { - QMessageBox::warning(Core::ICore::dialogParent(), - Tr::tr("CTF Visualizer"), - Tr::tr("Cannot read the CTF file.")); - return; - } - CtfJsonParserCallback ctfParser(this); - json::parser_callback_t callback = [&ctfParser](int depth, json::parse_event_t event, json &parsed) { - return ctfParser.callback(depth, event, parsed); - }; - json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false); - file.close(); - updateStatistics(); -} - void CtfTraceManager::finalize() { bool userConsentToIgnoreDeepTraces = false; diff --git a/src/plugins/ctfvisualizer/ctftracemanager.h b/src/plugins/ctfvisualizer/ctftracemanager.h index d376d010b5c..29694740297 100644 --- a/src/plugins/ctfvisualizer/ctftracemanager.h +++ b/src/plugins/ctfvisualizer/ctftracemanager.h @@ -5,13 +5,11 @@ #include "json/json.hpp" #include <QHash> +#include <QList> #include <QMap> #include <QObject> -#include <QVector> -namespace Timeline { -class TimelineModelAggregator; -} +namespace Timeline { class TimelineModelAggregator; } namespace CtfVisualizer { namespace Internal { @@ -34,7 +32,6 @@ public: void addEvent(const nlohmann::json &event); - void load(const QString &filename); void finalize(); bool isEmpty() const; @@ -46,6 +43,9 @@ public: void setThreadRestriction(const QString &tid, bool restrictToThisThread); bool isRestrictedTo(const QString &tid) const; + void updateStatistics(); + void clearAll(); + signals: void detailsRequested(const QString &title); @@ -53,10 +53,6 @@ protected: void addModelForThread(const QString &threadId, const QString &processId); void addModelsToAggregator(); - void updateStatistics(); - - void clearAll(); - Timeline::TimelineModelAggregator *const m_modelAggregator; CtfStatisticsModel *const m_statisticsModel; diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp index 22b6bd50473..6aee14e1236 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertool.cpp +++ b/src/plugins/ctfvisualizer/ctfvisualizertool.cpp @@ -13,30 +13,29 @@ #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/icore.h> -#include <coreplugin/progressmanager/progressmanager.h> +#include <coreplugin/progressmanager/taskprogress.h> + #include <debugger/analyzer/analyzerconstants.h> +#include <utils/async.h> #include <utils/stylehelper.h> #include <utils/utilsicons.h> -#include <QAction> -#include <QApplication> #include <QFileDialog> -#include <QFutureInterface> #include <QMenu> #include <QMessageBox> -#include <QThread> + +#include <fstream> using namespace Core; using namespace CtfVisualizer::Constants; - +using namespace Utils; namespace CtfVisualizer { namespace Internal { CtfVisualizerTool::CtfVisualizerTool() : QObject (nullptr) - , m_isLoading(false) , m_loadJson(nullptr) , m_traceView(nullptr) , m_modelAggregator(new Timeline::TimelineModelAggregator(this)) @@ -150,34 +149,84 @@ Timeline::TimelineZoomControl *CtfVisualizerTool::zoomControl() const return m_zoomControl.get(); } -void CtfVisualizerTool::loadJson(const QString &filename) +class CtfJsonParserFunctor { - if (m_isLoading) - return; +public: + CtfJsonParserFunctor(QPromise<nlohmann::json> &promise) + : m_promise(promise) {} + + bool operator()(int depth, nlohmann::json::parse_event_t event, nlohmann::json &parsed) + { + using json = nlohmann::json; + if ((event == json::parse_event_t::array_start && depth == 0) + || (event == json::parse_event_t::key && depth == 1 && parsed == json(CtfTraceEventsKey))) { + m_isInTraceArray = true; + m_traceArrayDepth = depth; + return true; + } + if (m_isInTraceArray && event == json::parse_event_t::array_end && depth == m_traceArrayDepth) { + m_isInTraceArray = false; + return false; + } + if (m_isInTraceArray && event == json::parse_event_t::object_end && depth == m_traceArrayDepth + 1) { + m_promise.addResult(parsed); + return false; + } + if (m_isInTraceArray || (event == json::parse_event_t::object_start && depth == 0)) { + // keep outer object and values in trace objects: + return true; + } + // discard any objects outside of trace array: + // TODO: parse other data, e.g. stack frames + return false; + } + +protected: + QPromise<nlohmann::json> &m_promise; + bool m_isInTraceArray = false; + int m_traceArrayDepth = 0; +}; + +static void load(QPromise<nlohmann::json> &promise, const QString &fileName) +{ + using json = nlohmann::json; - if (filename.isEmpty()) { - m_isLoading = false; + std::ifstream file(fileName.toStdString()); + if (!file.is_open()) { + promise.future().cancel(); return; } - m_isLoading = true; + CtfJsonParserFunctor functor(promise); + json::parser_callback_t callback = [&functor](int depth, json::parse_event_t event, json &parsed) { + return functor(depth, event, parsed); + }; - auto *futureInterface = new QFutureInterface<void>(); - auto *task = new QFuture<void>(futureInterface); + try { + json unusedValues = json::parse(file, callback, /*allow_exceptions*/ false); + } catch (...) { + // nlohmann::json can throw exceptions when requesting type that is wrong + } - QThread *thread = QThread::create([this, filename, futureInterface]() { - try { - m_traceManager->load(filename); - } catch (...) { - // nlohmann::json can throw exceptions when requesting type that is wrong - } - m_modelAggregator->moveToThread(QApplication::instance()->thread()); - m_modelAggregator->setParent(this); - futureInterface->reportFinished(); - }); + file.close(); +} - connect(thread, &QThread::finished, this, [this, thread, task, futureInterface]() { - // in main thread: +void CtfVisualizerTool::loadJson(const QString &fileName) +{ + using namespace Tasking; + + if (m_loader || fileName.isEmpty()) + return; + + const auto onSetup = [this, fileName](Async<nlohmann::json> &async) { + m_traceManager->clearAll(); + async.setConcurrentCallData(load, fileName); + connect(&async, &AsyncBase::resultReadyAt, this, [this, asyncPtr = &async](int index) { + m_traceManager->addEvent(asyncPtr->resultAt(index)); + }); + }; + const auto onDone = [this] { + m_traceManager->updateStatistics(); if (m_traceManager->isEmpty()) { QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("CTF Visualizer"), @@ -189,17 +238,22 @@ void CtfVisualizerTool::loadJson(const QString &filename) zoomControl()->setRange(m_traceManager->traceBegin(), m_traceManager->traceEnd() + m_traceManager->traceDuration() / 20); } setAvailableThreads(m_traceManager->getSortedThreads()); - thread->deleteLater(); - delete task; - delete futureInterface; - m_isLoading = false; - }, Qt::QueuedConnection); - - m_modelAggregator->setParent(nullptr); - m_modelAggregator->moveToThread(thread); - - thread->start(); - Core::ProgressManager::addTask(*task, Tr::tr("Loading CTF File"), CtfVisualizerTaskLoadJson); + m_loader.release()->deleteLater(); + }; + const auto onError = [this] { + QMessageBox::warning(Core::ICore::dialogParent(), + Tr::tr("CTF Visualizer"), + Tr::tr("Cannot read the CTF file.")); + m_loader.release()->deleteLater(); + }; + + const Group recipe { AsyncTask<nlohmann::json>(onSetup) }; + m_loader.reset(new TaskTree(recipe)); + connect(m_loader.get(), &TaskTree::done, this, onDone); + connect(m_loader.get(), &TaskTree::errorOccurred, this, onError); + auto progress = new TaskProgress(m_loader.get()); + progress->setDisplayName(Tr::tr("Loading CTF File")); + m_loader->start(); } } // namespace Internal diff --git a/src/plugins/ctfvisualizer/ctfvisualizertool.h b/src/plugins/ctfvisualizer/ctfvisualizertool.h index baea670d5cf..00be211efac 100644 --- a/src/plugins/ctfvisualizer/ctfvisualizertool.h +++ b/src/plugins/ctfvisualizer/ctfvisualizertool.h @@ -6,12 +6,15 @@ #include "ctfvisualizerconstants.h" #include <debugger/debuggermainwindow.h> + #include <tracing/timelinemodelaggregator.h> #include <tracing/timelinezoomcontrol.h> #include <QCoreApplication> #include <QScopedPointer> +namespace Tasking { class TaskTree; } + namespace CtfVisualizer { namespace Internal { @@ -21,7 +24,6 @@ class CtfStatisticsView; class CtfTimelineModel; class CtfVisualizerTraceView; - class CtfVisualizerTool : public QObject { Q_OBJECT @@ -34,7 +36,7 @@ public: CtfTraceManager *traceManager() const; Timeline::TimelineZoomControl *zoomControl() const; - void loadJson(const QString &filename); + void loadJson(const QString &fileName); private: void createViews(); @@ -45,11 +47,11 @@ private: void setAvailableThreads(const QList<CtfTimelineModel *> &threads); void toggleThreadRestriction(QAction *action); - Utils::Perspective m_perspective{Constants::CtfVisualizerPerspectiveId, + Utils::Perspective m_perspective{CtfVisualizer::Constants::CtfVisualizerPerspectiveId, QCoreApplication::translate("QtC::CtfVisualizer", "Chrome Trace Format Visualizer")}; - bool m_isLoading; + std::unique_ptr<Tasking::TaskTree> m_loader; QScopedPointer<QAction> m_loadJson; CtfVisualizerTraceView *m_traceView; |