blob: e10f4112a6b17995006ae40124d5280c535499c9 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
*/
/**
* @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;
}
size_t KsFilterProxyModel::_search(int column,
const QString &searchText,
search_condition_func cond,
QList<int> *matchList,
int step,
int first, int last,
QProgressBar *pb,
QLabel *l,
int *lastRowSearched,
bool notify)
{
int index, row, nRows(last - first + 1);
int milestone(1), pbCount(1);
QString item;
if (nRows > KS_PROGRESS_BAR_MAX)
milestone = pbCount = nRows / (KS_PROGRESS_BAR_MAX - step -
_searchProgress);
else
_searchProgress = KS_PROGRESS_BAR_MAX - nRows;
/* Loop over the items of the proxy model. */
for (index = first; index <= last; index += step) {
/*
* Use the index of the proxy model to retrieve the value
* of the row number in the base model.
*/
row = mapRowFromSource(index);
item = _source->getValueStr(column, row);
if (cond(searchText, item))
matchList->append(row);
if (_searchStop) {
if (lastRowSearched)
*lastRowSearched = index;
if (notify)
_pbCond.notify_one();
break;
}
/* Deal with the Progress bar of the seatch. */
if ((index - first) >= milestone) {
milestone += pbCount;
if (notify) {
/*
* This is a multi-threaded search. Notify
* the main thread to update the progress bar.
*/
std::lock_guard<std::mutex> lk(_mutex);
++_searchProgress;
_pbCond.notify_one();
} else {
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
* 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,
search_condition_func cond,
QList<int> *matchList,
QProgressBar *pb,
QLabel *l)
{
int nRows = rowCount({});
_search(column,
searchText,
cond,
matchList,
1, // step
0, // first
nRows - 1, // last
pb,
l,
nullptr,
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<int> *matchList)
{
int nRows = rowCount({});
_search(sm->column(),
sm->searchText(),
sm->condition(),
matchList,
1, // step
sm->_lastRowSearched + 1, // first
nRows - 1, // last
&sm->_searchProgBar,
&sm->_searchCountLabel,
&sm->_lastRowSearched,
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 step: The step used by the thread of the search when looping over
* the data.
* @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 lastRowSearched: Output location for parameter showing the index of
* the last searched item (data row).
* @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<int> KsFilterProxyModel::searchThread(int column,
const QString &searchText,
search_condition_func cond,
int step,
int first,
int last,
int *lastRowSearched,
bool notify)
{
QList<int> matchList;
_search(column,
searchText,
cond,
&matchList,
step,
first,
last,
nullptr,
nullptr,
lastRowSearched,
notify);
return matchList;
}
int KsFilterProxyModel::mapRowFromSource(int r) const
{
/*
* This works because the row number is shown in column
* TRACE_VIEW_COL_INDEX (or TRACE_VIEW_COL_INDEX - 1 in the case when
* the Stream Id column is hidden).
*/
int column = KsViewModel::TRACE_VIEW_COL_INDEX;
if(_source->singleStream())
column--;
return this->data(this->index(r, column)).toInt();
}
/** Create default (empty) KsViewModel object. */
KsViewModel::KsViewModel(QObject *parent)
: QAbstractTableModel(parent),
_data(nullptr),
_nRows(0),
_markA(KS_NO_ROW_SELECTED),
_markB(KS_NO_ROW_SELECTED),
_singleStream(true)
{
_updateHeader();
}
/** Update the list of table headers. */
void KsViewModel::_updateHeader()
{
beginRemoveColumns(QModelIndex(), 0, _header.count());
endRemoveColumns();
_header.clear();
if (KsUtils::getNStreams() > 1) {
_header << " >> ";
_singleStream = false;
} else {
_singleStream = true;
}
_header << "#" << "CPU" << "Time Stamp" << "Task" << "PID"
<< "Latency" << "Event" << "Info";
beginInsertColumns(QModelIndex(), 0, _header.count() - 1);
endInsertColumns();
}
/**
* 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 && index.column() != 0)
return QVariant::fromValue(QColor(Qt::white));
if (index.row() == _markB && index.column() != 0)
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 (index.column() == TRACE_VIEW_COL_STREAM &&
!_singleStream) {
int sd = _data[index.row()]->stream_id;
QColor col;
col << KsPlot::getColor(&_streamColors, sd);
return QVariant::fromValue(col);
}
}
if (role == Qt::DisplayRole)
return this->getValue(index.column(), index.row());
return {};
}
/** Get the string data stored in a given cell of the table. */
QString KsViewModel::getValueStr(int column, int row) const
{
char *buffer;
int pid;
/*
* If only one Data stream (file) is loaded, the first column
* (TRACE_VIEW_COL_STREAM) is not shown.
*/
if(_singleStream)
column++;
auto lanMakeString = [&buffer] () {
QString str(buffer);
free(buffer);
return str;
};
switch (column) {
case TRACE_VIEW_COL_STREAM :
return QString("%1").arg(_data[row]->stream_id);
case TRACE_VIEW_COL_INDEX :
return QString("%1").arg(row);
case TRACE_VIEW_COL_CPU:
return QString("%1").arg(_data[row]->cpu);
case TRACE_VIEW_COL_TS:
return KsUtils::Ts2String(_data[row]->ts, 6);
case TRACE_VIEW_COL_COMM:
buffer = kshark_get_task(_data[row]);
return lanMakeString();
case TRACE_VIEW_COL_PID:
pid = kshark_get_pid(_data[row]);
return QString("%1").arg(pid);
case TRACE_VIEW_COL_AUX:
buffer = kshark_get_aux_info(_data[row]);
return lanMakeString();
case TRACE_VIEW_COL_EVENT:
buffer = kshark_get_event_name(_data[row]);
return lanMakeString();
case TRACE_VIEW_COL_INFO :
buffer = kshark_get_info(_data[row]);
return lanMakeString();
default:
return {};
}
}
/** Get the data stored in a given cell of the table. */
QVariant KsViewModel::getValue(int column, int row) const
{
return getValueStr(column, row);
}
/**
* 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();
_streamColors = KsPlot::streamColorTable();
endInsertRows();
_updateHeader();
}
/** @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 = KS_NO_ROW_SELECTED;
} else {
_markB = row;
_markA = KS_NO_ROW_SELECTED;
}
}
/** 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);
}
/** Update the color scheme used by the model. */
void KsViewModel::loadColors()
{
beginResetModel();
_streamColors = KsPlot::streamColorTable();
endResetModel();
}
/** @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,
search_condition_func cond,
QList<size_t> *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();
}
/** @brief Reimplements handler to return a flags that enables the item
* (ItemIsEnabled) and allows it to be selecte.
*
* @param index: The index of the item.
*
* @returns Returns the item flags for the given index.
*/
Qt::ItemFlags KsViewModel::flags(const QModelIndex &index) const {
if (!index.isValid())
return Qt::ItemIsEnabled;
return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
}
/** Create a default (empty) KsFilterProxyModel object. */
KsGraphModel::KsGraphModel(QObject *parent)
: QAbstractTableModel(parent)
{
ksmodel_init(&_histo);
}
/** Destroy KsFilterProxyModel object. */
KsGraphModel::~KsGraphModel()
{
ksmodel_clear(&_histo);
}
/**
* @brief Provide the Visualization model with data. Calculate the current
* state of the model.
*/
void KsGraphModel::fill(KsDataStore *data)
{
kshark_entry **entries = data->rows();
size_t n = data->size();
if (n == 0)
return;
beginResetModel();
if (_histo.n_bins == 0)
ksmodel_set_bining(&_histo,
KS_DEFAULT_NBUNS,
entries[0]->ts,
entries[n - 1]->ts);
ksmodel_fill(&_histo, entries, n);
endResetModel();
}
/**
* @brief Shift the time-window of the model forward. Recalculate the current
* state of the model.
*
* @param n: Number of bins to shift.
*/
void KsGraphModel::shiftForward(size_t n)
{
beginResetModel();
ksmodel_shift_forward(&_histo, n);
endResetModel();
}
/**
* @brief Shift the time-window of the model backward. Recalculate the current
* state of the model.
*
* @param n: Number of bins to shift.
*/
void KsGraphModel::shiftBackward(size_t n)
{
beginResetModel();
ksmodel_shift_backward(&_histo, n);
endResetModel();
}
/**
* @brief Move the time-window of the model to a given location. Recalculate
* the current state of the model.
*
* @param ts: position in time to be visualized.
*/
void KsGraphModel::jumpTo(size_t ts)
{
beginResetModel();
ksmodel_jump_to(&_histo, ts);
endResetModel();
}
/**
* @brief Extend the time-window of the model. Recalculate the current state
* of the model.
*
* @param r: Scale factor of the zoom-out.
* @param mark: Focus point of the zoom-out.
*/
void KsGraphModel::zoomOut(double r, int mark)
{
beginResetModel();
ksmodel_zoom_out(&_histo, r, mark);
endResetModel();
}
/**
* @brief Shrink the time-window of the model. Recalculate the current state
* of the model.
*
* @param r: Scale factor of the zoom-in.
* @param mark: Focus point of the zoom-in.
*/
void KsGraphModel::zoomIn(double r, int mark)
{
beginResetModel();
ksmodel_zoom_in(&_histo, r, mark);
endResetModel();
}
/** Quick zoom out. The entire data-set will be visualized. */
void KsGraphModel::quickZoomOut()
{
beginResetModel();
ksmodel_set_bining(&_histo,
_histo.n_bins,
_histo.data[0]->ts,
_histo.data[_histo.data_size - 1]->ts);
ksmodel_fill(&_histo, _histo.data, _histo.data_size);
endResetModel();
}
/**
* @brief Quick zoom in to a state of the Visualization model which has the
* given bin size. The actual value of the bin size may en up being slightly
* different because of the fine tuning performed by the model.
*
* @param binSize: an approximate value for the new size of the bins.
*/
void KsGraphModel::quickZoomIn(uint64_t binSize)
{
double range, r;
range = _histo.max - _histo.min;
r = 1 - (binSize * _histo.n_bins) / range;
zoomIn(r);
}
/** Reset the model. */
void KsGraphModel::reset()
{
beginResetModel();
ksmodel_clear(&_histo);
endResetModel();
}
/** Update the model. Use this function if the data has changed. */
void KsGraphModel::update(KsDataStore *data)
{
beginResetModel();
if (data)
ksmodel_fill(&_histo, data->rows(), data->size());
endResetModel();
}