@@ -32,11 +32,13 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
message(STATUS "libkshark-gui")
set (ks-guiLib_hdr KsUtils.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
+ KsDualMarker.cpp
KsWidgetsLib.cpp)
target_link_libraries(kshark-gui kshark-plot
new file mode 100644
@@ -0,0 +1,332 @@
+// 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"
+
+/**
+ * @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;
+ _cpu = -1;
+ _task = -1;
+ _pos = 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 cpuGraph: The index of the CPU Graph this marker points to.
+ * @param taskGraph: The index of the Task Graph this marker points to.
+ */
+bool KsGraphMark::set(const KsDataStore &data,
+ kshark_trace_histo *histo,
+ size_t pos, int cpuGraph, int taskGraph)
+{
+ _isSet = true;
+ _pos = pos;
+ _ts = data.rows()[_pos]->ts;
+ _cpu = cpuGraph;
+ _task = taskGraph;
+
+ 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->_cpu, this->_task);
+}
+
+/** 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 "));
+ _buttonB.setFixedWidth(STRING_WIDTH(" Marker B "));
+
+ for (auto const &l: {&_labelMA, &_labelMB, &_labelDelta}) {
+ l->setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ l->setStyleSheet("QLabel {background-color : white;}");
+ 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);
+
+ _stateA->assignProperty(&_buttonB,
+ "styleSheet",
+ "color : rgb(70, 70, 70)");
+
+ styleSheetB = "background : " +
+ _markB._color.name() +
+ "; color : white";
+
+ _stateB = new QState;
+ _stateB->setObjectName("B");
+ _stateB->assignProperty(&_buttonA,
+ "styleSheet",
+ "color : rgb(70, 70, 70)");
+
+ _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);
+
+ /* 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);
+
+ _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 histo: Input location for the model descriptor.
+ */
+void KsDualMarkerSM::updateMarkers(const KsDataStore &data,
+ kshark_trace_histo *histo)
+{
+ _markA.update(data, histo);
+ _markB.update(data, histo);
+
+ updateLabels();
+}
+
+/**
+ * @brief Use this function to update the labels when the state of the model
+ * has changed.
+ */
+void KsDualMarkerSM::updateLabels()
+{
+ QString mark, delta;
+
+ // Marker A
+ if (_markA._isSet) {
+ mark = KsUtils::Ts2String(_markA._ts, 7);
+ _labelMA.setText(mark);
+ } else {
+ _labelMA.setText("");
+ }
+
+ // Marker B
+ if (_markB._isSet) {
+ mark = KsUtils::Ts2String(_markB._ts, 7);
+ _labelMB.setText(mark);
+ } else {
+ _labelMB.setText("");
+ }
+
+ // Delta
+ if (_markA._isSet && _markB._isSet) {
+ delta = KsUtils::Ts2String(_markB._ts - _markA._ts, 7);
+ _labelDelta.setText(delta);
+ } else {
+ _labelDelta.setText("");
+ }
+}
new file mode 100644
@@ -0,0 +1,188 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ * @file KsDualMarker.hpp
+ * @brief KernelShark Dual Marker.
+ */
+
+#ifndef _KS_DUAL_MARKER_H
+#define _KS_DUAL_MARKER_H
+
+// Qt
+#include <QtWidgets>
+
+// KernelShark
+#include "KsUtils.hpp"
+#include "KsPlotTools.hpp"
+
+/** The KsGraphMark represents a marker for KernelShark GUI. */
+class KsGraphMark : public QObject
+{
+ Q_OBJECT
+public:
+ KsGraphMark() = delete;
+
+ KsGraphMark(DualMarkerState s);
+
+ KsGraphMark(DualMarkerState s, QColor col);
+
+ void reset();
+
+ bool set(const KsDataStore &data,
+ kshark_trace_histo *histo,
+ size_t pos,
+ int cpuGraph,
+ int taskGraph);
+
+ bool update(const KsDataStore &data, kshark_trace_histo *histo);
+
+ /** Is this marker visible. */
+ bool isVisible() const {return _mark._visible;}
+
+ /** Draw this marker. */
+ void draw() const {_mark.draw();}
+
+ /** Set the visiblity of the marker. */
+ void setVisible(bool v) {_mark._visible = v;}
+
+ void remove();
+
+public:
+ /** Is this marker set. */
+ bool _isSet;
+
+ /** The number of the bin this marker points to. */
+ int _bin;
+
+ /** The index of the CPU Graph this marker points to. */
+ int _cpu;
+
+ /** The index of the Task Graph this marker points to. */
+ int _task;
+
+ /** The index inside the data array this marker points to. */
+ size_t _pos;
+
+ /** The timestamp of the marker. */
+ uint64_t _ts;
+
+ /** The RGB color of the marker. */
+ QColor _color;
+
+ /** The Identifier of the marker (A or B). */
+ const DualMarkerState _state;
+
+ /** The graphical element of this marker. */
+ KsPlot::Mark _mark;
+};
+
+DualMarkerState operator !(const DualMarkerState &state);
+
+/**
+ * The DualMarkerState represents the State Machine of the KernelShark GUI
+ * Dual Marker.
+ */
+class KsDualMarkerSM : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit KsDualMarkerSM(QWidget *parent = nullptr);
+
+ void reset();
+
+ void restart();
+
+ void placeInToolBar(QToolBar *tb);
+
+ /** Get the Identifier of the current state of the State Machine. */
+ DualMarkerState getState() const {return _markState;}
+
+ void setState(DualMarkerState st);
+
+ KsGraphMark &getMarker(DualMarkerState s);
+
+ /** Get the active marker. */
+ KsGraphMark &activeMarker() {return getMarker(_markState);}
+
+ /** Get the passive marker. */
+ KsGraphMark &passiveMarker() {return getMarker(!_markState);}
+
+ /** Get the marker A. */
+ KsGraphMark &markerA() {return _markA;}
+
+ /** Get the marker B. */
+ KsGraphMark &markerB() {return _markB;}
+
+ /** Get a pointer to the State A object. */
+ QState *stateAPtr() {return _stateA;}
+
+ /** Get a pointer to the State B object. */
+ QState *stateBPtr() {return _stateB;}
+
+ void updateMarkers(const KsDataStore &data,
+ kshark_trace_histo *histo);
+
+ void updateLabels();
+
+signals:
+ /**
+ * This signal is emitted when the Table View has to switch the color
+ * of the selected row.
+ */
+ void markSwitchForView();
+
+ /**
+ * This signal is emitted when the Table View has to show a different
+ * entry (row).
+ */
+ void updateView(size_t pos, bool mark);
+
+ /**
+ * This signal is emitted when the Graph mark has to show a different
+ * entry.
+ */
+ void updateGraph(size_t pos);
+
+ /**
+ * This signal is emitted when the State Machine has to switch to
+ * state A.
+ */
+ void machineToA();
+
+ /**
+ * This signal is emitted when the State Machine has to switch to
+ * state B.
+ */
+ void machineToB();
+
+private:
+ QPushButton _buttonA;
+
+ QPushButton _buttonB;
+
+ QLabel _labelMA, _labelMB, _labelDelta;
+
+ QLabel _labelDeltaDescr;
+
+ QState *_stateA;
+
+ QState *_stateB;
+
+ QStateMachine _machine;
+
+ DualMarkerState _markState;
+
+ KsGraphMark _markA, _markB;
+
+ QShortcut _scCtrlA, _scCtrlB;
+
+ void _doStateA();
+
+ void _doStateB();
+};
+
+#endif