blob: 1d63a9b687cd26fa1be9fdcf0ebc4bb39b4bc1aa [file] [log] [blame]
// 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 <cstring>
#include <math.h>
// C++
#include <algorithm>
#include <vector>
// 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);
}
/** Alpha blending with white background. */
void Color::blend(float alpha)
{
if (alpha < 0 || alpha > 1.)
return;
auto lamBld = [alpha](int val) {
return val * alpha + (1 - alpha) * 255;
};
int r = lamBld(this->r());
int g = lamBld(this->g());
int b = lamBld(this->b());
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 taskColorTable()
{
struct kshark_context *kshark_ctx(nullptr);
int nTasks(0), pid, *pids, i(0), *streamIds;
std::vector<int> tempPids;
ColorTable colors;
if (!kshark_instance(&kshark_ctx))
return colors;
streamIds = kshark_all_streams(kshark_ctx);
for (int j = 0; j < kshark_ctx->n_streams; ++j) {
nTasks = kshark_get_task_pids(kshark_ctx, streamIds[j], &pids);
tempPids.insert(tempPids.end(), pids, pids + nTasks);
free(pids);
}
free(streamIds);
if (!tempPids.size())
return colors;
std::sort(tempPids.begin(), tempPids.end());
/* Erase duplicates. */
tempPids.erase(unique(tempPids.begin(), tempPids.end()),
tempPids.end());
/* Erase negative (error codes). */
auto isNegative = [](int i) {return i < 0;};
auto it = remove_if(tempPids.begin(), tempPids.end(), isNegative);
tempPids.erase(it, tempPids.end());
nTasks = tempPids.size();
if (tempPids[i] == 0) {
/* Pid <= 0 will be plotted in black. */
colors[i++] = {};
}
for (; i < nTasks; ++i) {
pid = tempPids[i];
colors[pid].setRainbowColor(i);
}
return colors;
}
/**
* @brief Create a Hash table of Rainbow colors. The CPU Ids are
* mapped to the palette of Rainbow colors.
*
* @returns ColorTable instance.
*/
ColorTable CPUColorTable()
{
kshark_context *kshark_ctx(nullptr);
int nCPUs, nCPUMax(0), *streamIds;
kshark_data_stream *stream;
ColorTable colors;
if (!kshark_instance(&kshark_ctx))
return colors;
streamIds = kshark_all_streams(kshark_ctx);
for (int i = 0; i < kshark_ctx->n_streams; ++i) {
stream = kshark_get_data_stream(kshark_ctx, streamIds[i]);
nCPUs = stream->n_cpus;
if (nCPUMax < nCPUs)
nCPUMax = nCPUs;
}
free(streamIds);
for (int i = 0; i < nCPUMax; ++i)
colors[i].setRainbowColor(i + 8);
return colors;
}
/**
* @brief Create a Hash table of Rainbow colors. The Steam Ids are
* mapped to the palette of Rainbow colors.
*
* @returns ColorTable instance.
*/
ColorTable streamColorTable()
{
kshark_context *kshark_ctx(nullptr);
ColorTable colors;
Color color;
float alpha(.35);
int *streamIds;
if (!kshark_instance(&kshark_ctx))
return colors;
streamIds = kshark_all_streams(kshark_ctx);
for (int i = 0; i < kshark_ctx->n_streams; ++i) {
/*
* Starting from index 6 provides better functioning of the
* color scheme slider.
*/
color.setRainbowColor(i + 6);
color.blend(alpha);
colors[streamIds[i]] = color;
}
free(streamIds);
return colors;
}
/**
* @brief Search the Hash table of Rainbow colors for a particular key (Id).
*
* @param colors: Input location for the ColorTable instance.
* @param id: The Id to search for.
*
* @returns The Rainbow color of the key "id". If "id" does not exist, the
* returned color is Black.
*/
Color getColor(const ColorTable *colors, int id)
{
auto item = colors->find(id);
if (item != colors->end())
return item->second;
return {};
}
/**
* @brief A method to be implemented in the inheriting class. It calculates
* the distance between the position of the click and the shape. This
* distance is used by the GUI to decide if the corresponding
* "Double click" action of the shape must be executed. The default
* implementation returns infinity.
*
* @param x: The X coordinate of the click.
* @param y: The Y coordinate of the click.
*/
double PlotObject::distance(int x, int y) const
{
return std::numeric_limits<double>::max();
}
/**
* @brief Create a default Shape.
*/
Shape::Shape()
: _nPoints(0),
_points(nullptr)
{}
/**
* @brief Create a Shape defined by "n" points. All points are initialized
* at (0, 0).
*/
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;
}
/** @brief Get the coordinates of the geometrical center of this shape. */
ksplot_point Shape::center() const
{
ksplot_point c = {0, 0};
for (size_t i = 0; i < _nPoints; ++i) {
c.x += _points[i].x;
c.y += _points[i].y;
}
c.x /= _nPoints;
c.y /= _nPoints;
return c;
}
/** 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::point(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::pointX(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::pointY(size_t i) const {
if (i < _nPoints)
return _points[i].y;
return 0;
}
/** @brief Create a default Point. The point is initialized at (0, 0). */
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 Draw a dashed 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.
* @param period: The period of the dashed line.
*/
void drawDashedLine(const Point &a, const Point &b,
const Color &col, float size, float period)
{
int dx = b.x() - a.x(), dy = b.y() - a.y();
float mod = sqrt(dx * dx + dy * dy);
int n = mod / period;
Point p1, p2;
for (int i = 0; i < n; ++i) {
p1.setX(a.x() + (i + .25) * dx / n);
p1.setY(a.y() + (i + .25) * dy / n);
p2.setX(a.x() + (i + .75) * dx / n);
p2.setY(a.y() + (i + .75) * dy / n);
drawLine(p1, p2, col, size);
}
}
/** @brief Create a default line. The two points are initialized at (0, 0). */
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 polyline. All points are initialized at (0, 0).
*
* @param n: The number of points connected by the polyline.
*/
Polyline::Polyline(size_t n)
: Shape(n)
{}
void Polyline::_draw(const Color &col, float size) const
{
ksplot_draw_polyline(_points, _nPoints, col.color_c_ptr(), size);
}
/**
* @brief Create a default polygon. All points are initialized at (0, 0).
*
* @param n: The number of edges of the polygon.
*/
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);
}
/** The created object will print/draw only the text without the frame. */
TextBox::TextBox()
: _text(""),
_font(nullptr)
{
setPos(Point(0, 0));
_box._visible = false;
}
/** The created object will print/draw only the text without the frame. */
TextBox::TextBox(ksplot_font *f)
: _text(""),
_font(f)
{
setPos(Point(0, 0));
_box._visible = false;
}
/** The created object will print/draw only the text without the frame. */
TextBox::TextBox(ksplot_font *f, const std::string &text, const Point &pos)
: _text(text),
_font(f)
{
setPos(pos);
_box._visible = false;
}
/** The created object will print/draw only the text without the frame. */
TextBox::TextBox(ksplot_font *f, const std::string &text, const Color &col,
const Point &pos)
: _text(text),
_font(f)
{
_color = col;
setPos(pos);
_box._visible = false;
}
/** The created object will print/draw the text and the frame. */
TextBox::TextBox(ksplot_font *f, const std::string &text, const Color &col,
const Point &pos, int l, int h)
: _text(text),
_font(f)
{
setPos(pos);
setBoxAppearance(col, l, h);
}
/**
* @brief Set the position of the bottom-left corner of the frame.
*
* @param p: The coordinates of the bottom-left corner.
*/
void TextBox::setPos(const Point &p)
{
_box.setPoint(0, p);
}
/**
* @brief Set the color and the dimensions of the frame.
*
* @param col: The color of the frame.
* @param l: The length of the frame.
* @param h: The height of the frame.
*/
void TextBox::setBoxAppearance(const Color &col, int l, int h)
{
_box.setFill(true);
_box._color = col;
_box._visible = true;
if (h <= 0 && _font)
h = _font->height;
_box.setPoint(1, _box.pointX(0), _box.pointY(0) - h);
_box.setPoint(2, _box.pointX(0) + l, _box.pointY(0) - h);
_box.setPoint(3, _box.pointX(0) + l, _box.pointY(0));
}
void TextBox::_draw(const Color &col, float size) const
{
_box.draw();
if (!_font || _text.empty())
return;
if (_box._visible ) {
int bShift = (_box.pointY(0) - _box.pointY(1) - _font->height) / 2;
ksplot_print_text(_font, NULL,
_box.pointX(0) + _font->height / 4,
_box.pointY(0) - _font->base - bShift,
_text.c_str());
} else {
ksplot_print_text(_font, col.color_c_ptr(),
_box.pointX(0) + _font->height / 4,
_box.pointY(0) - _font->base,
_text.c_str());
}
}
/**
* @brief Create a default Mark.
*/
Mark::Mark()
: _dashed(false)
{
_visible = false;
_cpu._color = Color(225, 255, 100);
_cpu._size = 5.5f;
_task._color = Color(0, 255, 0);
_task._size = 5.5f;
_combo._color = Color(100, 150, 255);
_combo._size = 5.5f;
}
void Mark::_draw(const Color &col, float size) const
{
if (_dashed)
drawDashedLine(_a, _b, col, size, 3 * _cpu._size / size);
else
drawLine(_a, _b, col, size);
_cpu.draw();
_task.draw();
_combo.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 = _combo._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);
_combo.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 Set the visiblity of the Mark's Combo point.
*
* @param v: If True, the Task point will be visible.
*/
void Mark::setComboVisible(bool v)
{
_combo._visible = v;
}
/**
* @brief Set the Y coordinates (vertical) of the Mark's Combo points.
*
* @param yCombo: Y coordinate of the Mark's Task point.
*/
void Mark::setComboY(int yCombo)
{
_combo.setY(yCombo);
}
/**
* @brief Create a default Bin.
*/
Bin::Bin()
: _idFront(KS_EMPTY_BIN),
_idBack(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),
_collectionPtr(nullptr),
_binColors(nullptr),
_ensembleColors(nullptr),
_label(),
_idleSuppress(false),
_idlePid(0),
_drawBase(true)
{}
/**
* @brief Create a Graph to represent the state of the Vis. model.
*
* @param histo: Input location for the model descriptor.
* @param bct: Input location for the Hash table of bin's colors.
* @param ect: Input location for the Hash table of ensemble's colors.
*/
Graph::Graph(kshark_trace_histo *histo, KsPlot::ColorTable *bct, KsPlot::ColorTable *ect)
: _histoPtr(histo),
_bins(new(std::nothrow) Bin[histo->n_bins]),
_size(histo->n_bins),
_collectionPtr(nullptr),
_binColors(bct),
_ensembleColors(ect),
_label(),
_idleSuppress(false),
_idlePid(0),
_drawBase(true)
{
if (!_bins) {
_size = 0;
fprintf(stderr, "Failed to allocate memory graph's bins.\n");
}
_initBins();
}
/**
* @brief Destroy the Graph object.
*/
Graph::~Graph()
{
delete[] _bins;
}
int Graph::_firstBinOffset()
{
return _labelSize + 2 * _hMargin;
}
void Graph::_initBins()
{
for (int i = 0; i < _size; ++i) {
_bins[i]._base.setX(i + _firstBinOffset());
_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() const
{
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;
_label.setBoxAppearance(_label._color, _labelSize, height());
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 color and the dimensions of the graph's label.
*
* @param f: The font to be used to draw the labels.
* @param col: The color of the ComboGraph's label.
* @param lSize: the size of the graph's label in pixels.
* @param hMargin: the size of the white margine space in pixels.
*/
void Graph::setLabelAppearance(ksplot_font *f, Color col, int lSize, int hMargin)
{
if (!_size)
return;
_labelSize = lSize;
_hMargin = hMargin;
_label.setPos({_hMargin, base()});
_label.setFont(f);
_label.setBoxAppearance(col, lSize, height());
for (int i = 0; i < _size; ++i) {
_bins[i]._base.setX(i + _firstBinOffset());
_bins[i]._val.setX(_bins[i]._base.x());
}
}
/**
* @brief Set Idle Suppression. If True, the bins containing Idle task records
* are not grouped together.
*
* @param is: If True, Idle is suppressed.
* @param ip: The process Id of the Idle task. If Idle is not suppressed, this
* value has no effect.
*/
void Graph::setIdleSuppressed(bool is, int ip)
{
_idleSuppress = is;
_idlePid = ip;
}
/**
* @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) 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.
*/
void Graph::setBinPid(int bin, int pidF, int pidB)
{
_bins[bin]._idFront = pidF;
_bins[bin]._idBack = 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 sd: Data stream identifier.
* @param cpu: The CPU core.
*/
void Graph::fillCPUGraph(int sd, int cpu)
{
struct kshark_entry *eFront;
int pidFront(0), pidBack(0);
int pidBackNoFilter;
uint8_t visMask;
ssize_t index;
int bin;
auto lamGetPid = [&] (int bin)
{
eFront = nullptr;
pidFront = ksmodel_get_pid_front(_histoPtr, bin,
sd,
cpu,
true,
_collectionPtr,
&index);
if (index >= 0)
eFront = _histoPtr->data[index];
pidBack = ksmodel_get_pid_back(_histoPtr, bin,
sd,
cpu,
true,
_collectionPtr,
nullptr);
pidBackNoFilter =
ksmodel_get_pid_back(_histoPtr, bin,
sd,
cpu,
false,
_collectionPtr,
nullptr);
if (pidBack != pidBackNoFilter)
pidBack = KS_FILTERED_BIN;
visMask = 0x0;
if (eFront) {
if (!(eFront->visible & KS_EVENT_VIEW_FILTER_MASK) &&
ksmodel_cpu_visible_event_exist(_histoPtr, bin,
sd,
cpu,
_collectionPtr,
&index)) {
visMask = _histoPtr->data[index]->visible;
} else {
visMask = eFront->visible;
}
}
};
auto lamSetBin = [&] (int bin)
{
if (pidFront != KS_EMPTY_BIN || pidBack != KS_EMPTY_BIN) {
/* This is a regular process. */
setBin(bin, pidFront, pidBack,
getColor(_binColors, 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 first bin and see if the CPU is
* active.
*/
bin = 0;
lamGetPid(bin);
if (pidFront >= 0) {
/*
* The CPU is active and this is a regular process.
* Set this bin.
*/
lamSetBin(bin);
} else {
/*
* No data from this CPU in the very first 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,
sd,
cpu,
false,
_collectionPtr,
nullptr);
/* Now get the Pid back, applying filters. */
pidBack = ksmodel_get_pid_back(_histoPtr,
LOWER_OVERFLOW_BIN,
sd,
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.
*/
lamGetPid(bin);
lamSetBin(bin);
}
}
/**
* @brief Process a Task Graph.
*
* @param sd: Data stream identifier.
* @param pid: The Process Id of the Task.
*/
void Graph::fillTaskGraph(int sd, int pid)
{
int cpuFront, cpuBack(0), pidFront(0), pidBack(0), lastCpu(-1), bin(0);
struct kshark_entry *eFront;
uint8_t visMask;
ssize_t index;
auto lamSetBin = [&] (int bin)
{
if (cpuFront >= 0) {
KsPlot::Color col = getColor(_binColors, pid);
/* 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, cpuFront, cpuBack, 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, cpuFront, KS_FILTERED_BIN, col,
visMask);
} else {
if (pidFront != pid) {
/*
* There is some data from another
* task at the front side of this bin.
*/
cpuFront = KS_FILTERED_BIN;
}
if (pidBack != pid) {
/*
* There is some data from another
* task at the back side of this bin.
*/
cpuBack = KS_FILTERED_BIN;
}
setBin(bin, cpuFront, cpuBack, col, visMask);
}
lastCpu = cpuBack;
} else {
/*
* No data from the Task in this bin. Check the CPU,
* previously used by the task. We are looking for
* data from another task running on the same CPU,
* hence we cannot use the collection of this task.
*/
int cpuPid = ksmodel_get_pid_back(_histoPtr,
bin,
sd,
lastCpu,
false,
nullptr, // No collection
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 lamGetPidCPU = [&] (int bin)
{
eFront = nullptr;
/* Get the CPU used by this task. */
cpuFront = ksmodel_get_cpu_front(_histoPtr, bin,
sd,
pid,
false,
_collectionPtr,
&index);
if (cpuFront >= 0 && index >= 0)
eFront = _histoPtr->data[index];
cpuBack = ksmodel_get_cpu_back(_histoPtr, bin,
sd,
pid,
false,
_collectionPtr,
nullptr);
if (cpuFront < 0) {
pidFront = pidBack = cpuFront;
} else {
/*
* Get the process Id at the begining and at the end
* of the bin.
*/
pidFront = ksmodel_get_pid_front(_histoPtr,
bin,
sd,
cpuFront,
false,
_collectionPtr,
nullptr);
pidBack = ksmodel_get_pid_back(_histoPtr,
bin,
sd,
cpuBack,
false,
_collectionPtr,
nullptr);
visMask = 0x0;
if (eFront) {
if (!(eFront->visible & KS_EVENT_VIEW_FILTER_MASK) &&
ksmodel_task_visible_event_exist(_histoPtr,
bin,
sd,
pid,
_collectionPtr,
&index)) {
visMask = _histoPtr->data[index]->visible;
} else {
visMask = eFront->visible;
}
}
}
};
/*
* Check the content of the very first bin and see if the Task is
* active.
*/
lamGetPidCPU(bin);
if (cpuFront >= 0) {
/* The Task is active. Set this bin. */
lamSetBin(bin);
} else {
/*
* No data from this Task in the very first bin. Use the Lower
* Overflow Bin to retrieve the CPU used by the task (if any).
*/
cpuFront = ksmodel_get_cpu_back(_histoPtr, LOWER_OVERFLOW_BIN,
sd, pid,
false, _collectionPtr, nullptr);
if (cpuFront >= 0) {
/*
* The Lower Overflow Bin contains data from this Task.
* Now look again in the Lower Overflow Bin and Bim 0
* and find the Pid of the last active task on the same
* CPU.
*/
int pidCpu0, pidCpuLOB;
pidCpu0 = ksmodel_get_pid_back(_histoPtr,
0,
sd,
cpuFront,
false,
_collectionPtr,
nullptr);
pidCpuLOB = ksmodel_get_pid_back(_histoPtr,
LOWER_OVERFLOW_BIN,
sd,
cpuFront,
false,
_collectionPtr,
nullptr);
if (pidCpu0 < 0 && pidCpuLOB == pid) {
/*
* The Task is the last one running on this
* CPU. Set the Pid of the bin. In this case
* the very first bin is empty but we derive
* the Process Id from the Lower Overflow Bin.
*/
setBinPid(bin, cpuFront, cpuFront);
lastCpu = cpuFront;
} else {
setBinPid(bin, KS_EMPTY_BIN, KS_EMPTY_BIN);
}
}
}
/*
* The first bin is already processed. The loop starts from the second
* bin.
*/
for (bin = 1; bin < _histoPtr->n_bins; ++bin) {
lamGetPidCPU(bin);
/* Set the bin accordingly. */
lamSetBin(bin);
}
}
/**
* @brief Draw the Graph
*
* @param size: The size of the lines of the individual Bins.
*/
void Graph::draw(float size)
{
int lastPid(-1), b(0), boxH(_height * .3);
Rectangle taskBox;
_label.draw();
if (_drawBase) {
/*
* 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]._idFront >= 0 || _bins[i]._idBack >= 0 ||
_bins[i]._idFront == _idlePid || _bins[i]._idBack ==_idlePid)
if (_bins[i]._visMask & KS_EVENT_VIEW_FILTER_MASK) {
_bins[i]._size = size;
_bins[i].draw();
}
auto lamCheckEnsblVal = [this] (int v) {
if (_idleSuppress && v == _idlePid)
return false;
return v >= 0;
};
/*
* Draw colored boxes for processes. First find the first bin, which
* contains data and determine its PID.
*/
for (; b < _size; ++b) {
if (lamCheckEnsblVal(_bins[b]._idBack)) {
lastPid = _bins[b]._idFront;
/*
* Initialize a box starting from this bin.
* The color of the taskBox corresponds to the Pid
* of the process.
*/
taskBox._color = getColor(_ensembleColors, 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]._idFront == KS_EMPTY_BIN &&
_bins[b]._idBack == KS_EMPTY_BIN) {
/*
* This bin is empty. If a colored taskBox is already
* initialized, it will be extended.
*/
continue;
}
if (_bins[b]._idFront != _bins[b]._idBack ||
_bins[b]._idFront != lastPid ||
_bins[b]._idBack != lastPid) {
/* A new process starts here. */
if (b > 0 && lamCheckEnsblVal(lastPid)) {
/*
* 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 (lamCheckEnsblVal(_bins[b]._idBack)) {
/*
* This is a regular process. Initialize
* colored box starting from this bin.
*/
taskBox._color = getColor(_ensembleColors,
_bins[b]._idBack);
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]._idBack;
}
}
if (lamCheckEnsblVal(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();
}
}
void VirtGap::_draw(const Color &col, float size) const
{
if (_entryPoint.x() - _exitPoint.x() < 4)
return;
Point p0(_exitPoint.x() + _size, _exitPoint.y());
Point p1(_exitPoint.x() + _size, _exitPoint.y() - _height);
Point p2(_entryPoint.x() - _size , _entryPoint.y());
Point p3(_entryPoint.x() - _size , _entryPoint.y() - _height);
Rectangle g;
g.setPoint(0, p0);
g.setPoint(1, p1);
g.setPoint(2, p2);
g.setPoint(3, p3);
g._color = {255, 255, 255}; // The virt. gap is always white.
g.setFill(false);
g.draw();
}
} // KsPlot