All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yordan Karadzhov <ykaradzhov@vmware.com>
To: "rostedt@goodmis.org" <rostedt@goodmis.org>
Cc: "linux-trace-devel@vger.kernel.org"
	<linux-trace-devel@vger.kernel.org>,
	Yordan Karadzhov <y.karadz@gmail.com>
Subject: [PATCH v2 07/23] kernel-shark-qt: Add Trace Graph widget.
Date: Tue, 16 Oct 2018 15:53:05 +0000	[thread overview]
Message-ID: <20181016155232.5257-8-ykaradzhov@vmware.com> (raw)
In-Reply-To: <20181016155232.5257-1-ykaradzhov@vmware.com>

From: Yordan Karadzhov (VMware) <y.karadz@gmail.com>

This patch defines widget for interactive visualization of trace data
shown as time-series. The provides an info panel and a graph plotting
area (KsGLWidget). The panel and the plotting area are integrated with
the Dual Marker.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/src/CMakeLists.txt   |   2 +
 kernel-shark-qt/src/KsTraceGraph.cpp | 690 +++++++++++++++++++++++++++
 kernel-shark-qt/src/KsTraceGraph.hpp | 137 ++++++
 3 files changed, 829 insertions(+)
 create mode 100644 kernel-shark-qt/src/KsTraceGraph.cpp
 create mode 100644 kernel-shark-qt/src/KsTraceGraph.hpp

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 2ca5187..d406866 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -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
diff --git a/kernel-shark-qt/src/KsTraceGraph.cpp b/kernel-shark-qt/src/KsTraceGraph.cpp
new file mode 100644
index 0000000..21a09d0
--- /dev/null
+++ b/kernel-shark-qt/src/KsTraceGraph.cpp
@@ -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();
+	}
+}
diff --git a/kernel-shark-qt/src/KsTraceGraph.hpp b/kernel-shark-qt/src/KsTraceGraph.hpp
new file mode 100644
index 0000000..395cc1b
--- /dev/null
+++ b/kernel-shark-qt/src/KsTraceGraph.hpp
@@ -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
-- 
2.17.1

  parent reply	other threads:[~2018-10-16 23:44 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-16 15:52 [PATCH v2 00/23] Add Qt-based GUI for KernelShark Yordan Karadzhov
2018-10-16 15:52 ` [PATCH v2 01/23] kernel-shark-qt: Fix a simple bug in KsDataStore::_freeData() Yordan Karadzhov
2018-10-16 15:52 ` [PATCH v2 02/23] kernel-shark-qt: Add Dual Marker for KernelShark GUI Yordan Karadzhov
2018-10-19  2:03   ` Steven Rostedt
2018-10-19  7:41     ` Yordan Karadzhov (VMware)
2018-10-19  2:05   ` Steven Rostedt
2018-10-16 15:52 ` [PATCH v2 03/23] kernel-shark-qt: Add model for showing trace data in a text format Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 04/23] kernel-shark-qt: Add Trace Viewer widget Yordan Karadzhov
2018-10-19  2:20   ` Steven Rostedt
2018-10-19  2:24   ` Steven Rostedt
2018-10-16 15:53 ` [PATCH v2 05/23] kernel-shark-qt: Add visualization (graph) model Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 06/23] kernel-shark-qt: Add widget for OpenGL rendering Yordan Karadzhov
2018-10-19  2:33   ` Steven Rostedt
2018-10-16 15:53 ` Yordan Karadzhov [this message]
2018-10-19  2:38   ` [PATCH v2 07/23] kernel-shark-qt: Add Trace Graph widget Steven Rostedt
2018-10-16 15:53 ` [PATCH v2 08/23] kernel-shark-qt: Add dialog for Advanced filtering Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 09/23] kernel-shark-qt: Add a manager class for GUI sessions Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 10/23] kernel-shark-qt: Add Main Window widget for the KernelShark GUI Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 11/23] kernel-shark-qt: Add KernelShark GUI executable Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 12/23] kernel-shark-qt: Add "File exists" dialog Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 13/23] kernel-shark-qt: Fix the glitches in the preemption time visualization Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 14/23] kernel-shark-qt: Add dialog for of trace data recording Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 15/23] kernel-shark-qt: Add kshark-record executable Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 16/23] kernel-shark-qt: Instruct CMake to search for "pkexec" Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 17/23] kernel-shark-qt: Add PolicyKit Configuration for kshark-record Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 19/23] kernel-shark-qt: Add kernelshark.desktop file Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 20/23] kernel-shark-qt: Add make install Yordan Karadzhov
2018-10-19 15:52   ` Steven Rostedt
2018-10-19 17:13     ` [PATCH v3] " Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 21/23] kernel-shark-qt: Add Record dialog to KS GUI Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 22/23] kernel-shark-qt: Workaround for running as Root on Wayland Yordan Karadzhov
2018-10-16 15:53 ` [PATCH v2 23/23] kernel-shark-qt: Version 0.9.0 Yordan Karadzhov

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181016155232.5257-8-ykaradzhov@vmware.com \
    --to=ykaradzhov@vmware.com \
    --cc=linux-trace-devel@vger.kernel.org \
    --cc=rostedt@goodmis.org \
    --cc=y.karadz@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.