From patchwork Tue Oct 16 15:52:59 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yordan Karadzhov X-Patchwork-Id: 10759585 Return-Path: Received: from mail-sn1nam01on0073.outbound.protection.outlook.com ([104.47.32.73]:27872 "EHLO NAM01-SN1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726778AbeJPXoO (ORCPT ); Tue, 16 Oct 2018 19:44:14 -0400 From: Yordan Karadzhov To: "rostedt@goodmis.org" CC: "linux-trace-devel@vger.kernel.org" , Yordan Karadzhov Subject: [PATCH v2 03/23] kernel-shark-qt: Add model for showing trace data in a text format. Date: Tue, 16 Oct 2018 15:52:59 +0000 Message-ID: <20181016155232.5257-4-ykaradzhov@vmware.com> References: <20181016155232.5257-1-ykaradzhov@vmware.com> In-Reply-To: <20181016155232.5257-1-ykaradzhov@vmware.com> Content-Language: en-US MIME-Version: 1.0 Sender: linux-trace-devel-owner@vger.kernel.org List-ID: Content-Length: 15564 From: Yordan Karadzhov (VMware) This patch defines the model used by the Trace Viewer widget used to display trace data in a table-like form. Signed-off-by: Yordan Karadzhov (VMware) --- kernel-shark-qt/src/CMakeLists.txt | 2 + kernel-shark-qt/src/KsModels.cpp | 332 +++++++++++++++++++++++++++++ kernel-shark-qt/src/KsModels.hpp | 223 +++++++++++++++++++ 3 files changed, 557 insertions(+) create mode 100644 kernel-shark-qt/src/KsModels.cpp create mode 100644 kernel-shark-qt/src/KsModels.hpp diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt index dc86cdf..3fd518b 100644 --- a/kernel-shark-qt/src/CMakeLists.txt +++ b/kernel-shark-qt/src/CMakeLists.txt @@ -32,12 +32,14 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND) message(STATUS "libkshark-gui") set (ks-guiLib_hdr KsUtils.hpp + KsModels.hpp KsDualMarker.hpp KsWidgetsLib.hpp) QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr}) add_library(kshark-gui SHARED ${ks-guiLib_hdr_moc} KsUtils.cpp + KsModels.cpp KsDualMarker.cpp KsWidgetsLib.cpp) diff --git a/kernel-shark-qt/src/KsModels.cpp b/kernel-shark-qt/src/KsModels.cpp new file mode 100644 index 0000000..095e26b --- /dev/null +++ b/kernel-shark-qt/src/KsModels.cpp @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: LGPL-2.1 + +/* + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + */ + +/** + * @file KsModels.cpp + * @brief Models for data representation. + */ + +// KernelShark +#include "KsModels.hpp" +#include "KsWidgetsLib.hpp" +#include "KsUtils.hpp" + +/** Create a default (empty) KsFilterProxyModel object. */ +KsFilterProxyModel::KsFilterProxyModel(QObject *parent) +: QSortFilterProxyModel(parent), + _searchStop(false), + _source(nullptr) +{} + +/** + * Returns False if the item in the row indicated by the sourceRow and + * sourceParentshould be filtered out. Otherwise returns True. + */ +bool +KsFilterProxyModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const +{ + if (_data[sourceRow]->visible & KS_TEXT_VIEW_FILTER_MASK) + return true; + + return false; +} + +/** Provide the Proxy model with data. */ +void KsFilterProxyModel::fill(KsDataStore *data) +{ + _data = data->rows(); +} + +/** Set the source model for this Proxy model. */ +void KsFilterProxyModel::setSource(KsViewModel *s) +{ + QSortFilterProxyModel::setSourceModel(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) +{ + int row, nRows(last - first + 1); + int pbCount(1); + QVariant item; + QString text; + + _searchProgress = 0; + + if (nRows > KS_PROGRESS_BAR_MAX) + pbCount = nRows / KS_PROGRESS_BAR_MAX; + else + _searchProgress = KS_PROGRESS_BAR_MAX - nRows; + + /* Loop over the items of the proxy model. */ + for (int r = first; r <= last; ++r) { + /* + * Use the index of the proxy model to retrieve the value + * of the row number in the base model. + */ + row = mapRowFromSource(r); + item = _source->getValue(column, row); + if (cond(searchText, item.toString())) { + matchList->append(row); + } + + if (_searchStop) { + _searchStop = false; + break; + } + + /* Deal with the Progress bar of the seatch. */ + if ((r - first) % pbCount == 0) { + if (notify) { + std::lock_guard lk(_mutex); + ++_searchProgress; + _pbCond.notify_one(); + } else { + if (pb) + pb->setValue(pb->value() + 1); + if (l) + l->setText(QString(" %1").arg(matchList->count())); + QApplication::processEvents(); + } + } + } +} + +/** @brief Search the content of the table for a data satisfying an abstract + * condition. + * + * @param column: The number of the column to search in. + * @param searchText: The text to search for. + * @param cond: Matching condition function. + * @param matchList: Output location for a list containing the row indexes of + * the cells satisfying matching condition. + * @param pb: Input location for a Progressbar used to visualize the progress + * of the search. + * @param l: Input location for a label used to show the number of cells found. + * + * @returns The number of cells satisfying the matching condition. + */ +size_t KsFilterProxyModel::search(int column, + const QString &searchText, + 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 column: The number of the column to search in. + * @param searchText: The text to search for. + * @param cond: Matching condition function. + * @param first: Row index specifying the position inside the table from + * where the search starts. + * @param last: Row index specifying the position inside the table from + * where the search ends. + * @param notify: Input location for flag specifying if the search has to + * notify the main thread when to update the progress bar. + * + * @returns A list containing the row indexes of the cells satisfying matching + * condition. + */ +QList KsFilterProxyModel::searchMap(int column, + const QString &searchText, + condition_func cond, + int first, + int last, + bool notify) +{ + QList matchList; + qInfo() << "searchMap" << first << last; + _search(column, searchText, cond, &matchList, first, last, + nullptr, nullptr, notify); + + return matchList; +} + +/** Create default (empty) KsViewModel object. */ +KsViewModel::KsViewModel(QObject *parent) +: QAbstractTableModel(parent), + _data(nullptr), + _nRows(0), + _header({"#", "CPU", "Time Stamp", "Task", "PID", + "Latency", "Event", "Info"}), + _markA(-1), + _markB(-1) +{} + +/** + * Get the data stored under the given role for the item referred to by + * the index. This is an implementation of the pure virtual method of the + * abstract model class. + */ +QVariant KsViewModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::ForegroundRole) { + if (index.row() == _markA) + return QVariant::fromValue(QColor(Qt::white)); + + if (index.row() == _markB) + return QVariant::fromValue(QColor(Qt::white)); + } + + if (role == Qt::BackgroundRole) { + if (index.row() == _markA) + return QVariant::fromValue(QColor(_colorMarkA)); + + if (index.row() == _markB) + return QVariant::fromValue(QColor(_colorMarkB)); + } + + if (role == Qt::DisplayRole) + return this->getValue(index.column(), index.row()); + + return {}; +} + +/** Get the data stored in a given cell of the table. */ +QVariant KsViewModel::getValue(int column, int row) const +{ + switch (column) { + case TRACE_VIEW_COL_INDEX : + return row; + + case TRACE_VIEW_COL_CPU: + return _data[row]->cpu; + + case TRACE_VIEW_COL_TS: + return KsUtils::Ts2String(_data[row]->ts, 6); + + case TRACE_VIEW_COL_COMM: + return kshark_get_task_easy(_data[row]); + + case TRACE_VIEW_COL_PID: + return kshark_get_pid_easy(_data[row]); + + case TRACE_VIEW_COL_LAT: + return kshark_get_latency_easy(_data[row]); + + case TRACE_VIEW_COL_EVENT: + return kshark_get_event_name_easy(_data[row]); + + case TRACE_VIEW_COL_INFO : + return kshark_get_info_easy(_data[row]); + + default: + return {}; + } +} + +/** + * Get the header of a column. This is an implementation of the pure virtual + * method of the abstract model class. + */ +QVariant KsViewModel::headerData(int column, + Qt::Orientation orientation, + int role) const +{ + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return {}; + + if (column < _header.count()) + return _header.at(column); + + return {}; +} + +/** Provide the model with data. */ +void KsViewModel::fill(KsDataStore *data) +{ + beginInsertRows(QModelIndex(), 0, data->size() - 1); + + _data = data->rows(); + _nRows = data->size(); + + endInsertRows(); +} + +/** @brief Select a row in the table. + * + * @param state: Identifier of the marker used to select the row. + * @param row: Row index. + */ +void KsViewModel::selectRow(DualMarkerState state, int row) +{ + if (state == DualMarkerState::A) { + _markA = row; + _markB = -1; + } else { + _markB = row; + _markA = -1; + } +} + +/** Reset the model. */ +void KsViewModel::reset() +{ + beginResetModel(); + + _data = nullptr; + _nRows = 0; + + endResetModel(); +} + +/** Update the model. Use this function if the data has changed. */ +void KsViewModel::update(KsDataStore *data) +{ + /* + * Do not try to skip the reset(). The row count has to be set to + * zero before you full. + */ + reset(); + fill(data); +} + +/** @brief Search the content of the table for a data satisfying an abstract + * condition. + * + * @param column: The number of the column to search in. + * @param searchText: The text to search for. + * @param cond: Matching condition function. + * @param matchList: Output location for a list containing the row indexes of + * the cells satisfying the matching condition. + * + * @returns The number of cells satisfying the matching condition. + */ +size_t KsViewModel::search(int column, + const QString &searchText, + condition_func cond, + QList *matchList) +{ + int nRows = rowCount({}); + QVariant item; + + for (int r = 0; r < nRows; ++r) { + item = getValue(r, column); + if (cond(searchText, item.toString())) { + matchList->append(r); + } + } + + return matchList->count(); +} diff --git a/kernel-shark-qt/src/KsModels.hpp b/kernel-shark-qt/src/KsModels.hpp new file mode 100644 index 0000000..8935fd4 --- /dev/null +++ b/kernel-shark-qt/src/KsModels.hpp @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: LGPL-2.1 */ + +/* + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov + */ + +/** + * @file KsModels.hpp + * @brief Models for data representation. + */ + +#ifndef _KS_MODELS_H +#define _KS_MODELS_H + +// C++11 +#include +#include + +// Qt +#include +#include +#include +#include +#include + +// KernelShark +#include "libkshark.h" +#include "libkshark-model.h" + +/** Matching condition function type. To be user for searching. */ +typedef bool (*condition_func)(QString, QString); + +enum class DualMarkerState; + +class KsDataStore; + +/** + * Class KsViewModel provides models for trace data representation in a + * table view. + */ +class KsViewModel : public QAbstractTableModel +{ +public: + explicit KsViewModel(QObject *parent = nullptr); + + /** Set the colors of the two markers. */ + void setColors(const QColor &colA, const QColor &colB) { + _colorMarkA = colA; + _colorMarkB = colB; + }; + + /** + * Get the number of rows. This is an implementation of the pure + * virtual method of the abstract model class. + */ + int rowCount(const QModelIndex &) const override {return _nRows;} + + /** + * Get the number of columns. This is an implementation of the pure + * virtual method of the abstract model class. + */ + int columnCount(const QModelIndex &) const override + { + return _header.count(); + } + + QVariant headerData(int section, + Qt::Orientation orientation, + int role) const override; + + QVariant data(const QModelIndex &index, int role) const override; + + void fill(KsDataStore *data); + + void selectRow(DualMarkerState state, int row); + + void reset(); + + void update(KsDataStore *data); + + /** Get the list of column's headers. */ + QStringList header() const {return _header;} + + QVariant getValue(int column, int row) const; + + size_t search(int column, + const QString &searchText, + condition_func cond, + QList *matchList); + + /** Table columns Identifiers. */ + enum { + /** Identifier of the Index column. */ + TRACE_VIEW_COL_INDEX, + + /** Identifier of the CPU column. */ + TRACE_VIEW_COL_CPU, + + /** Identifier of the Timestamp column. */ + TRACE_VIEW_COL_TS, + + /** Identifier of the Task name (command) column. */ + TRACE_VIEW_COL_COMM, + + /** Identifier of the Process Id column. */ + TRACE_VIEW_COL_PID, + + /** Identifier of the Latency Id column. */ + TRACE_VIEW_COL_LAT, + + /** Identifier of the Event name Id column. */ + TRACE_VIEW_COL_EVENT, + + /** Identifier of the Event name Id column. */ + TRACE_VIEW_COL_INFO, + + /** Number of column. */ + TRACE_VIEW_N_COLUMNS, + }; + +private: + /** Trace data array. */ + kshark_entry **_data; + + /** The size of the data array. */ + size_t _nRows; + + /** The headers of the individual columns. */ + QStringList _header; + + /** The index of marker A inside the data array. */ + int _markA; + + /** The index of marker A inside the data array. */ + int _markB; + + /** The color of the row selected by marker A. */ + QColor _colorMarkA; + + /** The color of the row selected by marker B. */ + QColor _colorMarkB; +}; + +/** + * Class KsFilterProxyModel provides support for filtering trace data in + * table view. + */ +class KsFilterProxyModel : public QSortFilterProxyModel +{ + Q_OBJECT +public: + explicit KsFilterProxyModel(QObject *parent = nullptr); + + bool filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const override; + + void fill(KsDataStore *data); + + void setSource(KsViewModel *s); + + size_t search(int column, + const QString &searchText, + condition_func cond, + QList *matchList, + QProgressBar *pb = nullptr, + QLabel *l = nullptr); + + QList searchMap(int column, + const QString &searchText, + condition_func cond, + int first, + int last, + bool notify); + + /** Get the progress of the search. */ + int searchProgress() const {return _searchProgress;} + + /** Reset the progress value of the search. */ + void searchReset() {_searchProgress = 0;} + + /** 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. + */ + int mapRowFromSource(int r) const + { + /*This works because the row number is shown in column "0". */ + return this->data(this->index(r, 0)).toInt(); + } + + /** + * A condition variable used to notify the main thread to update the + * search progressbar. + */ + std::condition_variable _pbCond; + + /** A mutex used by the condition variable. */ + std::mutex _mutex; + +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); +}; + +#endif // _KS_MODELS_H