| // 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::SHIFT | 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::SHIFT | 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(); |
| } |
| } |