blob: f4fc35e1a227f820e97f3a21eda4c25cf871ce02 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2019 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
*/
/**
* @file KsPlugins.cpp
* @brief KernelShark C++ plugin declarations.
*/
// C++
#include<iostream>
#include <limits>
// KernelShark
#include "KsPlugins.hpp"
using namespace KsPlot;
/**
* A pair of Bin Id and a trace event data field in this bin, that needs to be
* plotted.
*/
typedef std::forward_list<std::pair<int, kshark_data_field_int64 *>> PlotPointList;
//! @cond Doxygen_Suppress
typedef std::function<void(int, kshark_data_container *, ssize_t,
PlotPointList *)> pushFunc;
typedef std::function<void(kshark_data_container *, ssize_t,
PlotPointList *)> resolveFunc;
//! @endcond
static void pointPlot(KsCppArgV *argvCpp, IsApplicableFunc isApplicable,
pluginShapeFunc makeShape, Color col, float size)
{
int nBins = argvCpp->_graph->size();
for (int bin = 0; bin < nBins; ++bin)
if (isApplicable(nullptr, bin))
argvCpp->_shapes->push_front(makeShape({argvCpp->_graph},
{bin}, {},
col, size));
}
static std::pair<ssize_t, ssize_t>
getRange(kshark_trace_histo *histo, kshark_data_container *data)
{
ssize_t firstEntry, lastEntry;
std::pair<ssize_t, ssize_t> err(-1, -2);
firstEntry = kshark_find_entry_field_by_time(histo->min,
data->data,
0,
data->size - 1);
if (firstEntry == BSEARCH_ALL_SMALLER)
return err;
if (firstEntry == BSEARCH_ALL_GREATER)
firstEntry = 0;
lastEntry = kshark_find_entry_field_by_time(histo->max,
data->data,
firstEntry,
data->size - 1);
if (lastEntry == BSEARCH_ALL_GREATER)
return err;
if (lastEntry == BSEARCH_ALL_SMALLER)
lastEntry = data->size - 1;
return {firstEntry, lastEntry};
}
static PlotPointList
getInBinEvents(kshark_trace_histo *histo,
kshark_data_container *data,
IsApplicableFunc isApplicable,
pushFunc push,
resolveFunc resolve)
{
int bin, lastBin(-1);
PlotPointList buffer;
auto lamIsOverflow = [] (int bin) {
return (bin == UPPER_OVERFLOW_BIN ||
bin == LOWER_OVERFLOW_BIN) ? true : false;
};
auto range = getRange(histo, data);
for (ssize_t i = range.second; i >= range.first; --i) {
if (isApplicable(data, i)) {
bin = ksmodel_get_bin(histo, data->data[i]->entry);
if (lamIsOverflow(bin))
continue;
if (bin != lastBin) {
push(bin, data, i, &buffer);
lastBin = bin;
} else {
resolve(data, i, &buffer);
}
}
}
return buffer;
}
static PlotPointList
getLastInBinEvents(kshark_trace_histo *histo, kshark_data_container *data,
IsApplicableFunc isApplicable)
{
pushFunc push = [] (int bin, kshark_data_container *data, ssize_t i,
PlotPointList *list) {
list->push_front({bin, data->data[i]});
};
/*
* Do not resolve. This means that only the very last (in time)
* appearance of the event in the bin will be visualized.
*/
resolveFunc resolve = [] (kshark_data_container *data, ssize_t i,
PlotPointList *list) {};
return getInBinEvents(histo, data, isApplicable, push, resolve);
}
static PlotPointList
getMaxInBinEvents(kshark_trace_histo *histo, kshark_data_container *data,
IsApplicableFunc isApplicable)
{
pushFunc push = [] (int bin, kshark_data_container *data, ssize_t i,
PlotPointList *list) {
list->push_front({bin, data->data[i]});
};
/* Overwrite if bigger. */
resolveFunc resolve = [] (kshark_data_container *data, ssize_t i,
PlotPointList *list) {
if (list->front().second < data->data[i])
list->front().second = data->data[i];
};
return getInBinEvents(histo, data, isApplicable, push, resolve);
}
static PlotPointList
getMinInBinEvents(kshark_trace_histo *histo, kshark_data_container *data,
IsApplicableFunc isApplicable)
{
pushFunc push = [] (int bin, kshark_data_container *data, ssize_t i,
PlotPointList *list) {
list->push_front({bin, data->data[i]});
};
/* Overwrite if smaller. */
resolveFunc resolve = [] (kshark_data_container *data, ssize_t i,
PlotPointList *list) {
if (list->front().second > data->data[i])
list->front().second = data->data[i];
};
return getInBinEvents(histo, data, isApplicable, push, resolve);
}
//! @cond Doxygen_Suppress
#define PLUGIN_MIN_BOX_SIZE 4
//! @endcond
static void intervalPlot(kshark_trace_histo *histo,
kshark_data_container *dataEvtA,
IsApplicableFunc checkFieldA,
kshark_data_container *dataEvtB,
IsApplicableFunc checkFieldB,
Graph *graph,
PlotObjList *shapes,
pluginShapeFunc makeShape,
Color col,
float size)
{
kshark_data_field_int64 *dataA, *dataB;
PlotPointList bufferA, bufferB;
int binA, binB;
int64_t tsB;
auto lamGetBin = [] (auto it) {return (*it).first;};
auto lamGetTime = [] (auto it) {return (*it).second->entry->ts;};
auto lamGetData = [] (auto it) {return (*it).second;};
bufferA = getLastInBinEvents(histo,
dataEvtA,
checkFieldA);
bufferB = getLastInBinEvents(histo,
dataEvtB,
checkFieldB);
if (bufferA.empty() || bufferB.empty())
return;
auto itA = bufferA.cbegin();
auto itB = bufferB.cbegin();
while (itA != bufferA.cend() && itB != bufferB.cend()) {
binA = lamGetBin(itA);
dataA = lamGetData(itA);
/*
* We will draw a shape between "Event A" and "Event B".
* Because the shape starts with "Event A", we will skip all
* "Event B" entries before the "Event A" entry.
*/
do {
dataB = lamGetData(itB);
tsB = lamGetTime(itB);
binB = lamGetBin(itB);
itB++;
} while (itB != bufferB.cend() && tsB < lamGetTime(itA));
/*
* The shape ends with "Event B" and we already have this
* event. However, we have to make sure that we will start the
* shape from the very last "Event A" entry, which is rigth
* before the "Event B" entry, which we already selected.
*/
while (itA != bufferA.cend() && lamGetTime(itA) < tsB) {
dataA = lamGetData(itA);
binA = lamGetBin(itA);
itA++;
}
if (binB - binA >= PLUGIN_MIN_BOX_SIZE)
shapes->push_front(makeShape({graph},
{binA, binB},
{dataA, dataB},
col, size));
}
}
/**
* @brief Generic plotting method for plugins. To be used for visualizing
* a trace events.
*
* @param argvCpp: The C++ arguments of the drawing function of the plugin.
* @param isApplicable: Check function used to select events from data
* container A.
* @param makeShape: Input location for a function pointer used to generate
* the shape to be plotted.
* @param col: The color of the shape to be plotted.
* @param size: The size of the shape to be plotted.
*/
void eventPlot(KsCppArgV *argvCpp,
IsApplicableFunc isApplicable,
pluginShapeFunc makeShape,
Color col,
float size)
{
try {
pointPlot(argvCpp, isApplicable, makeShape, col, size);
} catch (const std::exception &exc) {
std::cerr << "Exception in eventPlot\n"
<< exc.what() << std::endl;
}
}
//! @cond Doxygen_Suppress
enum class PlotWath {
Maximum,
Minimum,
};
//! @endcond
static void eventFieldPlot(KsCppArgV *argvCpp,
kshark_data_container *dataEvt,
IsApplicableFunc checkField,
PlotWath s,
pluginShapeFunc makeShape,
KsPlot::Color col,
float size)
{
PlotPointList buffer;
if (dataEvt->size == 0)
return;
if (!dataEvt->sorted)
kshark_data_container_sort(dataEvt);
try {
if (s == PlotWath::Maximum)
buffer = getMaxInBinEvents(argvCpp->_histo,
dataEvt, checkField);
if (s == PlotWath::Minimum)
buffer = getMinInBinEvents(argvCpp->_histo,
dataEvt, checkField);
for (auto const &i: buffer) {
argvCpp->_shapes->push_front(makeShape({argvCpp->_graph},
{i.first},
{i.second},
col, size));
}
} catch (const std::exception &exc) {
std::cerr << "Exception in eventFieldPlot\n"
<< exc.what() << std::endl;
}
}
/**
* @brief Generic plotting method for plugins. To be used for visualizing
* the value of a data fiels trace events.
*
* @param argvCpp: The C++ arguments of the drawing function of the plugin.
* @param dataEvt: Input location for the container of the Evant's data.
* @param checkField: Check function used to select events from data
* container.
* @param makeShape: Input location for a function pointer used to generate
* the shape to be plotted.
* @param col: The color of the shape to be plotted.
* @param size: The size of the shape to be plotted.
*/
void eventFieldPlotMax(KsCppArgV *argvCpp,
kshark_data_container *dataEvt,
IsApplicableFunc checkField,
pluginShapeFunc makeShape,
KsPlot::Color col,
float size)
{
eventFieldPlot(argvCpp, dataEvt, checkField,
PlotWath::Maximum,
makeShape, col, size);
}
/**
* @brief Generic plotting method for plugins. To be used for visualizing
* the value of a data fiels trace events.
*
* @param argvCpp: The C++ arguments of the drawing function of the plugin.
* @param dataEvt: Input location for the container of the Evant's data.
* @param checkField: check function used to select events from data
* container.
* @param makeShape: Input location for a function pointer used to generate
* the shape to be plotted.
* @param col: The color of the shape to be plotted.
* @param size: The size of the shape to be plotted.
*/
void eventFieldPlotMin(KsCppArgV *argvCpp,
kshark_data_container *dataEvt,
IsApplicableFunc checkField,
pluginShapeFunc makeShape,
KsPlot::Color col,
float size)
{
eventFieldPlot(argvCpp, dataEvt, checkField,
PlotWath::Minimum,
makeShape, col, size);
}
/**
* @brief Generic plotting method for plugins. To be used for visualizing
* the correlation between two trace events.
*
* @param argvCpp: The C++ arguments of the drawing function of the plugin.
* @param dataEvtA: Input location for the container of the Evant A data.
* @param checkFieldA: Check function used to select events from data
* container A.
* @param dataEvtB: Input location for the container of the Evant B data.
* @param checkFieldB: Check function used to select events from data
* container B.
* @param makeShape: Input location for a function pointer used to generate
* the shape to be plotted.
* @param col: The color of the shape to be plotted.
* @param size: The size of the shape to be plotted.
*/
void eventFieldIntervalPlot(KsCppArgV *argvCpp,
kshark_data_container *dataEvtA,
IsApplicableFunc checkFieldA,
kshark_data_container *dataEvtB,
IsApplicableFunc checkFieldB,
pluginShapeFunc makeShape,
KsPlot::Color col,
float size)
{
if (dataEvtA->size == 0 || dataEvtB->size == 0)
return;
if (!dataEvtA->sorted)
kshark_data_container_sort(dataEvtA);
if (!dataEvtB->sorted)
kshark_data_container_sort(dataEvtB);
try {
intervalPlot(argvCpp->_histo,
dataEvtA, checkFieldA,
dataEvtB, checkFieldB,
argvCpp->_graph,
argvCpp->_shapes,
makeShape, col, size);
} catch (const std::exception &exc) {
std::cerr << "Exception in eventFieldIntervalPlot\n"
<< exc.what() << std::endl;
}
}
/**
* @brief Distance between the click and the shape. Used to decide if
* the double click action must be executed.
*
* @param x: X coordinate of the click.
* @param y: Y coordinate of the click.
*
* @returns If the click is inside the box, the distance is zero.
* Otherwise infinity.
*/
double LatencyBox::distance(int x, int y) const
{
if (x < pointX(0) || x > pointX(2))
return std::numeric_limits<double>::max();
if (y < pointY(0) || y > pointY(1))
return std::numeric_limits<double>::max();
return 0;
}