blob: 9fb68e7b58f62f577d39fdfd46c9859800aa95f1 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
*/
/**
* @file KsDualMarker.cpp
* @brief KernelShark Dual Marker.
*/
#include "KsDualMarker.hpp"
#include "KsGLWidget.hpp"
/**
* Reimplemented handler for mouse press events. Right mouse click events will
* deselect the corresponding marker.
*/
void KsMarkerButton::mousePressEvent(QMouseEvent *e)
{
if(e->button() == Qt::RightButton) {
emit deselect();
return;
}
QPushButton::mousePressEvent(e);
}
/**
* @brief Create KsGraphMark object.
*
* @param s: The Identifier of the marker (state A or state B).
*/
KsGraphMark::KsGraphMark(DualMarkerState s)
: _color(Qt::darkGreen),
_state(s)
{
reset();
_mark._color << _color;
}
/**
* @brief Create KsGraphMark object.
*
* @param s: The Identifier of the marker (state A or state B).
* @param col: The color of the marker.
*/
KsGraphMark::KsGraphMark(DualMarkerState s, QColor col)
: _color(col),
_state(s)
{
reset();
_mark._color << _color;
}
/** Reset the marker. */
void KsGraphMark::reset()
{
_isSet = false;
_bin = -1;
_pos = 0;
_sd = 0;
_mark._visible = false;
}
/**
* @brief Set the marker.
*
* @param data: Input location for the Data Store object.
* @param histo: Input location for the model descriptor.
* @param pos: The index inside the data array this marker will points to.
* @param sd: Data stream identifier.
*/
bool KsGraphMark::set(const KsDataStore &data,
kshark_trace_histo *histo,
ssize_t pos, int sd)
{
uint8_t visFlags;
_isSet = true;
_pos = pos;
_sd = sd;
_ts = data.rows()[_pos]->ts;
visFlags = data.rows()[_pos]->visible;
if ((visFlags & KS_TEXT_VIEW_FILTER_MASK) &&
(visFlags & KS_GRAPH_VIEW_FILTER_MASK))
_mark.setDashed(false);
else
_mark.setDashed(true);
if (_ts > histo->max || _ts < histo->min) {
_bin = -1;
_mark._visible = false;
return false;
}
_bin = (_ts - histo->min)/histo->bin_size;
setVisible(true);
return true;
}
/**
* @brief Use this function to update the marker when the state of the model
* has changed.
*
* @param data: Input location for the Data Store object.
* @param histo: Input location for the model descriptor.
*/
bool KsGraphMark::update(const KsDataStore &data, kshark_trace_histo *histo)
{
if (!_isSet)
return false;
return set(data, histo, this->_pos, this->_sd);
}
/** Unset the Marker and make it invisible. */
void KsGraphMark::remove()
{
_isSet = false;
setVisible(false);
}
/** An operator for getting the opposite state of the marker identifier. */
DualMarkerState operator!(const DualMarkerState &state)
{
if (state == DualMarkerState::B)
return DualMarkerState::A;
return DualMarkerState::B;
}
/** @brief Create a Dual Marker State Machine. */
KsDualMarkerSM::KsDualMarkerSM(QWidget *parent)
: QWidget(parent),
_buttonA("Marker A", this),
_buttonB("Marker B", this),
_labelDeltaDescr(" A,B Delta: ", this),
_markA(DualMarkerState::A, Qt::darkGreen),
_markB(DualMarkerState::B, Qt::darkCyan),
_scCtrlA(this),
_scCtrlB(this)
{
QString styleSheetA, styleSheetB;
_buttonA.setFixedWidth(STRING_WIDTH(" Marker A ") + FONT_WIDTH);
_buttonB.setFixedWidth(STRING_WIDTH(" Marker B ") + FONT_WIDTH);
for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) {
l->setFrameStyle(QFrame::Panel | QFrame::Sunken);
l->setStyleSheet("QLabel {background-color : white; color : black}");
l->setTextInteractionFlags(Qt::TextSelectableByMouse);
l->setFixedWidth(FONT_WIDTH * 16);
}
styleSheetA = "background : " +
_markA._color.name() +
"; color : white";
_stateA = new QState;
_stateA->setObjectName("A");
_stateA->assignProperty(&_buttonA,
"styleSheet",
styleSheetA);
styleSheetB = "background : " +
_markB._color.name() +
"; color : white";
_stateB = new QState;
_stateB->setObjectName("B");
_stateB->assignProperty(&_buttonB,
"styleSheet",
styleSheetB);
/* Define transitions from State A to State B. */
_stateA->addTransition(this, &KsDualMarkerSM::machineToB, _stateB);
_scCtrlA.setKey(Qt::CTRL + Qt::Key_A);
_stateA->addTransition(&_scCtrlB, &QShortcut::activated, _stateB);
connect(&_scCtrlA, &QShortcut::activated,
this, &KsDualMarkerSM::_doStateA);
_stateA->addTransition(&_buttonB, &QPushButton::clicked, _stateB);
connect(&_buttonB, &QPushButton::clicked,
this, &KsDualMarkerSM::_doStateB);
connect(&_buttonB, &KsMarkerButton::deselect,
this, &KsDualMarkerSM::deselectB);
/* Define transitions from State B to State A. */
_stateB->addTransition(this, &KsDualMarkerSM::machineToA, _stateA);
_scCtrlB.setKey(Qt::CTRL + Qt::Key_B);
_stateB->addTransition(&_scCtrlA, &QShortcut::activated, _stateA);
connect(&_scCtrlB, &QShortcut::activated,
this, &KsDualMarkerSM::_doStateB);
_stateB->addTransition(&_buttonA, &QPushButton::clicked, _stateA);
connect(&_buttonA, &QPushButton::clicked,
this, &KsDualMarkerSM::_doStateA);
connect(&_buttonA, &KsMarkerButton::deselect,
this, &KsDualMarkerSM::deselectA);
_machine.setGlobalRestorePolicy(QState::RestoreProperties);
_machine.addState(_stateA);
_machine.addState(_stateB);
_machine.setInitialState(_stateA);
_markState = DualMarkerState::A;
_machine.start();
}
/**
* Reset the Mark A and Mark B and clear the information shown by the Marker's
* toolbar.
*/
void KsDualMarkerSM::reset()
{
_markA.reset();
_markB.reset();
_labelMA.setText("");
_labelMB.setText("");
_labelDelta.setText("");
}
/** Restart the Dual Marker State Machine. */
void KsDualMarkerSM::restart()
{
_machine.stop();
reset();
_markState = DualMarkerState::A;
_machine.start();
}
void KsDualMarkerSM::_doStateA()
{
if (_markState != DualMarkerState::A) {
_markState = DualMarkerState::A;
emit markSwitchForView();
} else if (activeMarker()._isSet) {
emit updateView(activeMarker()._pos, true);
emit updateGraph(activeMarker()._pos);
}
}
void KsDualMarkerSM::_doStateB()
{
if (_markState != DualMarkerState::B) {
_markState = DualMarkerState::B;
emit markSwitchForView();
} else if (activeMarker()._isSet) {
emit updateView(activeMarker()._pos, true);
emit updateGraph(activeMarker()._pos);
}
}
/** Get the Graph marker associated with a given state. */
KsGraphMark &KsDualMarkerSM::getMarker(DualMarkerState s)
{
if (s == DualMarkerState::A)
return _markA;
return _markB;
}
/** Position all buttons and labels of the Dual Marker in a toolbar. */
void KsDualMarkerSM::placeInToolBar(QToolBar *tb)
{
tb->addWidget(new QLabel(" "));
tb->addWidget(&_buttonA);
tb->addWidget(&_labelMA);
tb->addSeparator();
tb->addWidget(new QLabel(" "));
tb->addWidget(&_buttonB);
tb->addWidget(&_labelMB);
tb->addSeparator();
tb->addWidget(&_labelDeltaDescr);
tb->addWidget(&_labelDelta);
}
/** Set the state of the Dual Marker State Machine. */
void KsDualMarkerSM::setState(DualMarkerState st) {
if (st == _markState)
return;
if (st == DualMarkerState::A) {
emit machineToA();
_doStateA();
}
if (st == DualMarkerState::B) {
emit machineToB();
_doStateB();
}
}
/**
* @brief Use this function to update the two marker when the state of the
* model has changed.
*
* @param data: Input location for the Data Store object.
* @param glw: Input location for the OpenGL widget object.
*/
void KsDualMarkerSM::updateMarkers(const KsDataStore &data,
KsGLWidget *glw)
{
if(_markA.update(data, glw->model()->histo()))
glw->setMarkPoints(data, &_markA);
if(_markB.update(data, glw->model()->histo()))
glw->setMarkPoints(data, &_markB);
updateLabels();
}
/**
* @brief Use this function to update the labels when the state of the model
* has changed.
*/
void KsDualMarkerSM::updateLabels()
{
char separator(' ');
int precision(6); // 1 microsecond precision.
auto lamSetTimeLabel = [&precision, &separator] (QLabel &l, int64_t t) {
QString time = KsUtils::Ts2String(t, precision);
int i = time.indexOf('.') + 4;
/* Insert separators for milliseconds amd microseconds. */
while (i < time.size()) {
time.insert(i, separator);
i = i + 4;
}
l.setText(time);
};
// Marker A
if (_markA._isSet)
lamSetTimeLabel(_labelMA, _markA._ts);
else
_labelMA.clear();
// Marker B
if (_markB._isSet)
lamSetTimeLabel(_labelMB, _markB._ts);
else
_labelMB.clear();
// Delta
if (_markA._isSet && _markB._isSet) {
precision = 9; // 1 nanoseconds precision.
lamSetTimeLabel(_labelDelta, _markB._ts - _markA._ts);
} else {
_labelDelta.clear();
}
}