All of lore.kernel.org
 help / color / mirror / Atom feed
From: Steven Rostedt <rostedt@goodmis.org>
To: Yordan Karadzhov <ykaradzhov@vmware.com>
Cc: "linux-trace-devel@vger.kernel.org"
	<linux-trace-devel@vger.kernel.org>,
	Yordan Karadzhov <y.karadz@gmail.com>
Subject: Re: [PATCH v2 06/23] kernel-shark-qt: Add widget for OpenGL rendering
Date: Thu, 18 Oct 2018 22:33:28 -0400	[thread overview]
Message-ID: <20181018223328.5370d692@vmware.local.home> (raw)
In-Reply-To: <20181016155232.5257-7-ykaradzhov@vmware.com>

On Tue, 16 Oct 2018 15:53:03 +0000
Yordan Karadzhov <ykaradzhov@vmware.com> wrote:


> diff --git a/kernel-shark-qt/src/KsGLWidget.cpp b/kernel-shark-qt/src/KsGLWidget.cpp
> new file mode 100644
> index 0000000..22cbd96
> --- /dev/null
> +++ b/kernel-shark-qt/src/KsGLWidget.cpp
> @@ -0,0 +1,913 @@
> +// SPDX-License-Identifier: LGPL-2.1
> +
> +/*
> + * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <ykaradzhov@vmware.com>
> + */
> +
> + /**
> + *  @file    KsGLWidget.cpp
> + *  @brief   OpenGL widget for plotting trace graphs.
> + */
> +
> +// OpenGL
> +#include <GL/glut.h>
> +#include <GL/gl.h>
> +
> +// KernelShark
> +#include "KsGLWidget.hpp"
> +#include "KsUtils.hpp"
> +#include "KsPlugins.hpp"
> +#include "KsDualMarker.hpp"
> +
> +/** Create a default (empty) OpenGL widget. */
> +KsGLWidget::KsGLWidget(QWidget *parent)
> +: QOpenGLWidget(parent),
> +  _hMargin(20),
> +  _vMargin(30),
> +  _vSpacing(20),
> +  _mState(nullptr),
> +  _data(nullptr),
> +  _rubberBand(QRubberBand::Rectangle, this),
> +  _rubberBandOrigin(0, 0),
> +  _dpr(1)
> +{
> +	setMouseTracking(true);
> +
> +	/*
> +	 * Using the old Signal-Slot syntax because QWidget::update has
> +	 * overloads.
> +	 */
> +	connect(&_model, SIGNAL(modelReset()), this, SLOT(update()));
> +}
> +
> +/** Reimplemented function used to set up all required OpenGL resources. */
> +void KsGLWidget::initializeGL()
> +{
> +	_dpr = QApplication::desktop()->devicePixelRatio();
> +	ksplot_init_opengl(_dpr);
> +}
> +
> +/**
> + * Reimplemented function used to reprocess all graphs whene the widget has
> + * been resized.
> + */
> +void KsGLWidget::resizeGL(int w, int h)
> +{
> +	ksplot_resize_opengl(w, h);
> +	if(!_data)
> +		return;
> +
> +	/*
> +	 * From the size of the widget, calculate the number of bins.
> +	 * One bin will correspond to one pixel.
> +	 */
> +	int nBins = width() - _hMargin * 2;
> +
> +	/*
> +	 * Reload the data. The range of the histogram is the same
> +	 * but the number of bins changes.
> +	 */
> +	ksmodel_set_bining(_model.histo(),
> +			   nBins,
> +			   _model.histo()->min,
> +			   _model.histo()->max);
> +
> +	_model.fill(_data->rows(), _data->size());
> +}
> +
> +/** Reimplemented function used to plot trace graphs. */
> +void KsGLWidget::paintGL()
> +{
> +	glClear(GL_COLOR_BUFFER_BIT);
> +
> +	loadColors();
> +
> +	/* Draw the time axis. */
> +	if(_data)
> +		_drawAxisX();
> +
> +	/* Process and draw all graphs by using the built-in logic. */
> +	_makeGraphs(_cpuList, _taskList);
> +	for (auto const &g: _graphs)
> +		g->draw(1.5 * _dpr);

Again, let's get rid of the 1.5 and replace it with a meaningful macro
or variable (that perhaps can be changed later?

> +
> +	/* Process and draw all plugin-specific shapes. */
> +	_makePluginShapes(_cpuList, _taskList);
> +	while (!_shapes.empty()) {
> +		auto s = _shapes.front();
> +		s->draw();
> +		delete s;
> +		_shapes.pop_front();
> +	}
> +
> +	/*
> +	 * Update and draw the markers. Make sure that the active marker
> +	 * is drawn on top.
> +	 */
> +	_mState->updateMarkers(*_data, this);
> +	_mState->passiveMarker().draw();
> +	_mState->activeMarker().draw();
> +}
> +
> +/** Reimplemented event handler used to receive mouse press events. */
> +void KsGLWidget::mousePressEvent(QMouseEvent *event)
> +{
> +	if (event->button() == Qt::LeftButton) {
> +		_posMousePress = _posInRange(event->pos().x());
> +		_rangeBoundInit(_posMousePress);
> +	} else if (event->button() == Qt::RightButton) {
> +		emit deselect();
> +		_mState->activeMarker().remove();
> +		_mState->updateLabels();
> +		_model.update();
> +	}
> +}
> +
> +int KsGLWidget::_getLastTask(struct kshark_trace_histo *histo,
> +			     int bin, int cpu)
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	kshark_entry_collection *col;
> +	int pid;
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return KS_EMPTY_BIN;
> +
> +	col = kshark_find_data_collection(kshark_ctx->collections,
> +					  KsUtils::matchCPUVisible,
> +					  cpu);
> +
> +	for (int b = bin; b >= 0; --b) {
> +		pid = ksmodel_get_pid_back(histo, b, cpu, false, col, nullptr);
> +		if (pid >= 0)
> +			return pid;
> +	}
> +
> +	return ksmodel_get_pid_back(histo, LOWER_OVERFLOW_BIN,
> +					   cpu,
> +					   false,
> +					   col,
> +					   nullptr);
> +}
> +
> +int KsGLWidget::_getLastCPU(struct kshark_trace_histo *histo,
> +			     int bin, int pid)
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	kshark_entry_collection *col;
> +	int cpu;
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return KS_EMPTY_BIN;
> +
> +	col = kshark_find_data_collection(kshark_ctx->collections,
> +					  kshark_match_pid,
> +					  pid);
> +
> +	for (int b = bin; b >= 0; --b) {
> +		cpu = ksmodel_get_cpu_back(histo, b, pid, false, col, nullptr);
> +		if (cpu >= 0)
> +			return cpu;
> +	}
> +
> +	return ksmodel_get_cpu_back(histo, LOWER_OVERFLOW_BIN,
> +					   pid,
> +					   false,
> +					   col,
> +					   nullptr);
> +
> +}
> +
> +/** Reimplemented event handler used to receive mouse move events. */
> +void KsGLWidget::mouseMoveEvent(QMouseEvent *event)
> +{
> +	int bin, cpu, pid;
> +	size_t row;
> +	bool ret;
> +
> +	if (_rubberBand.isVisible())
> +		_rangeBoundStretched(_posInRange(event->pos().x()));
> +
> +	bin = event->pos().x() - _hMargin;
> +	cpu = _getCPU(event->pos().y());
> +	pid = _getPid(event->pos().y());
> +
> +	ret = _find(bin, cpu, pid, 5, false, &row);

Why 5?

> +	if (ret) {
> +		emit found(row);
> +	} else {
> +		if (cpu >= 0) {
> +			pid = _getLastTask(_model.histo(), bin, cpu);
> +		}
> +
> +		if (pid > 0) {
> +			cpu = _getLastCPU(_model.histo(), bin, pid);
> +		}
> +
> +		emit notFound(ksmodel_bin_ts(_model.histo(), bin), cpu, pid);
> +	}
> +}
> +
> +/** Reimplemented event handler used to receive mouse release events. */
> +void KsGLWidget::mouseReleaseEvent(QMouseEvent *event)
> +{
> +	if (event->button() == Qt::LeftButton) {
> +		size_t posMouseRel = _posInRange(event->pos().x());
> +		int min, max;
> +		if (_posMousePress < posMouseRel) {
> +			min = _posMousePress - _hMargin;
> +			max = posMouseRel - _hMargin;
> +		} else {
> +			max = _posMousePress - _hMargin;
> +			min = posMouseRel - _hMargin;
> +		}
> +
> +		_rangeChanged(min, max);
> +	}
> +}
> +
> +/** Reimplemented event handler used to receive mouse double click events. */
> +void KsGLWidget::mouseDoubleClickEvent(QMouseEvent *event)
> +{
> +	if (event->button() == Qt::LeftButton)
> +		_findAndSelect(event);
> +}
> +
> +/** Reimplemented event handler used to receive mouse wheel events. */
> +void KsGLWidget::wheelEvent(QWheelEvent * event)
> +{
> +	int zoomFocus;
> +
> +	if (_mState->activeMarker()._isSet &&
> +	    _mState->activeMarker().isVisible()) {
> +		/*
> +		 * Use the position of the marker as a focus point for the
> +		 * zoom.
> +		 */
> +		zoomFocus = _mState->activeMarker()._bin;
> +	} else {
> +		/*
> +		 * Use the position of the mouse as a focus point for the
> +		 * zoom.
> +		 */
> +		zoomFocus = event->pos().x() - _hMargin;
> +	}
> +
> +	if (event->delta() > 0) {
> +		_model.zoomIn(.05, zoomFocus);

Same for the .05s

> +	} else {
> +		_model.zoomOut(.05, zoomFocus);
> +	}
> +
> +	_mState->updateMarkers(*_data, this);
> +}
> +
> +/** Reimplemented event handler used to receive key press events. */
> +void KsGLWidget::keyPressEvent(QKeyEvent *event)
> +{
> +	if (event->isAutoRepeat())
> +		return;
> +
> +	switch (event->key()) {
> +	case Qt::Key_Plus:
> +		emit zoomIn();
> +		return;
> +
> +	case Qt::Key_Minus:
> +		emit zoomOut();
> +		return;
> +
> +	case Qt::Key_Left:
> +		emit scrollLeft();
> +		return;
> +
> +	case Qt::Key_Right:
> +		emit scrollRight();
> +		return;
> +
> +	default:
> +		QOpenGLWidget::keyPressEvent(event);
> +		return;
> +	}
> +}
> +
> +/** Reimplemented event handler used to receive key release events. */
> +void KsGLWidget::keyReleaseEvent(QKeyEvent *event)
> +{
> +	if (event->isAutoRepeat())
> +		return;
> +
> +	if(event->key() == Qt::Key_Plus ||
> +	   event->key() == Qt::Key_Minus ||
> +	   event->key() == Qt::Key_Left ||
> +	   event->key() == Qt::Key_Right) {
> +		emit stopUpdating();
> +		return;
> +	}
> +
> +	QOpenGLWidget::keyPressEvent(event);
> +	return;
> +}
> +
> +/**
> + * @brief Load and show trace data.
> + *
> + * @param data: Input location for the KsDataStore object.
> + *	  KsDataStore::loadDataFile() must be called first.
> + */
> +void KsGLWidget::loadData(KsDataStore *data)
> +{
> +	uint64_t tMin, tMax;
> +	int nCPUs, nBins;
> +
> +	_data = data;
> +
> +	/*
> +	 * From the size of the widget, calculate the number of bins.
> +	 * One bin will correspond to one pixel.
> +	 */
> +	nBins = width() - _hMargin * 2;
> +	nCPUs = tep_get_cpus(_data->tep());
> +
> +	_model.reset();
> +
> +	/* Now load the entire set of trace data. */
> +	tMin = _data->rows()[0]->ts;
> +	tMax = _data->rows()[_data->size() - 1]->ts;
> +	ksmodel_set_bining(_model.histo(), nBins, tMin, tMax);
> +	_model.fill(_data->rows(), _data->size());
> +
> +	/* Make a default CPU list. All CPUs will be plotted. */
> +	_cpuList = {};
> +	for (int i = 0; i < nCPUs; ++i)
> +		_cpuList.append(i);
> +
> +	/* Make a default task list. No tasks will be plotted. */
> +	_taskList = {};
> +
> +	loadColors();
> +	_makeGraphs(_cpuList, _taskList);
> +}
> +
> +/**
> + * Create a Hash table of Rainbow colors. The sorted Pid values are mapped to
> + * the palette of Rainbow colors.
> + */
> +void KsGLWidget::loadColors()
> +{
> +	_pidColors.clear();
> +	_pidColors = KsPlot::getColorTable();
> +}
> +
> +/**
> + * Position the graphical elements of the marker according to the current
> + * position of the graphs inside the GL widget.
> + */
> +void KsGLWidget::setMark(KsGraphMark *mark)
> +{
> +	mark->_mark.setDPR(_dpr);
> +	mark->_mark.setX(mark->_bin + _hMargin);
> +	mark->_mark.setY(_vMargin / 2 + 2, height() - _vMargin);
> +
> +	if (mark->_cpu >= 0) {
> +		mark->_mark.setCPUY(_graphs[mark->_cpu]->getBase());
> +		mark->_mark.setCPUVisible(true);
> +	} else {
> +		mark->_mark.setCPUVisible(false);
> +	}
> +
> +	if (mark->_task >= 0) {
> +		mark->_mark.setTaskY(_graphs[mark->_task]->getBase());
> +		mark->_mark.setTaskVisible(true);
> +	} else {
> +		mark->_mark.setTaskVisible(false);
> +	}
> +}
> +
> +/**
> + * @brief Check if a given KernelShark entry is ploted.
> + *
> + * @param e: Input location for the KernelShark entry.
> + * @param graphCPU: Output location for index of the CPU graph to which this
> + *		    entry belongs. If such a graph does not exist the outputted
> + *		    value is "-1".
> + * @param graphTask: Output location for index of the Task graph to which this
> + *		     entry belongs. If such a graph does not exist the
> + *		     outputted value is "-1".
> + */
> +void KsGLWidget::findGraphIds(const kshark_entry &e,
> +			      int *graphCPU,
> +			      int *graphTask)
> +{
> +	int graph(0);
> +	bool cpuFound(false), taskFound(false);
> +
> +	/*
> +	 * Loop over all CPU graphs and try to find the one that
> +	 * contains the entry.
> +	 */
> +	for (auto const &c: _cpuList) {
> +		if (c == e.cpu) {
> +			cpuFound = true;
> +			break;
> +		}
> +		++graph;
> +	}
> +
> +	if (cpuFound)
> +		*graphCPU = graph;
> +	else
> +		*graphCPU = -1;
> +
> +	/*
> +	 * Loop over all Task graphs and try to find the one that
> +	 * contains the entry.
> +	 */
> +	graph = _cpuList.count();
> +	for (auto const &p: _taskList) {
> +		if (p == e.pid) {
> +			taskFound = true;
> +			break;
> +		}
> +		++graph;
> +	}
> +
> +	if (taskFound)
> +		*graphTask = graph;
> +	else
> +		*graphTask = -1;
> +}
> +
> +void KsGLWidget::_drawAxisX()
> +{
> +	KsPlot::Point a0(_hMargin, _vMargin / 4), a1(_hMargin, _vMargin / 2);
> +	KsPlot::Point b0(width()/2, _vMargin / 4), b1(width() / 2, _vMargin / 2);
> +	KsPlot::Point c0(width() - _hMargin, _vMargin / 4),
> +			 c1(width() - _hMargin, _vMargin / 2);
> +	int lineSize = 2 * _dpr;
> +
> +	a0._size = c0._size = _dpr;
> +
> +	a0.draw();
> +	c0.draw();
> +	KsPlot::drawLine(a0, a1, {}, lineSize);
> +	KsPlot::drawLine(b0, b1, {}, lineSize);
> +	KsPlot::drawLine(c0, c1, {}, lineSize);
> +	KsPlot::drawLine(a0, c0, {}, lineSize);
> +}
> +
> +void KsGLWidget::_makeGraphs(QVector<int> cpuList, QVector<int> taskList)
> +{
> +	/* The very first thing to do is to clean up. */
> +	for (auto &g: _graphs)
> +		delete g;
> +	_graphs.resize(0);
> +
> +	if (!_data || !_data->size())
> +		return;
> +
> +	auto lamAddGraph = [&](KsPlot::Graph *graph) {
> +		/*
> +		* Calculate the base level of the CPU graph inside the widget.
> +		* Remember that the "Y" coordinate is inverted.
> +		*/
> +		if (!graph)
> +			return;
> +
> +		int base = _vMargin +
> +			   _vSpacing * _graphs.count() +
> +			   KS_GRAPH_HEIGHT * (_graphs.count() + 1);
> +
> +		graph->setBase(base);
> +		_graphs.append(graph);
> +	};
> +
> +	/* Create CPU graphs according to the cpuList. */
> +	for (auto const &cpu: cpuList)
> +		lamAddGraph(_newCPUGraph(cpu));
> +
> +	/* Create Task graphs taskList to the taskList. */
> +	for (auto const &pid: taskList)
> +		lamAddGraph(_newTaskGraph(pid));
> +}
> +
> +void KsGLWidget::_makePluginShapes(QVector<int> cpuList, QVector<int> taskList)
> +{
> +	kshark_context *kshark_ctx(nullptr);
> +	kshark_event_handler *evt_handlers;
> +	KsCppArgV cppArgv;
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return;
> +
> +	cppArgv._histo = _model.histo();
> +	cppArgv._shapes = &_shapes;
> +
> +	for (int g = 0; g < cpuList.count(); ++g) {
> +		cppArgv._graph = _graphs[g];
> +		evt_handlers = kshark_ctx->event_handlers;
> +		while (evt_handlers) {
> +			evt_handlers->draw_func(cppArgv.toC(),
> +						cpuList[g],
> +						KSHARK_PLUGIN_CPU_DRAW);
> +
> +			evt_handlers = evt_handlers->next;
> +		}
> +	}
> +
> +	for (int g = 0; g < taskList.count(); ++g) {
> +		cppArgv._graph = _graphs[cpuList.count() + g];
> +		evt_handlers = kshark_ctx->event_handlers;
> +		while (evt_handlers) {
> +			evt_handlers->draw_func(cppArgv.toC(),
> +						taskList[g],
> +						KSHARK_PLUGIN_TASK_DRAW);
> +
> +			evt_handlers = evt_handlers->next;
> +		}
> +	}
> +}
> +
> +KsPlot::Graph *KsGLWidget::_newCPUGraph(int cpu)
> +{
> +	KsPlot::Graph *graph = new KsPlot::Graph(_model.histo(),
> +						 &_pidColors);
> +	kshark_context *kshark_ctx(nullptr);
> +	kshark_entry_collection *col;
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return nullptr;
> +
> +	graph->setHMargin(_hMargin);
> +	graph->setHeight(KS_GRAPH_HEIGHT);
> +
> +	col = kshark_find_data_collection(kshark_ctx->collections,
> +					  KsUtils::matchCPUVisible,
> +					  cpu);
> +
> +	graph->setDataCollectionPtr(col);
> +	graph->fillCPUGraph(cpu);
> +
> +	return graph;
> +}
> +
> +KsPlot::Graph *KsGLWidget::_newTaskGraph(int pid)
> +{
> +	KsPlot::Graph *graph = new KsPlot::Graph(_model.histo(),
> +						 &_pidColors);
> +	kshark_context *kshark_ctx(nullptr);
> +	kshark_entry_collection *col;
> +
> +	if (!kshark_instance(&kshark_ctx))
> +		return nullptr;
> +
> +	graph->setHMargin(_hMargin);
> +	graph->setHeight(KS_GRAPH_HEIGHT);
> +
> +	col = kshark_find_data_collection(kshark_ctx->collections,
> +					  kshark_match_pid, pid);
> +	if (!col) {
> +		/*
> +		 * If a data collection for this task does not exist,
> +		 * register a new one.
> +		 */
> +		col = kshark_register_data_collection(kshark_ctx,
> +						      _data->rows(),
> +						      _data->size(),
> +						      kshark_match_pid, pid,
> +						      25);

25?

> +	}
> +
> +	/*
> +	 * Data collections are efficient only when used on graphs, having a
> +	 * lot of empty bins.
> +	 * TODO: Determine the optimal criteria to decide whether to use or
> +	 * not use data collection for this graph.
> +	 */
> +	if (_data->size() < 1e6 &&
> +	    col && col->size &&
> +	    _data->size() / col->size < 100) {

Perhaps make the 1e6 and 100 in macros or variables.

-- Steve

> +		/*
> +		 * No need to use collection in this case. Free the collection
> +		 * data, but keep the collection registered. This will prevent
> +		 * from recalculating the same collection next time when this
> +		 * task is ploted.
> +		 */
> +		kshark_reset_data_collection(col);
> +	}
> +
> +	graph->setDataCollectionPtr(col);
> +	graph->fillTaskGraph(pid);
> +
> +	return graph;
> +}
> +
> +

  reply	other threads:[~2018-10-19 10:37 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 [this message]
2018-10-16 15:53 ` [PATCH v2 07/23] kernel-shark-qt: Add Trace Graph widget Yordan Karadzhov
2018-10-19  2:38   ` 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=20181018223328.5370d692@vmware.local.home \
    --to=rostedt@goodmis.org \
    --cc=linux-trace-devel@vger.kernel.org \
    --cc=y.karadz@gmail.com \
    --cc=ykaradzhov@vmware.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.