diff mbox series

[v2,3/4] kernel-shark-qt: Add C++ API for drawing of Graphs

Message ID 20180824142116.21339-4-y.karadz@gmail.com (mailing list archive)
State Superseded
Headers show
Series Add drawing instrumentation to be used by the Qt-based KernelShark | expand

Commit Message

Yordan Karadzhov Aug. 24, 2018, 2:21 p.m. UTC
This patch extends the KernelShark Plotting library, by adding
a C++ API for drawing CPU and Task Graphs.

Signed-off-by: Yordan Karadzhov (VMware) <y.karadz@gmail.com>
---
 kernel-shark-qt/src/CMakeLists.txt  |    3 +-
 kernel-shark-qt/src/KsPlotTools.cpp | 1127 +++++++++++++++++++++++++++
 kernel-shark-qt/src/KsPlotTools.hpp |  465 +++++++++++
 3 files changed, 1594 insertions(+), 1 deletion(-)
 create mode 100644 kernel-shark-qt/src/KsPlotTools.cpp
 create mode 100644 kernel-shark-qt/src/KsPlotTools.hpp

Comments

Steven Rostedt Aug. 24, 2018, 2:50 p.m. UTC | #1
On Fri, 24 Aug 2018 17:21:15 +0300
"Yordan Karadzhov (VMware)" <y.karadz@gmail.com> wrote:

> +	/*
> +	 * Check the content of the very firs bin and see if the CPU is

I haven't really looked too much at this patch yet, but this spelling
stood out. Seems your 't' key is not working well when typing "first
bin". There's several places that have that ;-)

-- Steve

> +	 * active.
> +	 */
> +	bin = 0;
> +	get_pid(bin);
> +	if (pidFront >= 0) {
> +		/*
> +		 * The CPU is active and this is a regular process.
> +		 * Set this bin.
> +		 */
> +		set_bin(bin);
> +	} else {
> +		/*
> +		 * No data from this CPU in the very firs bin. Use the Lower
> +		 * Overflow Bin to retrieve the Process Id (if any). First
> +		 * get the Pid back, ignoring the filters.
> +		 */
Yordan Karadzhov Aug. 24, 2018, 8:27 p.m. UTC | #2
On 24.08.2018 17:50, Steven Rostedt wrote:
>> +	/*
>> +	 * Check the content of the very firs bin and see if the CPU is
> I haven't really looked too much at this patch yet, but this spelling
> stood out. Seems your 't' key is not working well when typing "first
> bin". There's several places that have that;-)
> 

I am sorry for this annoying typos. This is happening because my editor 
is trying to auto-complete the words when I am typing. And it does this 
based on what I already wrote in the file. So if I misspell a word once 
next time the editor will suggest the same typo.

Thanks!
Yordan

> -- Steve
>
diff mbox series

Patch

diff --git a/kernel-shark-qt/src/CMakeLists.txt b/kernel-shark-qt/src/CMakeLists.txt
index 9c74bc0..ac2847a 100644
--- a/kernel-shark-qt/src/CMakeLists.txt
+++ b/kernel-shark-qt/src/CMakeLists.txt
@@ -16,7 +16,8 @@  set_target_properties(kshark  PROPERTIES SUFFIX	".so.${KS_VERSION_STRING}")
 if (OPENGL_FOUND AND GLUT_FOUND)
 
     message(STATUS "libkshark-plot")
-    add_library(kshark-plot  SHARED  libkshark-plot.c)
+    add_library(kshark-plot  SHARED  libkshark-plot.c
+                                     KsPlotTools.cpp)
 
     target_link_libraries(kshark-plot  kshark
                                        ${OPENGL_LIBRARIES}
diff --git a/kernel-shark-qt/src/KsPlotTools.cpp b/kernel-shark-qt/src/KsPlotTools.cpp
new file mode 100644
index 0000000..0ae7582
--- /dev/null
+++ b/kernel-shark-qt/src/KsPlotTools.cpp
@@ -0,0 +1,1127 @@ 
+// SPDX-License-Identifier: LGPL-2.1
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+/**
+ *  @file    KsPlotTools.cpp
+ *  @brief   KernelShark Plot tools.
+ */
+
+// C
+#include <math.h>
+
+// C++
+#include <algorithm>
+#include <vector>
+
+// OpenGL
+#include <GL/freeglut.h>
+#include <GL/gl.h>
+
+// KernelShark
+#include "KsPlotTools.hpp"
+
+namespace KsPlot
+{
+
+float Color::_frequency = .75;
+
+/**
+ * @brief Create a default color (black).
+ */
+Color::Color()
+{
+	_col_c.red = _col_c.green = _col_c.blue = 0;
+}
+
+/**
+ * @brief Constructs a RGB color object.
+ *
+ * @param r: The red component of the color.
+ * @param g: The green component of the color.
+ * @param b: The blue component of the color
+ */
+Color::Color(uint8_t r, uint8_t g, uint8_t b)
+{
+	set(r, g, b);
+}
+
+/**
+ * @brief Constructs a RGB color object.
+ *
+ * @param rgb: RGB value.
+ */
+Color::Color(int rgb)
+{
+	set(rgb);
+}
+
+/**
+ * @brief Sets the color.
+ *
+ * @param r: The red component of the color.
+ * @param g: The green component of the color.
+ * @param b: The blue component of the color
+ */
+void Color::set(uint8_t r, uint8_t g, uint8_t b)
+{
+	_col_c.red = r;
+	_col_c.green = g;
+	_col_c.blue = b;
+}
+
+/**
+ * @brief Sets the color.
+ *
+ * @param rgb: RGB value.
+ */
+void Color::set(int rgb)
+{
+	int r = rgb & 0xFF;
+	int g = (rgb >> 8) & 0xFF;
+	int b = (rgb >> 16) & 0xFF;
+
+	set(r, g, b);
+}
+
+/**
+ * @brief The color is selected from the Rainbow palette.
+ *
+ * @param n: index of the color inside the Rainbow palette.
+ */
+void Color::setRainbowColor(int n)
+{
+	int r = sin(_frequency * n + 0) * 127 + 128;
+	int g = sin(_frequency * n + 2) * 127 + 128;
+	int b = sin(_frequency * n + 4) * 127 + 128;
+
+	set(r, g, b);
+}
+
+/**
+ * @brief Create a Hash table of Rainbow colors. The sorted Pid values are
+ *	  mapped to the palette of Rainbow colors.
+ *
+ * @returns ColorTable instance.
+ */
+ColorTable getColorTable()
+{
+	struct kshark_context *kshark_ctx(nullptr);
+	ColorTable colors;
+	int nTasks, pid, *pids;
+
+	if (!kshark_instance(&kshark_ctx))
+		return colors;
+
+	nTasks = kshark_get_task_pids(kshark_ctx, &pids);
+	if (!nTasks)
+		return colors;
+
+	std::vector<int> temp_pids(pids, pids + nTasks);
+	std::sort(temp_pids.begin(), temp_pids.end());
+
+	/* The "Idle" process (pid = 0) will be plotted in black. */
+	colors[0] = {};
+
+	for (int i = 1; i < nTasks; ++i) {
+		pid = temp_pids[i];
+		colors[pid].setRainbowColor(i - 1);
+	}
+
+	return colors;
+}
+
+/**
+ * @brief Search the Hash table of Rainbow colors for a particular key (pid).
+ *
+ * @param colors: Input location for the ColorTable instance.
+ * @param pid: the Process Id to search for.
+ *
+ * @returns The Rainbow color of the key "pid". If "pid" does not exist, the
+ *	    returned color is Black.
+ */
+Color getPidColor(ColorTable *colors, int pid)
+{
+	auto item = colors->find(pid);
+
+	if (item != colors->end())
+		return item->second;
+
+	return {};
+}
+
+/**
+ * @brief Create a default Shape.
+ */
+Shape::Shape()
+: _nPoints(0),
+  _points(nullptr)
+{}
+
+/**
+ * @brief Create a Shape defined by "n" points.
+ */
+Shape::Shape(int n)
+: _nPoints(n),
+  _points(new(std::nothrow) ksplot_point[n]())
+{
+	if (!_points) {
+		_size = 0;
+		fprintf(stderr,
+			"Failed to allocate memory for ksplot_points.\n");
+	}
+}
+
+/** Copy constructor. */
+Shape::Shape(const Shape &s)
+: _nPoints(0),
+  _points(nullptr)
+{
+	*this = s;
+}
+
+/** Move constructor. */
+Shape::Shape(Shape &&s)
+: _nPoints(s._nPoints),
+  _points(s._points)
+{
+	s._nPoints = 0;
+	s._points = nullptr;
+}
+
+/**
+* @brief Destroy the Shape object.
+*/
+Shape::~Shape() {
+	delete[] _points;
+}
+
+/** Assignment operator. */
+void Shape::operator=(const Shape &s)
+{
+	PlotObject::operator=(s);
+
+	if (s._nPoints != _nPoints) {
+		delete[] _points;
+		_points = new(std::nothrow) ksplot_point[s._nPoints];
+	}
+
+	if (_points) {
+		_nPoints = s._nPoints;
+		memcpy(_points, s._points,
+		       sizeof(_points) * _nPoints);
+	} else {
+		_nPoints = 0;
+		fprintf(stderr,
+			"Failed to allocate memory for ksplot_points.\n");
+	}
+}
+
+/**
+ * @brief Set the point of the polygon indexed by "i".
+ *
+ * @param i: the index of the point to be set.
+ * @param x: X coordinate of the point in pixels.
+ * @param y: Y coordinate of the point in pixels.
+ */
+void Shape::setPoint(size_t i, int x, int y)
+{
+	if (i < _nPoints) {
+		_points[i].x = x;
+		_points[i].y = y;
+	}
+}
+
+/**
+ * @brief Set the point of the polygon indexed by "i".
+ *
+ * @param i: the index of the point to be set.
+ * @param p: A ksplot_point object used to provide coordinate values.
+ */
+void Shape::setPoint(size_t i, const ksplot_point &p)
+{
+	setPoint(i, p.x, p.y);
+}
+
+/**
+ * @brief Set the point of the polygon indexed by "i".
+ *
+ * @param i: the index of the point to be set.
+ * @param p: A Point object used to provide coordinate values.
+ */
+void Shape::setPoint(size_t i, const Point &p)
+{
+	setPoint(i, p.x(), p.y());
+}
+
+/**
+ * @brief Get the point "i". If the point does not exist, the function returns
+ *	  nullptr.
+ */
+const ksplot_point *Shape::getPoint(size_t i) const
+{
+	if (i < _nPoints)
+		return &_points[i];
+
+	return nullptr;
+}
+
+/**
+ * @brief Set the horizontal coordinate of the point "i".
+ *
+ * @param i: the index of the point to be set.
+ * @param x: X coordinate of the point in pixels.
+ */
+void Shape::setPointX(size_t i, int x) {
+	if (i < _nPoints)
+		_points[i].x = x;
+}
+
+/**
+ * @brief Set the vertical coordinate of the point "i".
+ *
+ * @param i: the index of the point to be set.
+ * @param y: Y coordinate of the point in pixels.
+ */
+void Shape::setPointY(size_t i, int y) {
+	if (i < _nPoints)
+		_points[i].y = y;
+}
+
+/**
+ * @brief Get the horizontal coordinate of the point "i". If the point does
+ * 	  not exist, the function returns 0.
+ */
+int Shape::getPointX(size_t i) const {
+	if (i < _nPoints)
+		return _points[i].x;
+
+	return 0;
+}
+
+/**
+ * @brief Get the vertical coordinate of the point "i". If the point does
+ * 	  not exist, the function returns 0.
+ */
+int Shape::getPointY(size_t i) const {
+	if (i < _nPoints)
+		return _points[i].y;
+
+	return 0;
+}
+
+/**
+ * @brief Create a default Point.
+ */
+Point::Point()
+: Shape(1)
+{}
+
+/**
+ * @brief Create a point.
+ *
+ * @param x: X coordinate of the point in pixels.
+ * @param y: Y coordinate of the point in pixels.
+ */
+Point::Point(int x, int y)
+: Shape(1)
+{
+	setPoint(0, x, y);
+}
+
+void Point::_draw(const Color &col, float size) const
+{
+	if (_nPoints == 1)
+		ksplot_draw_point(_points, col.color_c_ptr(), size);
+}
+
+/**
+ * @brief Draw a line between point "a" and point "b".
+ *
+ * @param a: The first finishing point of the line.
+ * @param b: The second finishing point of the line.
+ * @param col: The color of the line.
+ * @param size: The size of the line.
+ */
+void drawLine(const Point &a, const Point &b,
+	      const Color &col, float size)
+{
+	ksplot_draw_line(a.point_c_ptr(),
+			 b.point_c_ptr(),
+			 col.color_c_ptr(),
+			 size);
+}
+
+/**
+ * @brief Create a default line.
+ */
+Line::Line()
+: Shape(2)
+{}
+
+/**
+ * @brief Create a line between the point "a" and point "b".
+ *
+ * @param a: first finishing point of the line.
+ * @param b: second finishing point of the line.
+ */
+Line::Line(const Point &a, const Point &b)
+: Shape(2)
+{
+	setPoint(0, a.x(), a.y());
+	setPoint(1, b.x(), b.y());
+}
+
+void Line::_draw(const Color &col, float size) const
+{
+	if (_nPoints == 2)
+		ksplot_draw_line(&_points[0], &_points[1],
+				 col.color_c_ptr(), size);
+}
+
+/**
+ * @brief Create a default line. The polygon object has the ownership of the
+ * 	  array of point used to define its shape.
+ *
+ * @param n: Number of edges in the polyhedron.
+ */
+Polygon::Polygon(size_t n)
+: Shape(n),
+  _fill(true)
+{}
+
+void Polygon::_draw(const Color &col, float size) const
+{
+	if (_fill)
+		ksplot_draw_polygon(_points, _nPoints,
+				    col.color_c_ptr(),
+				    size);
+	else
+		ksplot_draw_polygon_contour(_points, _nPoints,
+					    col.color_c_ptr(),
+					    size);
+}
+
+/**
+ * @brief Create a default Mark.
+ */
+Mark::Mark()
+{
+	_visible = false;
+	_cpu._color = Color(225, 255, 100);
+	_cpu._size = 5.5f;
+	_task._color = Color(0, 255, 0);
+	_task._size = 5.5f;
+}
+
+void Mark::_draw(const Color &col, float size) const
+{
+	drawLine(_a, _b, col, size);
+	_cpu.draw();
+	_task.draw();
+}
+
+/**
+ * @brief Set the device pixel ratio.
+ *
+ * @param dpr: device pixel ratio value.
+ */
+void Mark::setDPR(int dpr)
+{
+	_size = 1.5 * dpr;
+	_task._size = _cpu._size = 1.5 + 4.0 * dpr;
+}
+
+/**
+ * @brief Set the X coordinate (horizontal) of the Mark.
+ *
+ * @param x: X coordinate of the Makr in pixels.
+ */
+void Mark::setX(int x)
+{
+	_a.setX(x);
+	_b.setX(x);
+	_cpu.setX(x);
+	_task.setX(x);
+}
+
+/**
+ * @brief Set the Y coordinates (vertical) of the Mark's finishing points.
+ *
+ * @param yA: Y coordinate of the first finishing point of the Mark's line.
+ * @param yB: Y coordinate of the second finishing point of the Mark's line.
+ */
+void Mark::setY(int yA, int yB)
+{
+	_a.setY(yA);
+	_b.setY(yB);
+}
+
+/**
+ * @brief Set the Y coordinates (vertical) of the Mark's CPU points.
+ *
+ * @param yCPU: Y coordinate of the Mark's CPU point.
+ */
+void Mark::setCPUY(int yCPU)
+{
+	_cpu.setY(yCPU);
+}
+
+/**
+ * @brief Set the visiblity of the Mark's CPU points.
+ *
+ * @param v: If True, the CPU point will be visible.
+ */
+void Mark::setCPUVisible(bool v)
+{
+	_cpu._visible = v;
+}
+
+/**
+ * @brief Set the Y coordinates (vertical) of the Mark's Task points.
+ *
+ * @param yTask: Y coordinate of the Mark's Task point.
+ */
+void Mark::setTaskY(int yTask)
+{
+	_task.setY(yTask);
+}
+
+/**
+ * @brief Set the visiblity of the Mark's Task points.
+ *
+ * @param v: If True, the Task point will be visible.
+ */
+void Mark::setTaskVisible(bool v)
+{
+	_task._visible = v;
+}
+
+/**
+ * @brief Create a default Bin.
+ */
+Bin::Bin()
+: _pidFront(KS_EMPTY_BIN),
+  _pidBack(KS_EMPTY_BIN)
+{}
+
+void Bin::_draw(const Color &col, float size) const
+{
+	drawLine(_base, _val, col, size);
+}
+
+/**
+ * @brief Draw only the "val" Point og the Bin.
+ *
+ * @param size: The size of the point.
+ */
+void Bin::drawVal(float size)
+{
+	_val._size = size;
+	_val.draw();
+}
+
+/**
+ * @brief Create a default (empty) Graph.
+ */
+Graph::Graph()
+: _histoPtr(nullptr),
+  _bins(nullptr),
+  _size(0),
+  _hMargin(30),
+  _collectionPtr(nullptr),
+  _pidColors(nullptr)
+{}
+
+/**
+ * @brief Create a Graph to represent the state of the Vis. model.
+ *
+ * @param histo: Input location for the model descriptor.
+ * @param ct: Input location for the Hash table of Task's colors.
+ */
+Graph::Graph(kshark_trace_histo *histo, KsPlot::ColorTable *ct)
+: _histoPtr(histo),
+  _bins(new(std::nothrow) Bin[histo->n_bins]),
+  _size(histo->n_bins),
+  _hMargin(30),
+  _collectionPtr(nullptr),
+  _pidColors(ct)
+{
+	if (!_bins) {
+		_size = 0;
+		fprintf(stderr, "Failed to allocate memory graph's bins.\n");
+	}
+
+	_initBins();
+}
+
+/**
+ * @brief Destroy the Graph object.
+ */
+Graph::~Graph()
+{
+	delete[] _bins;
+}
+
+void Graph::_initBins()
+{
+	for (int i = 0; i < _size; ++i) {
+		_bins[i]._base.setX(i + _hMargin);
+		_bins[i]._base.setY(0);
+		_bins[i]._val.setX(_bins[i]._base.x());
+		_bins[i]._val.setY(_bins[i]._base.y());
+	}
+}
+
+/**
+ *  Get the number of bins.
+ */
+int Graph::size()
+{
+	return _size;
+}
+
+/**
+ * @brief Reinitialize the Graph according to the Vis. model.
+ *
+ * @param histo: Input location for the model descriptor.
+ */
+void Graph::setModelPtr(kshark_trace_histo *histo)
+{
+	if (_size != histo->n_bins) {
+		delete[] _bins;
+		_size = histo->n_bins;
+		_bins = new(std::nothrow) Bin[_size];
+		if (!_bins) {
+			_size = 0;
+			fprintf(stderr,
+				"Failed to allocate memory graph's bins.\n");
+		}
+	}
+
+	_histoPtr = histo;
+	_initBins();
+}
+
+/**
+ * @brief This function will set the Y (vertical) coordinate of the Graph's
+ *	  base. It is safe to use this function even if the Graph contains
+ *	  data.
+ *
+ * @param b: Y coordinate of the Graph's base in pixels.
+ */
+void Graph::setBase(int b)
+{
+	int mod;
+
+	if (!_size)
+		return;
+
+	if (b == _bins[0]._base.y()) // Nothing to do.
+		return;
+
+	for (int i = 0; i < _size; ++i) {
+		mod = _bins[i].mod();
+		_bins[i]._base.setY(b);
+		_bins[i]._val.setY(b + mod);
+	}
+}
+
+/**
+ * @brief Set the vertical size (height) of the Graph.
+ *
+ * @param h: the height of the Graph in pixels.
+ */
+void Graph::setHeight(int h)
+{
+	_height = h;
+}
+
+/**
+ * @brief Set the size of the white space added on both sides of the Graph.
+ *
+ * @param hMargin: the size of the white space in pixels.
+ */
+void Graph::setHMargin(int hMargin)
+{
+	if (!_size)
+		return;
+
+	if (hMargin == _bins[0]._base.x()) // Nothing to do.
+		return;
+
+	for (int i = 0; i < _size; ++i) {
+		_bins[i]._base.setX(i + hMargin);
+		_bins[i]._val.setX(_bins[i]._base.x());
+	}
+
+	_hMargin = hMargin;
+}
+
+/**
+ * @brief Set the value of a given bin.
+ *
+ * @param bin: Bin Id.
+ * @param val: Bin height in pixels.
+ */
+void Graph::setBinValue(int bin, int val)
+{
+	_bins[bin].setVal(val);
+}
+
+/**
+ * @brief Set the Process Id (Front and Back) a given bin.
+ *
+ * @param bin: Bin Id.
+ * @param pidF: The Process Id detected at the from (first in time) edge of
+ *		the bin.
+ * @param pidB: The Process Id detected at the back (last in time) edge of
+ *		the bin.
+ */
+void Graph::setBinPid(int bin, int pidF, int pidB)
+{
+	_bins[bin]._pidFront = pidF;
+	_bins[bin]._pidBack = pidB;
+}
+
+/**
+ * @brief Set the color of a given bin.
+ *
+ * @param bin: Bin Id.
+ * @param col: the color of the bin.
+ */
+void Graph::setBinColor(int bin, const Color &col)
+{
+	_bins[bin]._color = col;
+}
+
+/**
+ * @brief Set the visiblity mask of a given bin.
+ *
+ * @param bin: Bin Id.
+ * @param m: the visiblity mask.
+ */
+void Graph::setBinVisMask(int bin, uint8_t m)
+{
+	_bins[bin]._visMask = m;
+}
+
+/**
+ * @brief Set all fields of a given bin.
+ *
+ * @param bin: Bin Id.
+ * @param pidF: The Process Id detected at the from (first in time) edge of
+ *		the bin.
+ * @param pidB: The Process Id detected at the back (last in time) edge of
+ *		the bin.
+ * @param col: the color of the bin.
+ * @param m: the visiblity mask.
+ */
+void Graph::setBin(int bin, int pidF, int pidB, const Color &col, uint8_t m)
+{
+	setBinPid(bin, pidF, pidB);
+	setBinValue(bin, _height * .7);
+	setBinColor(bin, col);
+	setBinVisMask(bin, m);
+}
+
+/**
+ * @brief Process a CPU Graph.
+ *
+ * @param cpu: The CPU core.
+ */
+void Graph::fillCPUGraph(int cpu)
+{
+	struct kshark_entry *eFront;
+	int pidFront(0), pidBack(0);
+	int pidBackNoFilter;
+	uint8_t visMask;
+	ssize_t index;
+	int bin;
+
+	auto get_pid = [&] (int bin)
+	{
+		eFront = nullptr;
+
+		pidFront = ksmodel_get_pid_front(_histoPtr, bin,
+							    cpu,
+							    true,
+							    _collectionPtr,
+							    &index);
+
+		if (index >= 0)
+			eFront = _histoPtr->data[index];
+
+		pidBack = ksmodel_get_pid_back(_histoPtr, bin,
+							  cpu,
+							  true,
+							  _collectionPtr,
+							  nullptr);
+
+		pidBackNoFilter =
+			ksmodel_get_pid_back(_histoPtr, bin,
+						       cpu,
+						       false,
+						       _collectionPtr,
+						       nullptr);
+
+		if (pidBack != pidBackNoFilter)
+			pidBack = KS_FILTERED_BIN;
+
+		visMask = 0x0;
+		if (ksmodel_cpu_visible_event_exist(_histoPtr, bin,
+							       cpu,
+							       _collectionPtr,
+							       &index))
+
+			visMask = _histoPtr->data[index]->visible;
+		else if (eFront)
+			visMask = eFront->visible;
+	};
+
+	auto set_bin = [&] (int bin)
+	{
+		if (pidFront != KS_EMPTY_BIN || pidBack != KS_EMPTY_BIN) {
+			/* This is a regular process. */
+			setBin(bin, pidFront, pidBack,
+			       getPidColor(_pidColors, pidFront), visMask);
+		} else {
+			/* The bin contens no data from this CPU. */
+			setBinPid(bin, KS_EMPTY_BIN, KS_EMPTY_BIN);
+		}
+	};
+
+	/*
+	 * Check the content of the very firs bin and see if the CPU is
+	 * active.
+	 */
+	bin = 0;
+	get_pid(bin);
+	if (pidFront >= 0) {
+		/*
+		 * The CPU is active and this is a regular process.
+		 * Set this bin.
+		 */
+		set_bin(bin);
+	} else {
+		/*
+		 * No data from this CPU in the very firs bin. Use the Lower
+		 * Overflow Bin to retrieve the Process Id (if any). First
+		 * get the Pid back, ignoring the filters.
+		 */
+		pidBackNoFilter = ksmodel_get_pid_back(_histoPtr,
+						       LOWER_OVERFLOW_BIN,
+						       cpu,
+						       false,
+						       _collectionPtr,
+						       nullptr);
+
+		/* Now get the Pid back, applying filters. */
+		pidBack = ksmodel_get_pid_back(_histoPtr,
+					       LOWER_OVERFLOW_BIN,
+					       cpu,
+					       true,
+					       _collectionPtr,
+					       nullptr);
+
+		if (pidBack != pidBackNoFilter) {
+			/* The Lower Overflow Bin ends with filtered data. */
+			setBinPid(bin, KS_FILTERED_BIN, KS_FILTERED_BIN);
+		} else {
+			/*
+			 * The Lower Overflow Bin ends with data which has
+			 * to be plotted.
+			 */
+			setBinPid(bin, pidBack, pidBack);
+		}
+	}
+
+	/*
+	 * The first bin is already processed. The loop starts from the second
+	 * bin.
+	 */
+	for (bin = 1; bin < _histoPtr->n_bins; ++bin) {
+		/*
+		 * Check the content of this bin and see if the CPU is active.
+		 * If yes, retrieve the Process Id.
+		 */
+		get_pid(bin);
+		set_bin(bin);
+	}
+}
+
+/**
+ * @brief Process a Task Graph.
+ *
+ * @param pid: The Process Id of the Task.
+ */
+void Graph::fillTaskGraph(int pid)
+{
+	int cpu, pidFront(0), pidBack(0), lastCpu(-1), bin(0);
+	uint8_t visMask;
+	ssize_t index;
+
+	auto set_bin = [&] (int bin)
+	{
+		if (cpu >= 0) {
+			KsPlot::Color col;
+			col.setRainbowColor(cpu);
+
+			/* Data from the Task has been found in this bin. */
+			if (pid == pidFront && pid == pidBack) {
+				/* No data from other tasks in this bin. */
+				setBin(bin, pid, pid, col, visMask);
+			} else if (pid != pidFront && pid != pidBack) {
+				/*
+				 * There is some data from other tasks at both
+				 * front and back sides of this bin. But we
+				 * still want to see this bin drawn.
+				 */
+				setBin(bin, pid, KS_FILTERED_BIN, col,
+				       visMask);
+			} else {
+				if (pidFront != pid) {
+					/*
+					 * There is some data from another
+					 * task at the front side of this bin.
+					 */
+					pidFront = KS_FILTERED_BIN;
+				}
+
+				if (pidBack != pid) {
+					/*
+					 * There is some data from another
+					 * task at the back side of this bin.
+					 */
+					pidBack = KS_FILTERED_BIN;
+				}
+
+				setBin(bin, pidFront, pidBack, col, visMask);
+			}
+
+			lastCpu = cpu;
+		} else {
+			/*
+			 * No data from the Task in this bin. Check the CPU,
+			 * previously used by the task.
+			 */
+			int cpuPid = ksmodel_get_pid_back(_histoPtr,
+							  bin,
+							  lastCpu,
+							  false,
+							  _collectionPtr,
+							  nullptr);
+
+			if (cpuPid != KS_EMPTY_BIN) {
+				/*
+				 * If the CPU is active and works on another
+				 * task break the graph here.
+				 */
+				setBinPid(bin, KS_FILTERED_BIN, KS_EMPTY_BIN);
+			} else {
+				/*
+				 * No data from this CPU in the bin.
+				 * Continue the graph.
+				 */
+				setBinPid(bin, KS_EMPTY_BIN, KS_EMPTY_BIN);
+			}
+		}
+	};
+
+	auto get_pid_cpu = [&] (int bin)
+	{
+		/* Get the CPU used by this task. */
+		cpu = ksmodel_get_cpu_front(_histoPtr, bin,
+						       pid,
+						       false,
+						       _collectionPtr,
+						       nullptr);
+
+		if (cpu < 0) {
+			pidFront = pidBack = cpu;
+		} else {
+			/*
+			 * Get the process Id at the begining and at the end
+			 * of the bin.
+			 */
+			pidFront = ksmodel_get_pid_front(_histoPtr,
+							 bin,
+							 cpu,
+							 false,
+							 _collectionPtr,
+							 nullptr);
+
+			pidBack = ksmodel_get_pid_back(_histoPtr,
+						       bin,
+						       cpu,
+						       false,
+						       _collectionPtr,
+						       nullptr);
+
+			visMask = 0x0;
+			if (ksmodel_task_visible_event_exist(_histoPtr,
+							     bin,
+							     pid,
+							     _collectionPtr,
+							     &index)) {
+				visMask = _histoPtr->data[index]->visible;
+			}
+		}
+	};
+
+	/*
+	 * Check the content of the very firs bin and see if the Task is
+	 * active.
+	 */
+	get_pid_cpu(bin);
+
+	if (cpu >= 0) {
+		/* The Task is active. Set this bin. */
+		set_bin(bin);
+	} else {
+		/*
+		 * No data from this Task in the very firs bin. Use the Lower
+		 * Overflow Bin to retrieve the CPU used by the task (if any).
+		 */
+		cpu = ksmodel_get_cpu_back(_histoPtr, LOWER_OVERFLOW_BIN, pid,
+					   false, _collectionPtr, nullptr);
+		if (cpu >= 0) {
+			/*
+			 * The Lower Overflow Bin contains data from this Task.
+			 * Now look again in the Lower Overflow Bin and find
+			 * the Pid of the last active task on the same CPU.
+			 */
+			int pidCpu = ksmodel_get_pid_back(_histoPtr,
+							  LOWER_OVERFLOW_BIN,
+							  cpu,
+							  false,
+							  _collectionPtr,
+							  nullptr);
+			if (pidCpu == pid) {
+				/*
+				 * The Task is the last one running on this
+				 * CPU. Set the Pid of the bin. In this case
+				 * the very firs bin is empty but we derive
+				 * the Process Id from the Lower Overflow Bin.
+				 */
+				setBinPid(bin, pid, pid);
+				lastCpu = cpu;
+			}
+		}
+	}
+
+	/*
+	 * The first bin is already processed. The loop starts from the second
+	 * bin.
+	 */
+	for (bin = 1; bin < _histoPtr->n_bins; ++bin) {
+		get_pid_cpu(bin);
+
+		/* Set the bin accordingly. */
+		set_bin(bin);
+	}
+}
+
+/**
+ * @brief Draw the Graph
+ *
+ * @param size: The size of the lines of the individual Bins.
+ */
+void Graph::draw(float size)
+{
+	int lastPid(0), b(0), boxH(_height * .3);
+	Rectangle taskBox;
+
+	/*
+	 * Start by drawing a line between the base points of the first and
+	 * the last bin.
+	 */
+	drawLine(_bins[0]._base, _bins[_size - 1]._base, {}, size);
+
+	/* Draw as vartical lines all bins containing data. */
+	for (int i = 0; i < _size; ++i)
+		if (_bins[i]._pidFront >= 0 || _bins[i]._pidBack >= 0)
+			if (_bins[i]._visMask & KS_EVENT_VIEW_FILTER_MASK)
+				_bins[i].draw();
+
+	/*
+	 * Draw colored boxes for processes. First find the first bin, which
+	 * contains data and determine its PID.
+	 */
+	for (; b < _size; ++b) {
+		if (_bins[b]._pidBack > 0) {
+			lastPid = _bins[b]._pidFront;
+			/*
+			 * Initialize a box starting from this bin.
+			 * The color of the taskBox corresponds to the Pid
+			 * of the process.
+			 */
+			taskBox._color = getPidColor(_pidColors, lastPid);
+			taskBox.setPoint(0, _bins[b]._base.x(),
+					_bins[b]._base.y() - boxH);
+			taskBox.setPoint(1, _bins[b]._base.x(),
+					_bins[b]._base.y());
+			break;
+		}
+	}
+
+	for (; b < _size; ++b) {
+		if (_bins[b]._pidFront == KS_EMPTY_BIN &&
+		    _bins[b]._pidBack == KS_EMPTY_BIN) {
+			/*
+			 * This bin is empty. If a colored taskBox is already
+			 * initialized, it will be extended.
+			 */
+			continue;
+		}
+
+		if (_bins[b]._pidFront != _bins[b]._pidBack ||
+		    _bins[b]._pidFront != lastPid ||
+		    _bins[b]._pidBack  != lastPid) {
+			/* A new process starts here. */
+			if (lastPid > 0 && b > 0) {
+				/*
+				 * There is another process running up to this
+				 * point. Close its colored box here and draw.
+				 */
+				taskBox.setPoint(3, _bins[b]._base.x() - 1,
+						_bins[b]._base.y() - boxH);
+				taskBox.setPoint(2, _bins[b]._base.x() - 1,
+						_bins[b]._base.y());
+				taskBox.draw();
+			}
+
+			if (_bins[b]._pidBack > 0) {
+				/*
+				 * This is a regular process. Initialize
+				 * colored box starting from this bin.
+				 */
+				taskBox._color = getPidColor(_pidColors,
+							 _bins[b]._pidBack);
+
+				taskBox.setPoint(0, _bins[b]._base.x() - 1,
+						_bins[b]._base.y() - boxH);
+				taskBox.setPoint(1, _bins[b]._base.x() - 1,
+						_bins[b]._base.y());
+			}
+
+			lastPid = _bins[b]._pidBack;
+		}
+	}
+
+	if (lastPid > 0) {
+		/*
+		 * This is the end of the Graph and we have a process running.
+		 * Close its colored box and draw.
+		 */
+		taskBox.setPoint(3, _bins[_size - 1]._base.x(),
+				_bins[_size - 1]._base.y() - boxH);
+		taskBox.setPoint(2, _bins[_size - 1]._base.x(),
+				_bins[_size - 1]._base.y());
+		taskBox.draw();
+	}
+}
+
+}; // KsPlot
diff --git a/kernel-shark-qt/src/KsPlotTools.hpp b/kernel-shark-qt/src/KsPlotTools.hpp
new file mode 100644
index 0000000..7464c72
--- /dev/null
+++ b/kernel-shark-qt/src/KsPlotTools.hpp
@@ -0,0 +1,465 @@ 
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+/*
+ * Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
+ */
+
+ /**
+ *  @file    KsPlotTools.hpp
+ *  @brief   KernelShark Plot tools.
+ */
+
+#ifndef _KS_PLOT_TOOLS_H
+#define _KS_PLOT_TOOLS_H
+
+// C++
+#include <forward_list>
+#include <unordered_map>
+
+// KernelShark
+#include "libkshark.h"
+#include "libkshark-plot.h"
+#include "libkshark-model.h"
+
+namespace KsPlot {
+
+/** This class represents a RGB color. */
+class Color {
+public:
+	Color();
+
+	Color(uint8_t r, uint8_t g, uint8_t b);
+
+	Color(int rgb);
+
+	/** @brief Get the Red coordinate of the color. */
+	uint8_t r() const {return _col_c.red;}
+
+	/** @brief Get the Green coordinate of the color. */
+	uint8_t g() const {return _col_c.green;}
+
+	/** @brief Get the Blue coordinate of the color. */
+	uint8_t b() const {return _col_c.blue;}
+
+	void set(uint8_t r, uint8_t g, uint8_t b);
+
+	void set(int rgb);
+
+	void setRainbowColor(int n);
+
+	/**
+	 * @brief Get the C struct defining the RGB color.
+	 */
+	const ksplot_color *color_c_ptr() const {return &_col_c;}
+
+	/**
+	 * @brief Set the frequency value used to generate the Rainbow
+	 * palette.
+	 */
+	static void setRainbowFrequency(float f) {_frequency = f;}
+
+	/**
+	 * @brief Get the frequency value used to generate the Rainbow
+	 * palette.
+	 */
+	static float getRainbowFrequency() {return _frequency;}
+
+private:
+	ksplot_color _col_c;
+
+	/** The frequency value used to generate the Rainbow palette. */
+	static float _frequency;
+};
+
+/** Hash table of colors. */
+typedef std::unordered_map<int, KsPlot::Color> ColorTable;
+
+ColorTable getColorTable();
+
+Color getPidColor(ColorTable *colors, int pid);
+
+/** Represents an abstract graphical element. */
+class PlotObject {
+public:
+	/**
+	 * @brief Create a default object.
+	 */
+	PlotObject() : _visible(true), _size(2.) {}
+
+	/**
+	* @brief Destroy the object. Keep this destructor virtual.
+	*/
+	virtual ~PlotObject() {}
+
+	/** Generic function used to draw different objects. */
+	void draw() const {
+		if (_visible)
+			_draw(_color, _size);
+	}
+
+	/** Is this object visible. */
+	bool	_visible;
+
+	/** The color of the object. */
+	Color	_color;
+
+	/** The size of the object. */
+	float	_size;
+
+private:
+	virtual void _draw(const Color &col, float s) const = 0;
+};
+
+/** List of graphical element. */
+typedef std::forward_list<PlotObject*> PlotObjList;
+
+class Point;
+
+/** Represents an abstract shape. */
+class Shape : public PlotObject {
+public:
+	Shape();
+
+	Shape(int n);
+
+	Shape(const Shape &);
+
+	Shape(Shape &&);
+
+	/* Keep this destructor virtual. */
+	virtual ~Shape();
+
+	void operator=(const Shape &s);
+
+	void setPoint(size_t i, int x, int y);
+
+	void setPoint(size_t i, const ksplot_point &p);
+
+	void setPoint(size_t i, const Point &p);
+
+	const ksplot_point *getPoint(size_t i) const;
+
+	void setPointX(size_t i, int x);
+
+	void setPointY(size_t i, int y);
+
+	int getPointX(size_t i) const;
+
+	int getPointY(size_t i) const;
+
+	/**
+	 * @brief Get the number of point used to define the polygon.
+	 */
+	size_t pointCount() const {return _nPoints;}
+
+protected:
+	/** The number of point used to define the polygon. */
+	size_t		_nPoints;
+
+	/** The array of point used to define the polygon. */
+	ksplot_point	*_points;
+};
+
+/** This class represents a 2D poin. */
+class Point : public Shape {
+public:
+	Point();
+
+	Point(int x, int y);
+
+	/**
+	* @brief Destroy the Point object. Keep this destructor virtual.
+	*/
+	virtual ~Point() {}
+
+	/** @brief Get the horizontal coordinate of the point. */
+	int x() const {return getPointX(0);}
+
+	/** @brief Get the vertical coordinate of the point. */
+	int y() const {return getPointY(0);}
+
+	/** @brief Set the horizontal coordinate of the point. */
+	void setX(int x) {setPointX(0, x);}
+
+	/** @brief Set the vertical coordinate of the point. */
+	void setY(int y) {setPointY(0, y);}
+
+	/**
+	 * @brief Set the coordinats of the point.
+	 *
+	 * @param x: horizontal coordinate of the point in pixels.
+	 * @param y: vertical coordinate of the point in pixels.
+	 */
+	void set(int x, int y) {setPoint(0, x, y);}
+
+	/**
+	 * @brief Get the C struct defining the point.
+	 */
+	const ksplot_point *point_c_ptr() const {return getPoint(0);}
+
+private:
+	void _draw(const Color &col, float size = 1.) const override;
+};
+
+void drawLine(const Point &a, const Point &b,
+	      const Color &col, float s);
+
+/** This class represents a straight line. */
+class Line : public Shape {
+public:
+	Line();
+
+	Line(const Point &a, const Point &b);
+
+	/**
+	* @brief Destroy the Line object. Keep this destructor virtual.
+	*/
+	virtual ~Line() {}
+
+	/**
+	 * @brief Set the coordinats of the first finishing point of the
+	 *	  line.
+	 *
+	 * @param x: horizontal coordinate of the point in pixels.
+	 * @param y: vertical coordinate of the point in pixels.
+	 */
+	void setA(int x, int y) { setPoint(0, x, y);}
+
+	/** @brief Get the first finishing point of the line. */
+	const ksplot_point *getA() const {return getPoint(0);}
+
+	/**
+	 * @brief Set the coordinats of the second finishing point of the
+	 *	  line.
+	 *
+	 * @param x: horizontal coordinate of the point in pixels.
+	 * @param y: vertical coordinate of the point in pixels.
+	 */
+	void setB(int x, int y) {setPoint(1, x, y);}
+
+	/** @brief Get the second finishing point of the line. */
+	const ksplot_point *getB() const {return getPoint(1);}
+
+private:
+	void _draw(const Color &col, float size = 1.) const override;
+};
+
+/** This class represents a polygon. */
+class Polygon : public Shape {
+public:
+	Polygon(size_t n);
+
+	/**
+	 * @brief Destroy the polygon object. Keep this destructor virtual.
+	 */
+	virtual ~Polygon() {}
+
+	/**
+	 * @brief Specify the way the polygon will be drawn.
+	 *
+	 * @param f: If True, the area of the polygon will be colored.
+	 *	  Otherwise only the contour of the polygon will be plotted.
+	 */
+	void setFill(bool f) {_fill = f;}
+
+private:
+	Polygon() = delete;
+
+	void _draw(const Color &, float size = 1.) const override;
+
+	/**
+	 * If True, the area of the polygon will be colored. Otherwise only
+	 * the contour of the polygon will be plotted.
+	 */
+	bool		_fill;
+};
+
+/** This class represents a triangle. */
+class Triangle : public Polygon {
+public:
+	Triangle() : Polygon(3) {}
+};
+
+/** This class represents a rectangle. */
+class Rectangle : public Polygon {
+public:
+	Rectangle() : Polygon(4) {}
+};
+
+/**
+ * This class represents the graphical element of the KernelShark GUI marker.
+ */
+class Mark : public PlotObject {
+public:
+	Mark();
+
+	/**
+	* @brief Destroy the Mark object. Keep this destructor virtual.
+	*/
+	virtual ~Mark() {}
+
+	void setDPR(int dpr);
+
+	void setX(int x);
+
+	void setY(int yA, int yB);
+
+	void setCPUY(int yCPU);
+
+	void setCPUVisible(bool v);
+
+	void setTaskY(int yTask);
+
+	void setTaskVisible(bool v);
+
+private:
+	void _draw(const Color &col, float size = 1.) const override;
+
+	/** First finishing point of the Mark's line. */
+	Point _a;
+
+	/** Second finishing point of the Mark's line. */
+	Point _b;
+
+	/** A point indicating the position of the Mark in a CPU graph. */
+	Point _cpu;
+
+	/** A point indicating the position of the Mark in a Task graph. */
+	Point _task;
+};
+
+/** This class represents a KernelShark graph's bin. */
+class Bin : public PlotObject {
+public:
+	Bin();
+
+	void drawVal(float size = 2.);
+
+	/** Get the height (module) of the line, representing the Bin. */
+	int mod() {return _val.y() - _base.y();}
+
+	/** @brief Set the vertical coordinate of the "val" Point. */
+	void setVal(int v) {_val.setY(_base.y() - v); }
+
+	/**
+	 * The Process Id detected at the front (first in time) edge of
+	 * the bin.
+	 */
+	int	_pidFront;
+
+	/**
+	 * The Process Id detected at the back (last in time) edge of
+	 * the bin.
+	 */
+	int	_pidBack;
+
+	/** Lower finishing point of the line, representing the Bin. */
+	Point	_base;
+
+	/** Upper finishing point of the line, representing the Bin. */
+	Point	_val;
+
+	/** A bit mask controlling the visibility of the Bin. */
+	uint8_t	_visMask;
+
+private:
+	void _draw(const Color &col, float size = 1.) const override;
+};
+
+/** This class represents a KernelShark graph. */
+class Graph {
+public:
+	Graph();
+
+	/*
+	 * Disable copying. We can enable the Copy Constructor in the future,
+	 * but only if we really need it for some reason.
+	 */
+	Graph(const Graph &) = delete;
+
+	/* Disable moving. Same as copying.*/
+	Graph(Graph &&) = delete;
+
+	Graph(kshark_trace_histo *histo, KsPlot::ColorTable *ct);
+
+	~Graph();
+
+	int size();
+
+	void setModelPtr(kshark_trace_histo *histo);
+
+	/**
+	 * @brief Provide the Graph with a Data Collection. The collection
+	 *	  will be used to optimise the processing of the content of
+	 *	  the bins.
+	 *
+	 * @param col: Input location for the data collection descriptor.
+	 */
+	void setDataCollectionPtr(kshark_entry_collection *col) {
+		_collectionPtr = col;
+	}
+
+	/** @brief Set the Hash table of Task's colors. */
+	void setColorTablePtr(KsPlot::ColorTable *ct) {_pidColors = ct;}
+
+	void fillCPUGraph(int cpu);
+
+	void fillTaskGraph(int pid);
+
+	void draw(float s = 1);
+
+	void setBase(int b);
+
+	/** @brief Get the vertical coordinate of the Graph's base. */
+	int getBase() const {return _bins[0]._base.y();}
+
+	void setHeight(int h);
+
+	int getHeight() const {return _height;}
+
+	void setBinValue(int bin, int val);
+
+	void setBinPid(int bin, int pidF, int pidB);
+
+	void setBinColor(int bin, const Color &col);
+
+	void setBinVisMask(int bin, uint8_t m);
+
+	void setBin(int bin, int pidF, int pidB,
+		    const Color &col, uint8_t m);
+
+	const Bin &getBin(int bin) const {return _bins[bin];}
+
+	void setHMargin(int hMargin);
+
+private:
+	/** Pointer to the model descriptor object. */
+	kshark_trace_histo	*_histoPtr;
+
+	/** An array of Bins. */
+	Bin			*_bins;
+
+	/** The number of Bins. */
+	int			_size;
+
+	/**
+	 * The size (in pixels) of the white space added on both sides of
+	 * the Graph.
+	 */
+	int			_hMargin;
+
+	/** The vertical size (height) of the Graph. */
+	int			_height;
+
+	/** Pointer to the data collection object. */
+	kshark_entry_collection	*_collectionPtr;
+
+	/** Hash table of Task's colors. */
+	ColorTable		*_pidColors;
+
+	void _initBins();
+};
+
+}; // KsPlot
+
+#endif  /* _KS_PLOT_TOOLS_H */