@@ -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}
new file mode 100644
@@ -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
new file mode 100644
@@ -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 */
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