From patchwork Fri Jan 4 20:06:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 10760221 Return-Path: Received: from mail-eopbgr820051.outbound.protection.outlook.com ([40.107.82.51]:56939 "EHLO NAM01-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1725986AbfADUHK (ORCPT ); Fri, 4 Jan 2019 15:07:10 -0500 From: Yordan Karadzhov To: "rostedt@goodmis.org" CC: "linux-trace-devel@vger.kernel.org" Subject: [PATCH 2/2] kernel-shark-qt: Implement State machine for searching in the data Date: Fri, 4 Jan 2019 20:06:21 +0000 Message-ID: <20190104200559.24471-3-ykaradzhov@vmware.com> References: <20190104200559.24471-1-ykaradzhov@vmware.com> In-Reply-To: <20190104200559.24471-1-ykaradzhov@vmware.com> Content-Language: en-US MIME-Version: 1.0 Sender: linux-trace-devel-owner@vger.kernel.org List-ID: Content-Length: 35178 This patch was originally motivated by the request to make it possible to continue searching from the place when the search has been stopped by the user. However because the implementation of this feature made the original code quite messy (ugly), I decided to reorganize the way the searching is controlled and add a KsSearchFSM class that implements a Finite-State Machine by following the "State" design pattern. KsSearchFSM class now encapsulates the searching logic. Signed-off-by: Yordan Karadzhov --- kernel-shark-qt/src/CMakeLists.txt | 2 + kernel-shark-qt/src/KsModels.cpp | 70 +++++--- kernel-shark-qt/src/KsModels.hpp | 39 +++-- kernel-shark-qt/src/KsSearchFSM.cpp | 232 +++++++++++++++++++++++++ kernel-shark-qt/src/KsSearchFSM.hpp | 209 ++++++++++++++++++++++ kernel-shark-qt/src/KsTraceViewer.cpp | 241 +++++++++----------------- kernel-shark-qt/src/KsTraceViewer.hpp | 32 ++-- 7 files changed, 603 insertions(+), 222 deletions(-) create mode 100644 kernel-shark-qt/src/KsSearchFSM.cpp create mode 100644 kernel-shark-qt/src/KsSearchFSM.hpp diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt index 3eddaed..1e0a794 100644 --- a/kernel-shark-qt/src/CMakeLists.txt +++ b/kernel-shark-qt/src/CMakeLists.txt @@ -34,6 +34,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND) set (ks-guiLib_hdr KsUtils.hpp KsModels.hpp KsGLWidget.hpp + KsSearchFSM.hpp KsDualMarker.hpp KsWidgetsLib.hpp KsTraceGraph.hpp @@ -49,6 +50,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND) KsModels.cpp KsSession.cpp KsGLWidget.cpp + KsSearchFSM.cpp KsDualMarker.cpp KsWidgetsLib.cpp KsTraceGraph.cpp diff --git a/kernel-shark-qt/src/KsModels.cpp b/kernel-shark-qt/src/KsModels.cpp index c8cc410..1b68143 100644 --- a/kernel-shark-qt/src/KsModels.cpp +++ b/kernel-shark-qt/src/KsModels.cpp @@ -48,33 +48,31 @@ void KsFilterProxyModel::setSource(KsViewModel *s) _source = s; } -void KsFilterProxyModel::_search(int column, - const QString &searchText, - condition_func cond, - QList *matchList, - int first, int last, - QProgressBar *pb, - QLabel *l, - bool notify) +size_t KsFilterProxyModel::_search(int column, + const QString &searchText, + search_condition_func cond, + QList *matchList, + int first, int last, + QProgressBar *pb, + QLabel *l, + bool notify) { - int row, nRows(last - first + 1); + int index, row, nRows(last - first + 1); int pbCount(1); QString item; - _searchProgress = 0; - if (nRows > KS_PROGRESS_BAR_MAX) - pbCount = nRows / KS_PROGRESS_BAR_MAX; + pbCount = nRows / (KS_PROGRESS_BAR_MAX - _searchProgress); else _searchProgress = KS_PROGRESS_BAR_MAX - nRows; /* Loop over the items of the proxy model. */ - for (int r = first; r <= last; ++r) { + for (index = first; index <= last; ++index) { /* * Use the index of the proxy model to retrieve the value * of the row number in the base model. */ - row = mapRowFromSource(r); + row = mapRowFromSource(index); item = _source->getValueStr(column, row); if (cond(searchText, item)) matchList->append(row); @@ -89,20 +87,25 @@ void KsFilterProxyModel::_search(int column, } /* Deal with the Progress bar of the seatch. */ - if ((r - first) % pbCount == 0) { + if ((index - first) % pbCount == 0) { if (notify) { std::lock_guard lk(_mutex); ++_searchProgress; _pbCond.notify_one(); } else { - if (pb) + if (pb) { pb->setValue(pb->value() + 1); + ++_searchProgress; + } + if (l) l->setText(QString(" %1").arg(matchList->count())); QApplication::processEvents(); } } } + + return index; } /** @brief Search the content of the table for a data satisfying an abstract @@ -121,15 +124,40 @@ void KsFilterProxyModel::_search(int column, */ size_t KsFilterProxyModel::search(int column, const QString &searchText, - condition_func cond, + search_condition_func cond, QList *matchList, QProgressBar *pb, QLabel *l) +{ + int nRows = rowCount({}); + _search(column, searchText, cond, matchList, + 0, nRows - 1, pb, l, false); + + return matchList->count(); +} + +/** @brief Search the content of the table for a data satisfying an abstract + * condition. + * + * @param sm: Input location for the Search State machine object. + * @param matchList: Output location for a list containing the row indexes of + * + * @returns The number of cells satisfying the matching condition. + */ +size_t KsFilterProxyModel::search(KsSearchFSM *sm, QList *matchList) { int nRows = rowCount({}); - _search(column, searchText, cond, matchList, 0, nRows - 1, - pb, l, false); + sm->_lastRowSearched = + _search(sm->column(), + sm->searchText(), + sm->condition(), + matchList, + sm->_lastRowSearched + 1, + nRows - 1, + &sm->_searchProgBar, + &sm->_searchCountLabel, + false); return matchList->count(); } @@ -152,7 +180,7 @@ size_t KsFilterProxyModel::search(int column, */ QList KsFilterProxyModel::searchMap(int column, const QString &searchText, - condition_func cond, + search_condition_func cond, int first, int last, bool notify) @@ -325,7 +353,7 @@ void KsViewModel::update(KsDataStore *data) */ size_t KsViewModel::search(int column, const QString &searchText, - condition_func cond, + search_condition_func cond, QList *matchList) { int nRows = rowCount({}); diff --git a/kernel-shark-qt/src/KsModels.hpp b/kernel-shark-qt/src/KsModels.hpp index 08019e7..808c574 100644 --- a/kernel-shark-qt/src/KsModels.hpp +++ b/kernel-shark-qt/src/KsModels.hpp @@ -26,9 +26,7 @@ // KernelShark #include "libkshark.h" #include "libkshark-model.h" - -/** Matching condition function type. To be user for searching. */ -typedef bool (*condition_func)(const QString &, const QString &); +#include "KsSearchFSM.hpp" enum class DualMarkerState; @@ -87,7 +85,7 @@ public: size_t search(int column, const QString &searchText, - condition_func cond, + search_condition_func cond, QList *matchList); /** Table columns Identifiers. */ @@ -162,14 +160,16 @@ public: size_t search(int column, const QString &searchText, - condition_func cond, + search_condition_func cond, QList *matchList, QProgressBar *pb = nullptr, QLabel *l = nullptr); + size_t search(KsSearchFSM *sm, QList *matchList); + QList searchMap(int column, const QString &searchText, - condition_func cond, + search_condition_func cond, int first, int last, bool notify); @@ -183,9 +183,6 @@ public: _searchStop = false; } - /** Stop the serch for all threads. */ - void searchStop() {_searchStop = true;} - /** * Use the "row" index in the Proxy model to retrieve the "row" index * in the source model. @@ -196,6 +193,9 @@ public: return this->data(this->index(r, 0)).toInt(); } + /** Get the source model. */ + KsViewModel *source() {return _source;} + /** * A condition variable used to notify the main thread to update the * search progressbar. @@ -205,24 +205,25 @@ public: /** A mutex used by the condition variable. */ std::mutex _mutex; + /** A flag used to stop the serch for all threads. */ + bool _searchStop; + private: int _searchProgress; - bool _searchStop; - /** Trace data array. */ kshark_entry **_data; KsViewModel *_source; - void _search(int column, - const QString &searchText, - condition_func cond, - QList *matchList, - int first, int last, - QProgressBar *pb, - QLabel *l, - bool notify); + size_t _search(int column, + const QString &searchText, + search_condition_func cond, + QList *matchList, + int first, int last, + QProgressBar *pb, + QLabel *l, + bool notify); }; /** diff --git a/kernel-shark-qt/src/KsSearchFSM.cpp b/kernel-shark-qt/src/KsSearchFSM.cpp new file mode 100644 index 0000000..4f01cc8 --- /dev/null +++ b/kernel-shark-qt/src/KsSearchFSM.cpp @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov + */ + +/** + * @file KsSearchFSM.cpp + * @brief Finite-state machine for searching in trace data. + */ + +// KernelShark +#include "KsSearchFSM.hpp" +#include "KsUtils.hpp" +#include "KsTraceViewer.hpp" +#include "KsWidgetsLib.hpp" + +static bool notHaveCond(const QString &searchText, const QString &itemText) +{ + return !itemText.contains(searchText, Qt::CaseInsensitive); +} + +static bool containsCond(const QString &searchText, const QString &itemText) +{ + return itemText.contains(searchText, Qt::CaseInsensitive); +} + +static bool matchCond(const QString &searchText, const QString &itemText) +{ + return (itemText.compare(searchText, Qt::CaseInsensitive) == 0); +} + +static bool noCond(const QString &searchText, const QString &itemText) +{ + return false; +} + +/** Create a Finite-state machine for searching. */ +KsSearchFSM::KsSearchFSM(QWidget *parent) +: _currentState(new NotDone), + _lastRowSearched(0), + _searchProgBar(parent), + _searchCountLabel("", parent), + _columnComboBox(parent), + _selectComboBox(parent), + _searchLineEdit(parent), + _prevButton("Prev", parent), + _nextButton("Next", parent), + _searchRestartButton(QIcon::fromTheme("media-playback-start"), "", parent), +// _searchStopButton(QIcon::fromTheme("media-playback-pause"), "", parent), + _searchStopButton(QIcon::fromTheme("process-stop"), "", parent), + _cond(nullptr), + _pbAction(nullptr), + _searchStopAction(nullptr), + _searchRestartAction(nullptr) +{ + int bWidth = FONT_WIDTH * 6; + + _nextButton.setFixedWidth(bWidth); + _prevButton.setFixedWidth(bWidth); + + _searchProgBar.setMaximumWidth(FONT_WIDTH * 10); + _searchProgBar.setRange(0, KS_PROGRESS_BAR_MAX); + + _selectComboBox.addItem("contains"); + _selectComboBox.addItem("full match"); + _selectComboBox.addItem("does not have"); + updateCondition(); +} + +/** + * Position all buttons and labels of the Finite-state machine for searching + * in a toolbar. + */ +void KsSearchFSM::placeInToolBar(QToolBar *tb) +{ + tb->addWidget(&_columnComboBox); + tb->addWidget(&_selectComboBox); + tb->addWidget(&_searchLineEdit); + tb->addSeparator(); + + tb->addWidget(&_nextButton); + tb->addWidget(&_prevButton); + tb->addSeparator(); + + _pbAction = tb->addWidget(&_searchProgBar); + _pbAction->setVisible(false); + + tb->addWidget(&_searchCountLabel); + + _searchStopAction = tb->addWidget(&_searchStopButton); + _searchStopAction->setVisible(false); + + _searchRestartAction = tb->addWidget(&_searchRestartButton); + _searchRestartAction->setVisible(false); + tb->addSeparator(); +} + +/** + * Update the Matching condition function of the search according to the user + * input. + */ +void KsSearchFSM::updateCondition() +{ + int xSelect = _selectComboBox.currentIndex(); + + switch (xSelect) { + case Condition::Containes: + _cond = containsCond; + return; + + case Condition::Match: + _cond = matchCond; + return; + + case Condition::NotHave: + _cond = notHaveCond; + return; + + default: + _cond = noCond; + return; + } +} + +void KsSearchFSM ::_lockSearchPanel(bool lock) +{ + _columnComboBox.setEnabled(!lock); + _selectComboBox.setEnabled(!lock); + _searchLineEdit.setReadOnly(lock); + _prevButton.setEnabled(!lock); + _nextButton.setEnabled(!lock); +// _graphFollowsCheckBox.setEnabled(!lock); +} + +/** Act according to the provided input. */ +void NotDone::handleInput(KsSearchFSM* sm, sm_input_t input) +{ + switch(input) { + case sm_input_t::Start: + sm->_lastRowSearched = -1; + sm->lockSearchPanel(); + sm->updateCondition(); + sm->progressBarVisible(true); + + if (sm->column() == KsViewModel::TRACE_VIEW_COL_INFO || + sm->column() == KsViewModel::TRACE_VIEW_COL_LAT) + sm->searchStopVisible(true); + + sm->changeState(std::shared_ptr(new InProgress)); + break; + + case sm_input_t::Finish: + sm->changeState(std::shared_ptr(new Done)); + break; + + default: + /* Ignore the input. */ + break; + } +} + +/** Act according to the provided input. */ +void Paused::handleInput(KsSearchFSM* sm, sm_input_t input) +{ + switch(input) { + case sm_input_t::Start: + sm->lockSearchPanel(); + sm->searchStopVisible(true); + sm->searchRestartVisible(false); + sm->changeState(std::shared_ptr(new InProgress)); + break; + + case sm_input_t::Change: + sm->_searchProgBar.setValue(0); + sm->_searchCountLabel.setText(""); + sm->progressBarVisible(false); + sm->searchRestartVisible(false); + sm->changeState(std::shared_ptr(new NotDone)); + break; + + default: + /* Ignore the input. */ + break; + } +} + +/** Act according to the provided input. */ +void InProgress::handleInput(KsSearchFSM* sm, sm_input_t input) +{ + auto lamUnlock = [&sm] () { + sm->searchStopVisible(false); + sm->unlockSearchPanel(); + }; + + switch(input) { + case sm_input_t::Stop: + lamUnlock(); + sm->searchRestartVisible(true); + sm->changeState(std::shared_ptr(new Paused)); + break; + + case sm_input_t::Finish: + lamUnlock(); + sm->progressBarVisible(false); + sm->changeState(std::shared_ptr(new Done)); + break; + + default: + /* Ignore the input. */ + break; + } +} + +/** Act according to the provided input. */ +void Done::handleInput(KsSearchFSM* sm, sm_input_t i) +{ + switch(i) { + case sm_input_t::Change: + sm->_searchProgBar.setValue(0); + sm->progressBarVisible(false); + sm->_searchCountLabel.setText(""); + sm->searchStopVisible(false); + sm->searchRestartVisible(false); + sm->changeState(std::shared_ptr(new NotDone)); + break; + + default: + /* Ignore the input. */ + break; + } +} diff --git a/kernel-shark-qt/src/KsSearchFSM.hpp b/kernel-shark-qt/src/KsSearchFSM.hpp new file mode 100644 index 0000000..2089912 --- /dev/null +++ b/kernel-shark-qt/src/KsSearchFSM.hpp @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ + +/* + * Copyright (C) 2018 VMware Inc, Yordan Karadzhov + */ + +/** + * @file KsSearchFSM.hpp + * @brief Finite-state machine for searching in trace data. + */ + +#ifndef _KS_SEARCH_FSM_H +#define _KS_SEARCH_FSM_H + +// C++11 +#include + +// Qt +#include + +/** Matching condition function type. To be user for searching. */ +typedef bool (*search_condition_func)(const QString &, const QString &); + +/** State Identifiers of the Finite-state machine for searching. */ +enum class search_state_t +{ + /** Identifier of the "NotDone" state. */ + NotDone_s = 0, + + /** Identifier of the "InProgress" state. */ + InProgress_s = 1, + + /** Identifier of the "Paused" state. */ + Paused_s = 2, + + /** Identifier of the "Done" state. */ + Done_s = 3 +}; + +/** Inputs of the Finite-state machine for searching. */ +enum class sm_input_t +{ + Start = 0, + Stop = 1, + Finish = 2, + Change = 3 +}; + +class KsSearchFSM; + +/** + * State provides a base class for the states of the Finite-state machine for + * searching. + */ +struct State +{ + /** Virtual destructor. */ + virtual ~State() {} + + /** + * Act according to the provided input. This is a pure virtual + * function. + */ + virtual void handleInput(KsSearchFSM* sm, sm_input_t i) = 0; + + /** + * Get the identifier of this state. This is a pure virtual function. + */ + virtual search_state_t id() = 0; +}; + +/** "NotDone" state. */ +struct NotDone : public State +{ + void handleInput(KsSearchFSM* sm, sm_input_t i) override; + + search_state_t id() override {return search_state_t::NotDone_s;} +}; + +/** "InProgress" state. */ +struct InProgress : public State +{ + void handleInput(KsSearchFSM* sm, sm_input_t i) override; + + /** Get the identifier of this state. */ + search_state_t id() override {return search_state_t::InProgress_s;} +}; + +/** "Paused" state. */ +struct Paused : public State +{ + void handleInput(KsSearchFSM* sm, sm_input_t i) override; + + /** Get the identifier of this state. */ + search_state_t id() override {return search_state_t::Paused_s;} +}; + +/** "Done" state. */ +struct Done : public State +{ + void handleInput(KsSearchFSM* sm, sm_input_t i) override; + + /** Get the identifier of this state. */ + search_state_t id() override {return search_state_t::Done_s;} +}; + +/** Finite-state machine for searching. */ +class KsSearchFSM : public QWidget +{ + Q_OBJECT +public: + explicit KsSearchFSM(QWidget *parent = nullptr); + + void placeInToolBar(QToolBar *tb); + + /** Act according to the provided input. */ + void handleInput(sm_input_t input) + { + _currentState->handleInput(this, input); + } + + /** Switch the state. */ + void changeState(std::shared_ptr newState) + { + _currentState = newState; + } + + /** Get the identifier of the Current state. */ + search_state_t getState() const {return _currentState->id();} + + /** Get the data column to search in. */ + int column() const {return _columnComboBox.currentIndex();} + + /** Get the Matching condition function. */ + search_condition_func condition() const {return _cond;} + + /** Get the text to search for. */ + QString searchText() const {return _searchLineEdit.text();} + + /** Set the value of the Search Progress Bar. */ + void setProgress(int v) {_searchProgBar.setValue(v);} + + /** Increment the value of the Search Progress Bar. */ + void incrementProgress() + { + _searchProgBar.setValue(_searchProgBar.value() + 1); + } + + void updateCondition(); + + /** Disable the user searching input (lock the panel). */ + void lockSearchPanel() {_lockSearchPanel(true);} + + /** Enable the user searching input (unlock the panel). */ + void unlockSearchPanel() {_lockSearchPanel(false);} + + /** Set the visibility of the Search Progress Bar. */ + void progressBarVisible(bool v) {_pbAction->setVisible(v);} + + /** Set the visibility of the Search Stop button. */ + void searchStopVisible(bool v) {_searchStopAction->setVisible(v);} + + /** Set the visibility of the Search Restart button. */ + void searchRestartVisible(bool v) {_searchRestartAction->setVisible(v);} + + /** Current State of the Finite-state machine for searching. */ + std::shared_ptr _currentState; + + /** + * Last row, tested for matching. To be used when restarting the + * search. + */ + ssize_t _lastRowSearched; + +//! @cond Doxygen_Suppress + + QProgressBar _searchProgBar; + + QLabel _searchCountLabel; + + QComboBox _columnComboBox; + + QComboBox _selectComboBox; + + QLineEdit _searchLineEdit; + + QPushButton _prevButton, _nextButton; + + QPushButton _searchRestartButton, _searchStopButton; + +//! @endcond + +private: + + search_condition_func _cond; + + QAction *_pbAction, *_searchStopAction, *_searchRestartAction; + + void _lockSearchPanel(bool lock); + + enum Condition + { + Containes = 0, + Match = 1, + NotHave = 2 + }; +}; + +#endif diff --git a/kernel-shark-qt/src/KsTraceViewer.cpp b/kernel-shark-qt/src/KsTraceViewer.cpp index f02fbbb..369c78e 100644 --- a/kernel-shark-qt/src/KsTraceViewer.cpp +++ b/kernel-shark-qt/src/KsTraceViewer.cpp @@ -46,7 +46,6 @@ void KsTableView::scrollTo(const QModelIndex &index, ScrollHint hint) QTableView::scrollTo(index, hint); } - /** Create a default (empty) Trace viewer widget. */ KsTraceViewer::KsTraceViewer(QWidget *parent) : QWidget(parent), @@ -57,23 +56,12 @@ KsTraceViewer::KsTraceViewer(QWidget *parent) _toolbar(this), _labelSearch("Search: Column", this), _labelGrFollows("Graph follows ", this), - _columnComboBox(this), - _selectComboBox(this), - _searchLineEdit(this), - _prevButton("Prev", this), - _nextButton("Next", this), - _searchStopButton(QIcon::fromTheme("process-stop"), "", this), - _pbAction(nullptr), + _searchFSM(this), _graphFollowsCheckBox(this), - _searchProgBar(this), - _searchCountLabel("", this), - _searchDone(false), _graphFollows(true), _mState(nullptr), _data(nullptr) { - int bWidth; - this->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum); /* Make a search toolbar. */ @@ -82,71 +70,51 @@ KsTraceViewer::KsTraceViewer(QWidget *parent) /* On the toolbar make two Combo boxes for the search settings. */ _toolbar.addWidget(&_labelSearch); - _columnComboBox.addItems(_tableHeader); + _searchFSM._columnComboBox.addItems(_tableHeader); /* * Using the old Signal-Slot syntax because * QComboBox::currentIndexChanged has overloads. */ - connect(&_columnComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(_searchEdit(int))); - - _toolbar.addWidget(&_columnComboBox); - - _selectComboBox.addItem("contains"); - _selectComboBox.addItem("full match"); - _selectComboBox.addItem("does not have"); + connect(&_searchFSM._columnComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(_searchEdit(int))); /* * Using the old Signal-Slot syntax because * QComboBox::currentIndexChanged has overloads. */ - connect(&_selectComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(_searchEdit(int))); - - _toolbar.addWidget(&_selectComboBox); + connect(&_searchFSM._selectComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(_searchEdit(int))); /* On the toolbar, make a Line edit field for search. */ - _searchLineEdit.setMaximumWidth(FONT_WIDTH * 20); - - connect(&_searchLineEdit, &QLineEdit::returnPressed, - this, &KsTraceViewer::_search); + _searchFSM._searchLineEdit.setMaximumWidth(FONT_WIDTH * 20); - connect(&_searchLineEdit, &QLineEdit::textEdited, - this, &KsTraceViewer::_searchEditText); + connect(&_searchFSM._searchLineEdit, &QLineEdit::returnPressed, + this, &KsTraceViewer::_search); - _toolbar.addWidget(&_searchLineEdit); - _toolbar.addSeparator(); + connect(&_searchFSM._searchLineEdit, &QLineEdit::textEdited, + this, &KsTraceViewer::_searchEditText); /* On the toolbar, add Prev & Next buttons. */ - bWidth = FONT_WIDTH * 6; - - _nextButton.setFixedWidth(bWidth); - _toolbar.addWidget(&_nextButton); - connect(&_nextButton, &QPushButton::pressed, - this, &KsTraceViewer::_next); - - _prevButton.setFixedWidth(bWidth); - _toolbar.addWidget(&_prevButton); - connect(&_prevButton, &QPushButton::pressed, - this, &KsTraceViewer::_prev); - - _toolbar.addSeparator(); - _searchProgBar.setMaximumWidth(FONT_WIDTH * 10); - _searchProgBar.setRange(0, 200); - _pbAction = _toolbar.addWidget(&_searchProgBar); - _pbAction->setVisible(false); - _toolbar.addWidget(&_searchCountLabel); - _searchStopAction = _toolbar.addWidget(&_searchStopButton); - _searchStopAction->setVisible(false); - connect(&_searchStopButton, &QPushButton::pressed, - this, &KsTraceViewer::_searchStop); + connect(&_searchFSM._nextButton, &QPushButton::pressed, + this, &KsTraceViewer::_next); + + connect(&_searchFSM._prevButton, &QPushButton::pressed, + this, &KsTraceViewer::_prev); + + + connect(&_searchFSM._searchStopButton, &QPushButton::pressed, + this, &KsTraceViewer::_searchStop); + + connect(&_searchFSM._searchRestartButton, &QPushButton::pressed, + this, &KsTraceViewer::_searchContinue); + + _searchFSM.placeInToolBar(&_toolbar); /* * On the toolbar, make a Check box for connecting the search pannel * to the Graph widget. */ - _toolbar.addSeparator(); _toolbar.addWidget(&_graphFollowsCheckBox); _toolbar.addWidget(&_labelGrFollows); _graphFollowsCheckBox.setCheckState(Qt::Checked); @@ -238,10 +206,8 @@ void KsTraceViewer::reset() void KsTraceViewer::_searchReset() { - _searchProgBar.setValue(0); - _searchCountLabel.setText(""); + _searchFSM.handleInput(sm_input_t::Change); _proxyModel.searchReset(); - _searchDone = false; } /** Get the index of the first (top) visible row. */ @@ -320,88 +286,26 @@ void KsTraceViewer::_graphFollowsChanged(int state) { _graphFollows = (bool) state; - if (_graphFollows && _searchDone) + if (_graphFollows && _searchDone()) emit select(*_it); // Send a signal to the Graph widget. } -static bool notHaveCond(const QString &searchText, const QString &itemText) -{ - return !itemText.contains(searchText, Qt::CaseInsensitive); -} - -static bool containsCond(const QString &searchText, const QString &itemText) -{ - return itemText.contains(searchText, Qt::CaseInsensitive); -} - -static bool matchCond(const QString &searchText, const QString &itemText) -{ - return (itemText.compare(searchText, Qt::CaseInsensitive) == 0); -} - -void KsTraceViewer::_lockSearchPanel(bool lock) -{ - _columnComboBox.setEnabled(!lock); - _selectComboBox.setEnabled(!lock); - _searchLineEdit.setReadOnly(lock); - _prevButton.setEnabled(!lock); - _nextButton.setEnabled(!lock); - _graphFollowsCheckBox.setEnabled(!lock); -} - void KsTraceViewer::_search() { - if (!_searchDone) { + if (!_searchDone()) { /* * The search is not done. This means that the search settings * have been modified since the last time we searched. */ - int xColumn, xSelect; - QString xText; - - /* Disable the user input until the search is done. */ - _lockSearchPanel(true); - _matchList.clear(); - xText = _searchLineEdit.text(); - if (xText.isEmpty()) { - /* - * No text is provided by the user. Most probably this - * is an accidental key press. Just reenable the input. - */ - _lockSearchPanel(false); - return; - } - - xColumn = _columnComboBox.currentIndex(); - xSelect = _selectComboBox.currentIndex(); - - switch (xSelect) { - case Condition::Containes: - _searchItems(xColumn, xText, &containsCond); - break; - - case Condition::Match: - _searchItems(xColumn, xText, &matchCond); - break; - - case Condition::NotHave: - _searchItems(xColumn, xText, ¬HaveCond); - break; - - default: - break; - } + _searchItems(); if (!_matchList.empty()) { - this->showRow(*_it, true); + showRow(*_it, true); if (_graphFollows) emit select(*_it); // Send a signal to the Graph widget. } - - /* Enable the user input. */ - _lockSearchPanel(false); } else { /* * If the search is done, pressing "Enter" is equivalent @@ -413,13 +317,13 @@ void KsTraceViewer::_search() void KsTraceViewer::_next() { - if (!_searchDone) { + if (!_searchDone()) { _search(); return; } if (!_matchList.empty()) { // Items have been found. - int row = _getSelectedDataRow(); + int row = selectedRow(); /* * The iterator can only be at the selected row or if the * selected row is not a match at the first matching row after @@ -427,7 +331,7 @@ void KsTraceViewer::_next() */ if (*_it == row) { ++_it; // Move the iterator. - if (_it == _matchList.end() ) { + if (_it == _matchList.end()) { /* * This is the last item of the list. * Go back to the beginning. @@ -448,7 +352,7 @@ void KsTraceViewer::_next() void KsTraceViewer::_prev() { - if (!_searchDone) { + if (!_searchDone()) { _search(); return; } @@ -474,6 +378,7 @@ void KsTraceViewer::_prev() void KsTraceViewer::_updateSearchCount() { int index, total; + QString countText; if (_matchList.isEmpty()) return; @@ -481,14 +386,20 @@ void KsTraceViewer::_updateSearchCount() index = _it - _matchList.begin(); total =_matchList.count(); - _searchCountLabel.setText(QString(" %1 / %2").arg(index).arg(total)); + countText = QString(" %1 / %2").arg(index + 1).arg(total); + _searchFSM._searchCountLabel.setText(countText); } void KsTraceViewer::_searchStop() { - _searchStopAction->setVisible(false); - _proxyModel.searchStop(); - _lockSearchPanel(false); + _proxyModel._searchStop = true; + _searchFSM.handleInput(sm_input_t::Stop); +} + +void KsTraceViewer::_searchContinue() +{ + _proxyModel._searchStop = false; + _searchItems(); } void KsTraceViewer::_clicked(const QModelIndex& i) @@ -499,7 +410,7 @@ void KsTraceViewer::_clicked(const QModelIndex& i) */ size_t row = _proxyModel.mapRowFromSource(i.row()); - if (_searchDone && _matchList.count()) { + if (_searchDone() && _matchList.count()) { _setSearchIterator(row); _updateSearchCount(); } @@ -594,7 +505,7 @@ void KsTraceViewer::markSwitch() _view.clearSelection(); } - row = _getSelectedDataRow(); + row = selectedRow(); if (row >= 0) { _setSearchIterator(row); _updateSearchCount(); @@ -634,7 +545,7 @@ void KsTraceViewer::resizeEvent(QResizeEvent* event) void KsTraceViewer::keyReleaseEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down) { - int row = _getSelectedDataRow(); + int row = selectedRow(); if (row >= 0) emit select(row); // Send a signal to the Graph widget. @@ -668,52 +579,52 @@ void KsTraceViewer::_resizeToContents() //! @endcond -size_t KsTraceViewer::_searchItems(int column, - const QString &searchText, - condition_func cond) +size_t KsTraceViewer::_searchItems() { + int column = _searchFSM._columnComboBox.currentIndex(); + QString searchText = _searchFSM._searchLineEdit.text(); int count, dataRow; - _pbAction->setVisible(true); + if (searchText.isEmpty()) { + /* + * No text is provided by the user. Most probably this + * is an accidental key press. */ + return 0; + } if (_proxyModel.rowCount({}) < KS_SEARCH_SHOW_PROGRESS_MIN) { /* * This is a small data-set. Do a single-threaded search * without showing the progress. */ - _proxyModel.search(column, searchText, cond, &_matchList, + _proxyModel.search(column, searchText, _searchFSM.condition(), &_matchList, nullptr, nullptr); } else { - _searchStopAction->setVisible(true); + _searchFSM.handleInput(sm_input_t::Start); if (column == KsViewModel::TRACE_VIEW_COL_INFO || - column == KsViewModel::TRACE_VIEW_COL_LAT) { - _proxyModel.search(column, searchText, - cond, &_matchList, - &_searchProgBar, - &_searchCountLabel); - } else { - _searchItemsMapReduce(column, searchText, cond); - } - - _searchStopAction->setVisible(false); + column == KsViewModel::TRACE_VIEW_COL_LAT) + _proxyModel.search(&_searchFSM, &_matchList); + else + _searchItemsMapReduce(column, searchText, _searchFSM.condition()); } count = _matchList.count(); - - _pbAction->setVisible(false); - _searchDone = true; + _searchFSM.handleInput(sm_input_t::Finish); if (count == 0) // No items have been found. Do nothing. return 0; - dataRow = _getSelectedDataRow(); + dataRow = selectedRow(); if (dataRow >= 0) { _view.clearSelection(); _setSearchIterator(dataRow); + showRow(*_it, true); + + if (_graphFollows) + emit select(*_it); // Send a signal to the Graph widget. } else { /* Move the iterator to the beginning of the match list. */ - _view.clearSelection(); _it = _matchList.begin(); } @@ -747,7 +658,7 @@ void KsTraceViewer::_setSearchIterator(int row) void KsTraceViewer::_searchItemsMapReduce(int column, const QString &searchText, - condition_func cond) + search_condition_func cond) { int nThreads = std::thread::hardware_concurrency(); std::vector> ranges(nThreads); @@ -763,9 +674,9 @@ void KsTraceViewer::_searchItemsMapReduce(int column, }; auto lamSearchReduce = [&] (QList &resultList, - const QList &mapList) { + const QList &mapList) { resultList << mapList; - _searchProgBar.setValue(_searchProgBar.value() + 1); + _searchFSM.incrementProgress(); }; for (auto &r: ranges) { @@ -785,7 +696,7 @@ void KsTraceViewer::_searchItemsMapReduce(int column, while (_proxyModel.searchProgress() < KS_PROGRESS_BAR_MAX - nThreads) { std::unique_lock lk(_proxyModel._mutex); _proxyModel._pbCond.wait(lk); - _searchProgBar.setValue(_proxyModel.searchProgress()); + _searchFSM.setProgress(_proxyModel.searchProgress()); QApplication::processEvents(); } @@ -793,7 +704,11 @@ void KsTraceViewer::_searchItemsMapReduce(int column, lamSearchReduce(_matchList, m.get()); } -int KsTraceViewer::_getSelectedDataRow() +/** + * Get the currently selected row. If no row is selected the function + * returns -1. + */ +int KsTraceViewer::selectedRow() { QItemSelectionModel *sm = _view.selectionModel(); int dataRow = -1; diff --git a/kernel-shark-qt/src/KsTraceViewer.hpp b/kernel-shark-qt/src/KsTraceViewer.hpp index a8c1fe6..f59f5df 100644 --- a/kernel-shark-qt/src/KsTraceViewer.hpp +++ b/kernel-shark-qt/src/KsTraceViewer.hpp @@ -18,6 +18,7 @@ // KernelShark #include "KsUtils.hpp" #include "KsModels.hpp" +#include "KsSearchFSM.hpp" #include "KsDualMarker.hpp" /** @@ -67,6 +68,8 @@ public: void clearSelection(); + int selectedRow(); + void update(KsDataStore *data); signals: @@ -100,24 +103,10 @@ private: QLabel _labelSearch, _labelGrFollows; - QComboBox _columnComboBox; - - QComboBox _selectComboBox; - - QLineEdit _searchLineEdit; - - QPushButton _prevButton, _nextButton, _searchStopButton; - - QAction *_pbAction, *_searchStopAction; + KsSearchFSM _searchFSM; QCheckBox _graphFollowsCheckBox; - QProgressBar _searchProgBar; - - QLabel _searchCountLabel; - - bool _searchDone; - bool _graphFollows; QList _matchList; @@ -139,11 +128,10 @@ private: void _resizeToContents(); - size_t _searchItems(int column, const QString &searchText, - condition_func cond); + size_t _searchItems(); void _searchItemsMapReduce(int column, const QString &searchText, - condition_func cond); + search_condition_func cond); void _searchEditText(const QString &); @@ -161,13 +149,19 @@ private: void _searchStop(); + void _searchContinue(); + void _clicked(const QModelIndex& i); void _onCustomContextMenu(const QPoint &); void _setSearchIterator(int row); - int _getSelectedDataRow(); + bool _searchDone() + { + return _searchFSM.getState() == search_state_t::Done_s || + _searchFSM.getState() == search_state_t::Paused_s; + } private slots: void _searchEdit(int);