@@ -36,6 +36,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
KsGLWidget.hpp
KsDualMarker.hpp
KsWidgetsLib.hpp
+ KsTraceGraph.hpp
KsTraceViewer.hpp)
QT5_WRAP_CPP(ks-guiLib_hdr_moc ${ks-guiLib_hdr})
@@ -45,6 +46,7 @@ if (Qt5Widgets_FOUND AND Qt5Network_FOUND)
KsGLWidget.cpp
KsDualMarker.cpp
KsWidgetsLib.cpp
+ KsTraceGraph.cpp
KsTraceViewer.cpp)
target_link_libraries(kshark-gui kshark-plot
new file mode 100644
@@ -0,0 +1,690 @@
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ * @file KsTraceGraph.cpp
+ * @brief KernelShark Trace Graph widget.
+ */
+
+// KernelShark
+#include "KsUtils.hpp"
+#include "KsDualMarker.hpp"
+#include "KsTraceGraph.hpp"
+
+/** Create a default (empty) Trace graph widget. */
+KsTraceGraph::KsTraceGraph(QWidget *parent)
+: QWidget(parent),
+ _pointerBar(this),
+ _navigationBar(this),
+ _zoomInButton("+", this),
+ _quickZoomInButton("++", this),
+ _zoomOutButton("-", this),
+ _quickZoomOutButton("- -", this),
+ _scrollLeftButton("<", this),
+ _scrollRightButton(">", this),
+ _labelP1("Pointer: ", this),
+ _labelP2("", this),
+ _labelI1("", this),
+ _labelI2("", this),
+ _labelI3("", this),
+ _labelI4("", this),
+ _labelI5("", this),
+ _scrollArea(this),
+ _drawWindow(&_scrollArea),
+ _legendWindow(&_drawWindow),
+ _legendAxisX(&_drawWindow),
+ _labelXMin("", &_legendAxisX),
+ _labelXMid("", &_legendAxisX),
+ _labelXMax("", &_legendAxisX),
+ _glWindow(&_drawWindow),
+ _mState(nullptr),
+ _data(nullptr),
+ _keyPressed(false)
+{
+ auto lamMakeNavButton = [&](QPushButton *b) {
+ b->setMaximumWidth(FONT_WIDTH * 5);
+
+ connect(b, &QPushButton::released,
+ this, &KsTraceGraph::_stopUpdating);
+ _navigationBar.addWidget(b);
+ };
+
+ _pointerBar.setMaximumHeight(FONT_HEIGHT * 1.75);
+ _pointerBar.setOrientation(Qt::Horizontal);
+
+ _navigationBar.setMaximumHeight(FONT_HEIGHT * 1.75);
+ _navigationBar.setMinimumWidth(FONT_WIDTH * 90);
+ _navigationBar.setOrientation(Qt::Horizontal);
+
+ _pointerBar.addWidget(&_labelP1);
+ _labelP2.setFrameStyle(QFrame::Panel | QFrame::Sunken);
+ _labelP2.setStyleSheet("QLabel { background-color : white;}");
+ _labelP2.setTextInteractionFlags(Qt::TextSelectableByMouse);
+ _labelP2.setFixedWidth(FONT_WIDTH * 16);
+ _pointerBar.addWidget(&_labelP2);
+ _pointerBar.addSeparator();
+
+ _labelI1.setStyleSheet("QLabel {color : blue;}");
+ _labelI2.setStyleSheet("QLabel {color : green;}");
+ _labelI3.setStyleSheet("QLabel {color : red;}");
+ _labelI4.setStyleSheet("QLabel {color : blue;}");
+ _labelI5.setStyleSheet("QLabel {color : green;}");
+
+ _pointerBar.addWidget(&_labelI1);
+ _pointerBar.addSeparator();
+ _pointerBar.addWidget(&_labelI2);
+ _pointerBar.addSeparator();
+ _pointerBar.addWidget(&_labelI3);
+ _pointerBar.addSeparator();
+ _pointerBar.addWidget(&_labelI4);
+ _pointerBar.addSeparator();
+ _pointerBar.addWidget(&_labelI5);
+
+ _legendAxisX.setFixedHeight(FONT_HEIGHT * 1.5);
+ _legendAxisX.setLayout(new QHBoxLayout);
+ _legendAxisX.layout()->setSpacing(0);
+ _legendAxisX.layout()->setContentsMargins(0, 0, FONT_WIDTH, 0);
+
+ _labelXMin.setAlignment(Qt::AlignLeft);
+ _labelXMid.setAlignment(Qt::AlignHCenter);
+ _labelXMax.setAlignment(Qt::AlignRight);
+
+ _legendAxisX.layout()->addWidget(&_labelXMin);
+ _legendAxisX.layout()->addWidget(&_labelXMid);
+ _legendAxisX.layout()->addWidget(&_labelXMax);
+ _drawWindow.setMinimumSize(100, 100);
+ _drawWindow.setStyleSheet("QWidget {background-color : white;}");
+
+ _drawLayout.setContentsMargins(0, 0, 0, 0);
+ _drawLayout.setSpacing(0);
+ _drawLayout.addWidget(&_legendAxisX, 0, 1);
+ _drawLayout.addWidget(&_legendWindow, 1, 0);
+ _drawLayout.addWidget(&_glWindow, 1, 1);
+ _drawWindow.setLayout(&_drawLayout);
+
+ _drawWindow.installEventFilter(this);
+
+ connect(&_glWindow, &KsGLWidget::select,
+ this, &KsTraceGraph::markEntry);
+
+ connect(&_glWindow, &KsGLWidget::found,
+ this, &KsTraceGraph::_setPointerInfo);
+
+ connect(&_glWindow, &KsGLWidget::notFound,
+ this, &KsTraceGraph::_resetPointer);
+
+ connect(&_glWindow, &KsGLWidget::zoomIn,
+ this, &KsTraceGraph::_zoomIn);
+
+ connect(&_glWindow, &KsGLWidget::zoomOut,
+ this, &KsTraceGraph::_zoomOut);
+
+ connect(&_glWindow, &KsGLWidget::scrollLeft,
+ this, &KsTraceGraph::_scrollLeft);
+
+ connect(&_glWindow, &KsGLWidget::scrollRight,
+ this, &KsTraceGraph::_scrollRight);
+
+ connect(&_glWindow, &KsGLWidget::stopUpdating,
+ this, &KsTraceGraph::_stopUpdating);
+
+ connect(_glWindow.model(), &KsGraphModel::modelReset,
+ this, &KsTraceGraph::_updateTimeLegends);
+
+ _scrollArea.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ _scrollArea.setWidget(&_drawWindow);
+
+ lamMakeNavButton(&_scrollLeftButton);
+ connect(&_scrollLeftButton, &QPushButton::pressed,
+ this, &KsTraceGraph::_scrollLeft);
+
+ lamMakeNavButton(&_zoomInButton);
+ connect(&_zoomInButton, &QPushButton::pressed,
+ this, &KsTraceGraph::_zoomIn);
+
+ lamMakeNavButton(&_zoomOutButton);
+ connect(&_zoomOutButton, &QPushButton::pressed,
+ this, &KsTraceGraph::_zoomOut);
+
+ lamMakeNavButton(&_scrollRightButton);
+ connect(&_scrollRightButton, &QPushButton::pressed,
+ this, &KsTraceGraph::_scrollRight);
+
+ _navigationBar.addSeparator();
+
+ lamMakeNavButton(&_quickZoomInButton);
+ connect(&_quickZoomInButton, &QPushButton::pressed,
+ this, &KsTraceGraph::_quickZoomIn);
+
+ lamMakeNavButton(&_quickZoomOutButton);
+ connect(&_quickZoomOutButton, &QPushButton::pressed,
+ this, &KsTraceGraph::_quickZoomOut);
+
+ _layout.addWidget(&_pointerBar);
+ _layout.addWidget(&_navigationBar);
+ _layout.addWidget(&_scrollArea);
+ this->setLayout(&_layout);
+ updateGeom();
+}
+
+/**
+ * @brief Load and show trace data.
+ *
+ * @param data: Input location for the KsDataStore object.
+ * KsDataStore::loadDataFile() must be called first.
+ */
+void KsTraceGraph::loadData(KsDataStore *data)
+{
+ _data = data;
+ _glWindow.loadData(data);
+ _updateGraphLegends();
+ updateGeom();
+}
+
+/** Connect the KsGLWidget widget and the State machine of the Dual marker. */
+void KsTraceGraph::setMarkerSM(KsDualMarkerSM *m)
+{
+ _mState = m;
+ _navigationBar.addSeparator();
+ _mState->placeInToolBar(&_navigationBar);
+ _glWindow.setMarkerSM(m);
+}
+
+/** Reset (empty) the widget. */
+void KsTraceGraph::reset()
+{
+ /* Clear the all graph lists and update. */
+ _glWindow._cpuList = {};
+ _glWindow._taskList = {};
+
+ _labelP2.setText("");
+ for (auto l1: {&_labelI1, &_labelI2, &_labelI3, &_labelI4, &_labelI5})
+ l1->setText("");
+
+ _glWindow.model()->reset();
+ _selfUpdate();
+ for (auto l2: {&_labelXMin, &_labelXMid, &_labelXMax})
+ l2->setText("");
+}
+
+void KsTraceGraph::_selfUpdate()
+{
+ _updateGraphLegends();
+ _updateTimeLegends();
+ _markerReDraw();
+ updateGeom();
+}
+
+void KsTraceGraph::_zoomIn()
+{
+ _updateGraphs(GraphActions::ZoomIn);
+}
+
+void KsTraceGraph::_zoomOut()
+{
+ _updateGraphs(GraphActions::ZoomOut);
+}
+
+void KsTraceGraph::_quickZoomIn()
+{
+ /* Bin size will be 100 ns. */
+ _glWindow.model()->quickZoomIn(100);
+ if (_mState->activeMarker()._isSet &&
+ _mState->activeMarker().isVisible()) {
+ /*
+ * Use the position of the active marker as
+ * a focus point of the zoom.
+ */
+ uint64_t ts = _mState->activeMarker()._ts;
+ _glWindow.model()->jumpTo(ts);
+ }
+}
+
+void KsTraceGraph::_quickZoomOut()
+{
+ _glWindow.model()->quickZoomOut();
+}
+
+void KsTraceGraph::_scrollLeft()
+{
+ _updateGraphs(GraphActions::ScrollLeft);
+}
+
+void KsTraceGraph::_scrollRight()
+{
+ _updateGraphs(GraphActions::ScrollRight);
+}
+
+void KsTraceGraph::_stopUpdating()
+{
+ /*
+ * The user is no longer pressing the action button. Reset the
+ * "Key Pressed" flag. This will stop the ongoing user action.
+ */
+ _keyPressed = false;
+}
+
+void KsTraceGraph::_resetPointer(uint64_t ts, int cpu, int pid)
+{
+ uint64_t sec, usec;
+ QString pointer;
+
+ kshark_convert_nano(ts, &sec, &usec);
+ pointer.sprintf("%lu.%lu", sec, usec);
+ _labelP2.setText(pointer);
+
+ if (pid > 0 && cpu >= 0) {
+ struct kshark_context *kshark_ctx(NULL);
+
+ if (!kshark_instance(&kshark_ctx))
+ return;
+
+ QString comm(tep_data_comm_from_pid(kshark_ctx->pevent, pid));
+ comm.append("-");
+ comm.append(QString("%1").arg(pid));
+ _labelI1.setText(comm);
+ _labelI2.setText(QString("CPU %1").arg(cpu));
+ } else {
+ _labelI1.setText("");
+ _labelI2.setText("");
+ }
+
+ for (auto const &l: {&_labelI3, &_labelI4, &_labelI5}) {
+ l->setText("");
+ }
+}
+
+void KsTraceGraph::_setPointerInfo(size_t i)
+{
+ kshark_entry *e = _data->rows()[i];
+ QString event(kshark_get_event_name_easy(e));
+ QString lat(kshark_get_latency_easy(e));
+ QString info(kshark_get_info_easy(e));
+ QString comm(kshark_get_task_easy(e));
+ QString pointer, elidedText;
+ int labelWidth, width;
+ uint64_t sec, usec;
+
+ kshark_convert_nano(e->ts, &sec, &usec);
+ pointer.sprintf("%lu.%lu", sec, usec);
+ _labelP2.setText(pointer);
+
+ comm.append("-");
+ comm.append(QString("%1").arg(kshark_get_pid_easy(e)));
+
+ _labelI1.setText(comm);
+ _labelI2.setText(QString("CPU %1").arg(e->cpu));
+ _labelI3.setText(lat);
+ _labelI4.setText(event);
+ _labelI5.setText(info);
+ QCoreApplication::processEvents();
+
+ labelWidth =
+ _pointerBar.geometry().right() - _labelI4.geometry().right();
+ if (labelWidth > STRING_WIDTH(info) + FONT_WIDTH * 5)
+ return;
+
+ /*
+ * The Info string is too long and cannot be displayed on the toolbar.
+ * Try to fit the text in the available space.
+ */
+ QFontMetrics metrix(_labelI5.font());
+ width = labelWidth - FONT_WIDTH * 3;
+ elidedText = metrix.elidedText(info, Qt::ElideRight, width);
+
+ while(labelWidth < STRING_WIDTH(elidedText) + FONT_WIDTH * 5) {
+ width -= FONT_WIDTH * 3;
+ elidedText = metrix.elidedText(info, Qt::ElideRight, width);
+ }
+
+ _labelI5.setText(elidedText);
+ _labelI5.setVisible(true);
+ QCoreApplication::processEvents();
+}
+
+/**
+ * @brief Use the active marker to select particular entry.
+ *
+ * @param row: The index of the entry to be selected by the marker.
+ */
+void KsTraceGraph::markEntry(size_t row)
+{
+ int graph, cpuGrId, taskGrId;
+
+ _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId);
+
+ /*
+ * If a Task graph has been found, this Task graph will be
+ * visible. If no Task graph has been found, make visible
+ * the corresponding CPU graph.
+ */
+ if (taskGrId >= 0)
+ graph = taskGrId;
+ else
+ graph = cpuGrId;
+
+ _scrollArea.ensureVisible(0,
+ _legendAxisX.height() +
+ _glWindow.vMargin() +
+ KS_GRAPH_HEIGHT / 2 +
+ graph*(KS_GRAPH_HEIGHT + _glWindow.vSpacing()),
+ 50,
+ KS_GRAPH_HEIGHT / 2 + _glWindow.vSpacing() / 2);
+
+ _glWindow.model()->jumpTo(_data->rows()[row]->ts);
+ _mState->activeMarker().set(*_data,
+ _glWindow.model()->histo(),
+ row, cpuGrId, taskGrId);
+
+ _mState->updateMarkers(*_data, &_glWindow);
+}
+
+void KsTraceGraph::_markerReDraw()
+{
+ int cpuGrId, taskGrId;
+ size_t row;
+
+ if (_mState->markerA()._isSet) {
+ row = _mState->markerA()._pos;
+ _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId);
+ _mState->markerA().set(*_data,
+ _glWindow.model()->histo(),
+ row, cpuGrId, taskGrId);
+ }
+
+ if (_mState->markerB()._isSet) {
+ row = _mState->markerB()._pos;
+ _glWindow.findGraphIds(*_data->rows()[row], &cpuGrId, &taskGrId);
+ _mState->markerB().set(*_data,
+ _glWindow.model()->histo(),
+ row, cpuGrId, taskGrId);
+ }
+}
+
+/**
+ * @brief Redreaw all CPU graphs.
+ *
+ * @param v: CPU ids to be plotted.
+ */
+void KsTraceGraph::cpuReDraw(QVector<int> v)
+{
+ _glWindow._cpuList = v;
+ _selfUpdate();
+}
+
+/**
+ * @brief Redreaw all Task graphs.
+ *
+ * @param v: Process ids of the tasks to be plotted.
+ */
+void KsTraceGraph::taskReDraw(QVector<int> v)
+{
+ _glWindow._taskList = v;
+ _selfUpdate();
+}
+
+/** Add (and plot) a CPU graph to the existing list of CPU graphs. */
+void KsTraceGraph::addCPUPlot(int cpu)
+{
+ if (_glWindow._cpuList.contains(cpu))
+ return;
+
+ _glWindow._cpuList.append(cpu);
+ _selfUpdate();
+}
+
+/** Add (and plot) a Task graph to the existing list of Task graphs. */
+void KsTraceGraph::addTaskPlot(int pid)
+{
+ if (_glWindow._taskList.contains(pid))
+ return;
+
+ _glWindow._taskList.append(pid);
+ _selfUpdate();
+}
+
+/** Update the content of all graphs. */
+void KsTraceGraph::update(KsDataStore *data)
+{
+ _glWindow.model()->update(data);
+ _selfUpdate();
+}
+
+/** Update the geometry of the widget. */
+void KsTraceGraph::updateGeom()
+{
+ int saWidth, saHeight, dwWidth, hMin;
+
+ /* Set the size of the Scroll Area. */
+ saWidth = width() - _layout.contentsMargins().left() -
+ _layout.contentsMargins().right();
+
+ saHeight = height() - _pointerBar.height() -
+ _navigationBar.height() -
+ _layout.spacing() * 2 -
+ _layout.contentsMargins().top() -
+ _layout.contentsMargins().bottom();
+
+ _scrollArea.resize(saWidth, saHeight);
+
+ /*
+ * Calculate the width of the Draw Window, taking into account the size
+ * of the scroll bar.
+ */
+ dwWidth = _scrollArea.width();
+ if (_glWindow.height() + _legendAxisX.height() > _scrollArea.height())
+ dwWidth -=
+ qApp->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
+
+ /*
+ * Set the height of the Draw window according to the number of
+ * plotted graphs.
+ */
+ _drawWindow.resize(dwWidth,
+ _glWindow.height() + _legendAxisX.height());
+
+ /* Set the minimum height of the Graph widget. */
+ hMin = _drawWindow.height() +
+ _pointerBar.height() +
+ _navigationBar.height() +
+ _layout.contentsMargins().top() +
+ _layout.contentsMargins().bottom();
+
+ if (hMin > KS_GRAPH_HEIGHT * 8)
+ hMin = KS_GRAPH_HEIGHT * 8;
+
+ setMinimumHeight(hMin);
+
+ /*
+ * Now use the height of the Draw Window to fix the maximum height
+ * of the Graph widget.
+ */
+ setMaximumHeight(_drawWindow.height() +
+ _pointerBar.height() +
+ _navigationBar.height() +
+ _layout.spacing() * 2 +
+ _layout.contentsMargins().top() +
+ _layout.contentsMargins().bottom() +
+ 2); /* Just a little bit of extra space. This will
+ * allow the scroll bar to disappear when the
+ * widget is extended to maximum.
+ */
+}
+
+void KsTraceGraph::_updateGraphLegends()
+{
+ QString graphLegends, graphName;
+ QVBoxLayout *layout;
+ int width = 0;
+
+ if (_legendWindow.layout()) {
+ /*
+ * Remove and delete the existing layout of the legend window.
+ */
+ QLayoutItem *child;
+ while ((child = _legendWindow.layout()->takeAt(0)) != 0) {
+ delete child->widget();
+ delete child;
+ }
+
+ delete _legendWindow.layout();
+ }
+
+ layout = new QVBoxLayout;
+ layout->setContentsMargins(FONT_WIDTH, 0, 0, 0);
+ layout->setSpacing(_glWindow.vSpacing());
+ layout->setAlignment(Qt::AlignTop);
+ layout->addSpacing(_glWindow.vMargin());
+
+ auto lamMakeName = [&]() {
+ QLabel *name = new QLabel(graphName);
+
+ if (width < STRING_WIDTH(graphName))
+ width = STRING_WIDTH(graphName);
+
+ name->setAlignment(Qt::AlignBottom);
+ name->setStyleSheet("QLabel {background-color : white;}");
+ name->setFixedHeight(KS_GRAPH_HEIGHT);
+ layout->addWidget(name);
+ };
+
+ for (auto const &cpu: _glWindow._cpuList) {
+ graphName = QString("CPU %1").arg(cpu);
+ lamMakeName();
+ }
+
+ for (auto const &pid: _glWindow._taskList) {
+ graphName = QString(tep_data_comm_from_pid(_data->tep(),
+ pid));
+ graphName.append(QString("-%1").arg(pid));
+ lamMakeName();
+ }
+
+ _legendWindow.setLayout(layout);
+ _legendWindow.setMaximumWidth(width + FONT_WIDTH);
+}
+
+void KsTraceGraph::_updateTimeLegends()
+{
+ uint64_t sec, usec, tsMid;
+ QString tMin, tMid, tMax;
+
+ kshark_convert_nano(_glWindow.model()->histo()->min, &sec, &usec);
+ tMin.sprintf("%lu.%lu", sec, usec);
+ _labelXMin.setText(tMin);
+
+ tsMid = (_glWindow.model()->histo()->min +
+ _glWindow.model()->histo()->max) / 2;
+ kshark_convert_nano(tsMid, &sec, &usec);
+ tMid.sprintf("%lu.%lu", sec, usec);
+ _labelXMid.setText(tMid);
+
+ kshark_convert_nano(_glWindow.model()->histo()->max, &sec, &usec);
+ tMax.sprintf("%lu.%lu", sec, usec);
+ _labelXMax.setText(tMax);
+}
+
+/**
+ * Reimplemented event handler used to update the geometry of the widget on
+ * resize events.
+ */
+void KsTraceGraph::resizeEvent(QResizeEvent* event)
+{
+ updateGeom();
+}
+
+/**
+ * Reimplemented event handler (overriding a virtual function from QObject)
+ * used to detect the position of the mouse with respect to the Draw window and
+ * according to this position to grab / release the focus of the keyboard. The
+ * function has nothing to do with the filtering of the trace events.
+ */
+bool KsTraceGraph::eventFilter(QObject* obj, QEvent* evt)
+{
+ if (obj == &_drawWindow && evt->type() == QEvent::Enter)
+ _glWindow.setFocus();
+
+ if (obj == &_drawWindow && evt->type() == QEvent::Leave)
+ _glWindow.clearFocus();
+
+ return QWidget::eventFilter(obj, evt);
+}
+
+void KsTraceGraph::_updateGraphs(GraphActions action)
+{
+ double k;
+ int bin;
+
+ /*
+ * Set the "Key Pressed" flag. The flag will stay set as long as the user
+ * keeps the corresponding action button pressed.
+ */
+ _keyPressed = true;
+
+ /* Initialize the zooming factor with a small value. */
+ k = .01;
+ while (_keyPressed) {
+ switch (action) {
+ case GraphActions::ZoomIn:
+ if (_mState->activeMarker()._isSet &&
+ _mState->activeMarker().isVisible()) {
+ /*
+ * Use the position of the active marker as
+ * a focus point of the zoom.
+ */
+ bin = _mState->activeMarker()._bin;
+ _glWindow.model()->zoomIn(k, bin);
+ } else {
+ /*
+ * The default focus point is the center of the
+ * range interval of the model.
+ */
+ _glWindow.model()->zoomIn(k);
+ }
+
+ break;
+
+ case GraphActions::ZoomOut:
+ if (_mState->activeMarker()._isSet &&
+ _mState->activeMarker().isVisible()) {
+ /*
+ * Use the position of the active marker as
+ * a focus point of the zoom.
+ */
+ bin = _mState->activeMarker()._bin;
+ _glWindow.model()->zoomOut(k, bin);
+ } else {
+ /*
+ * The default focus point is the center of the
+ * range interval of the model.
+ */
+ _glWindow.model()->zoomOut(k);
+ }
+
+ break;
+
+ case GraphActions::ScrollLeft:
+ _glWindow.model()->shiftBackward(10);
+ break;
+
+ case GraphActions::ScrollRight:
+ _glWindow.model()->shiftForward(10);
+ break;
+ }
+
+ /*
+ * As long as the action button is pressed, the zooming factor
+ * will grow smoothly until it reaches a maximum value. This
+ * will have a visible effect of an accelerating zoom.
+ */
+ if (k < .25)
+ k *= 1.02;
+
+ _mState->updateMarkers(*_data, &_glWindow);
+ _updateTimeLegends();
+ QCoreApplication::processEvents();
+ }
+}
new file mode 100644
@@ -0,0 +1,137 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
+ */
+
+/**
+ * @file KsTraceGraph.hpp
+ * @brief KernelShark Trace Graph widget.
+ */
+#ifndef _KS_TRACEGRAPH_H
+#define _KS_TRACEGRAPH_H
+
+// KernelShark
+#include "KsGLWidget.hpp"
+
+/**
+ * Scroll Area class, needed in order to reimplemented the handler for mouse
+ * wheel events.
+ */
+class KsGraphScrollArea : public QScrollArea {
+public:
+ /** Create a default Scroll Area. */
+ explicit KsGraphScrollArea(QWidget *parent = nullptr)
+ : QScrollArea(parent) {}
+
+ /**
+ * Reimplemented handler for mouse wheel events. All mouse wheel
+ * events will be ignored.
+ */
+ void wheelEvent(QWheelEvent *evt) {evt->ignore();}
+};
+
+/**
+ * The KsTraceViewer class provides a widget for interactive visualization of
+ * trace data shown as time-series.
+ */
+class KsTraceGraph : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit KsTraceGraph(QWidget *parent = nullptr);
+
+ void loadData(KsDataStore *data);
+
+ void setMarkerSM(KsDualMarkerSM *m);
+
+ void reset();
+
+ /** Get the KsGLWidget object. */
+ KsGLWidget *glPtr() {return &_glWindow;}
+
+ void markEntry(size_t);
+
+ void cpuReDraw(QVector<int>);
+
+ void taskReDraw(QVector<int>);
+
+ void addCPUPlot(int);
+
+ void addTaskPlot(int);
+
+ void update(KsDataStore *data);
+
+ void updateGeom();
+
+ void resizeEvent(QResizeEvent* event) override;
+
+ bool eventFilter(QObject* obj, QEvent* evt) override;
+
+private:
+
+ void _zoomIn();
+
+ void _zoomOut();
+
+ void _quickZoomIn();
+
+ void _quickZoomOut();
+
+ void _scrollLeft();
+
+ void _scrollRight();
+
+ void _stopUpdating();
+
+ void _resetPointer(uint64_t ts, int cpu, int pid);
+
+ void _setPointerInfo(size_t);
+
+ void _updateTimeLegends();
+
+ void _updateGraphLegends();
+
+ void _selfUpdate();
+
+ void _markerReDraw();
+
+ enum class GraphActions {
+ ZoomIn,
+ ZoomOut,
+ ScrollLeft,
+ ScrollRight
+ };
+
+ void _updateGraphs(GraphActions action);
+
+ QToolBar _pointerBar, _navigationBar;
+
+ QPushButton _zoomInButton, _quickZoomInButton;
+ QPushButton _zoomOutButton, _quickZoomOutButton;
+
+ QPushButton _scrollLeftButton, _scrollRightButton;
+
+ QLabel _labelP1, _labelP2, // Pointer
+ _labelI1, _labelI2, _labelI3, _labelI4, _labelI5; // Proc. info
+
+ KsGraphScrollArea _scrollArea;
+
+ QWidget _drawWindow, _legendWindow, _legendAxisX;
+
+ QLabel _labelXMin, _labelXMid, _labelXMax;
+
+ KsGLWidget _glWindow;
+
+ QGridLayout _drawLayout;
+
+ QVBoxLayout _layout;
+
+ KsDualMarkerSM *_mState;
+
+ KsDataStore *_data;
+
+ bool _keyPressed;
+};
+
+#endif // _KS_TRACEGRAPH_H