blob: 20947958d68c60a442d6f32f117fcf36030fd4ef [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2017 VMware Inc, Yordan Karadzhov <y.karadz@gmail.com>
*/
/**
* @file libkshark-model.c
* @brief Visualization model for FTRACE (trace-cmd) data.
*/
// C
#include <stdlib.h>
#include <assert.h>
// KernelShark
#include "libkshark-model.h"
/** The index of the Upper Overflow bin. */
#define UOB(histo) (histo->n_bins)
/** The index of the Lower Overflow bin. */
#define LOB(histo) (histo->n_bins + 1)
/** For all bins. */
# define ALLB(histo) LOB(histo)
/**
* @brief Initialize the Visualization model.
*
* @param histo: Input location for the model descriptor.
*/
void ksmodel_init(struct kshark_trace_histo *histo)
{
/*
* Initialize an empty histo. The histo will have no bins and will
* contain no data.
*/
histo->bin_size = 0;
histo->min = 0;
histo->max = 0;
histo->n_bins = 0;
histo->bin_count = NULL;
histo->map = NULL;
}
/**
* @brief Clear (reset) the Visualization model.
*
* @param histo: Input location for the model descriptor.
*/
void ksmodel_clear(struct kshark_trace_histo *histo)
{
/* Reset the histo. It will have no bins and will contain no data. */
free(histo->map);
free(histo->bin_count);
ksmodel_init(histo);
}
static void ksmodel_reset_bins(struct kshark_trace_histo *histo,
size_t first, size_t last)
{
/*
* Reset the content of the bins.
* Be careful here! Resetting the entire array of signed integers with
* memset() will work only for values of "0" and "-1". Hence
* KS_EMPTY_BIN is expected to be "-1".
*/
memset(&histo->map[first], KS_EMPTY_BIN,
(last - first + 1) * sizeof(histo->map[0]));
memset(&histo->bin_count[first], 0,
(last - first + 1) * sizeof(histo->bin_count[0]));
}
static bool ksmodel_histo_alloc(struct kshark_trace_histo *histo, size_t n)
{
free(histo->bin_count);
free(histo->map);
/* Create bins. Two overflow bins are added. */
histo->map = calloc(n + 2, sizeof(*histo->map));
histo->bin_count = calloc(n + 2, sizeof(*histo->bin_count));
if (!histo->map || !histo->bin_count) {
ksmodel_clear(histo);
fprintf(stderr, "Failed to allocate memory for a histo.\n");
return false;
}
histo->n_bins = n;
return true;
}
static void ksmodel_set_in_range_bining(struct kshark_trace_histo *histo,
size_t n, uint64_t min, uint64_t max,
bool force_in_range)
{
uint64_t corrected_range, delta_range, range = max - min;
struct kshark_entry *last;
/* The size of the bin must be >= 1, hence the range must be >= n. */
if (n == 0 || range < n) {
range = n;
max = min + n;
}
/*
* If the number of bins changes, allocate memory for the descriptor of
* the model.
*/
if (n != histo->n_bins) {
if (!ksmodel_histo_alloc(histo, n)) {
ksmodel_clear(histo);
return;
}
}
/* Reset the content of all bins (including overflow bins) to zero. */
ksmodel_reset_bins(histo, 0, ALLB(histo));
if (range % n == 0) {
/*
* The range is multiple of the number of bin and needs no
* adjustment. This is very unlikely to happen but still ...
*/
histo->min = min;
histo->max = max;
histo->bin_size = range / n;
} else {
/*
* The range needs adjustment. The new range will be slightly
* bigger, compared to the requested one.
*/
histo->bin_size = range / n + 1;
corrected_range = histo->bin_size * n;
delta_range = corrected_range - range;
histo->min = min - delta_range / 2;
histo->max = histo->min + corrected_range;
if (!force_in_range)
return;
/*
* Make sure that the new range doesn't go outside of the time
* interval of the dataset.
*/
last = histo->data[histo->data_size - 1];
if (histo->min < histo->data[0]->ts) {
histo->min = histo->data[0]->ts;
histo->max = histo->min + corrected_range;
} else if (histo->max > last->ts) {
histo->max = last->ts;
histo->min = histo->max - corrected_range;
}
}
}
/**
* @brief Prepare the bining of the Visualization model.
*
* @param histo: Input location for the model descriptor.
* @param n: Number of bins.
* @param min: Lower edge of the time-window to be visualized.
* @param max: Upper edge of the time-window to be visualized.
*/
void ksmodel_set_bining(struct kshark_trace_histo *histo,
size_t n, uint64_t min, uint64_t max)
{
ksmodel_set_in_range_bining(histo, n, min, max, false);
}
static size_t ksmodel_set_lower_edge(struct kshark_trace_histo *histo)
{
/*
* Find the index of the first entry inside the range
* (timestamp >= min). Note that the value of "min" is considered
* inside the range.
*/
ssize_t row = kshark_find_entry_by_time(histo->min,
histo->data,
0,
histo->data_size - 1);
assert(row != BSEARCH_ALL_SMALLER);
if (row == BSEARCH_ALL_GREATER || row == 0) {
/* Lower Overflow bin is empty. */
histo->map[LOB(histo)] = KS_EMPTY_BIN;
histo->bin_count[LOB(histo)] = 0;
row = 0;
} else {
/*
* The first entry inside the range is not the first entry of
* the dataset. This means that the Lower Overflow bin contains
* data.
*/
/* Lower Overflow bin starts at "0". */
histo->map[LOB(histo)] = 0;
/*
* The number of entries inside the Lower Overflow bin is equal
* to the index of the first entry inside the range.
*/
histo->bin_count[LOB(histo)] = row;
}
/*
* Now check if the first entry inside the range falls into the first
* bin.
*/
if (histo->data[row]->ts < histo->min + histo->bin_size) {
/*
* It is inside the first bin. Set the beginning
* of the first bin.
*/
histo->map[0] = row;
} else {
/* The first bin is empty. */
histo->map[0] = KS_EMPTY_BIN;
}
return row;
}
static size_t ksmodel_set_upper_edge(struct kshark_trace_histo *histo)
{
/*
* Find the index of the first entry outside the range
* (timestamp > max). Note that the value of "max" is considered inside
* the range. Remember that kshark_find_entry_by_time returns the first
* entry which is equal or greater than the reference time.
*/
ssize_t row = kshark_find_entry_by_time(histo->max + 1,
histo->data,
0,
histo->data_size - 1);
assert(row != BSEARCH_ALL_GREATER);
if (row == BSEARCH_ALL_SMALLER) {
/* Upper Overflow bin is empty. */
histo->map[UOB(histo)] = KS_EMPTY_BIN;
histo->bin_count[UOB(histo)] = 0;
} else {
/*
* The Upper Overflow bin contains data. Set its beginning and
* the number of entries.
*/
histo->map[UOB(histo)] = row;
histo->bin_count[UOB(histo)] = histo->data_size - row;
}
return row;
}
static void ksmodel_set_next_bin_edge(struct kshark_trace_histo *histo,
size_t bin, size_t last_row)
{
size_t time, next_bin = bin + 1;
ssize_t row;
/* Calculate the beginning of the next bin. */
time = histo->min + next_bin * histo->bin_size;
/*
* The timestamp of the very last entry of the dataset can be exactly
* equal to the value of the upper edge of the range. This is very
* likely to happen when we use ksmodel_set_in_range_bining(). In this
* case we have to increase the size of the very last bin in order to
* make sure that the last entry of the dataset will fall into it.
*/
if (next_bin == histo->n_bins - 1)
++time;
/*
* Find the index of the first entry inside
* the next bin (timestamp > time).
*/
row = kshark_find_entry_by_time(time, histo->data, last_row,
histo->data_size - 1);
if (row < 0 || histo->data[row]->ts >= time + histo->bin_size) {
/* The bin is empty. */
histo->map[next_bin] = KS_EMPTY_BIN;
return;
}
/* Set the index of the first entry. */
histo->map[next_bin] = row;
}
/*
* Fill in the bin_count array, which maps the number of entries within each
* bin.
*/
static void ksmodel_set_bin_counts(struct kshark_trace_histo *histo)
{
int i = 0, prev_not_empty;
histo->tot_count = 0;
memset(&histo->bin_count[0], 0,
(histo->n_bins) * sizeof(histo->bin_count[0]));
/*
* Find the first bin which contains data. Start by checking the Lower
* Overflow bin.
*/
if (histo->map[LOB(histo)] != KS_EMPTY_BIN) {
prev_not_empty = LOB(histo);
} else {
/* Loop till the first non-empty bin. */
while (histo->map[i] < 0) {
++i;
}
prev_not_empty = i++;
}
/*
* Starting from the first not empty bin, loop over all bins and fill
* in the bin_count array to hold the number of entries in each bin.
*/
for (; i < histo->n_bins; ++i) {
if (histo->map[i] != KS_EMPTY_BIN) {
/* The current bin is not empty, take its data row and
* subtract it from the data row of the previous not
* empty bin, which will give us the number of data
* rows in the "prev_not_empty" bin.
*/
histo->bin_count[prev_not_empty] =
histo->map[i] - histo->map[prev_not_empty];
if (prev_not_empty != LOB(histo))
histo->tot_count +=
histo->bin_count[prev_not_empty];
prev_not_empty = i;
}
}
/* Check if the Upper Overflow bin contains data. */
if (histo->map[UOB(histo)] == KS_EMPTY_BIN) {
/*
* The Upper Overflow bin is empty. Use the size of the dataset
* to calculate the content of the previouse not empty bin.
*/
histo->bin_count[prev_not_empty] = histo->data_size -
histo->map[prev_not_empty];
} else {
/*
* Use the index of the first entry inside the Upper Overflow
* bin to calculate the content of the previouse not empty
* bin.
*/
histo->bin_count[prev_not_empty] = histo->map[UOB(histo)] -
histo->map[prev_not_empty];
}
histo->tot_count += histo->bin_count[prev_not_empty];
}
/**
* @brief Provide the Visualization model with data. Calculate the current
* state of the model.
*
* @param histo: Input location for the model descriptor.
* @param data: Input location for the trace data.
* @param n: Number of bins.
*/
void ksmodel_fill(struct kshark_trace_histo *histo,
struct kshark_entry **data, size_t n)
{
size_t last_row = 0;
int bin;
histo->data_size = n;
histo->data = data;
if (histo->n_bins == 0 ||
histo->bin_size == 0 ||
histo->data_size == 0) {
/*
* Something is wrong with this histo.
* Most likely the binning is not set.
*/
ksmodel_clear(histo);
fprintf(stderr,
"Unable to fill the model with data.\n");
fprintf(stderr,
"Try to set the bining of the model first.\n");
return;
}
/* Set the Lower Overflow bin */
ksmodel_set_lower_edge(histo);
/*
* Loop over the dataset and set the beginning of all individual bins.
*/
for (bin = 0; bin < histo->n_bins; ++bin) {
ksmodel_set_next_bin_edge(histo, bin, last_row);
if (histo->map[bin + 1] > 0)
last_row = histo->map[bin + 1];
}
/* Set the Upper Overflow bin. */
ksmodel_set_upper_edge(histo);
/* Calculate the number of entries in each bin. */
ksmodel_set_bin_counts(histo);
}
/**
* @brief Get the total number of entries in a given bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
*
* @returns The number of entries in this bin.
*/
size_t ksmodel_bin_count(struct kshark_trace_histo *histo, int bin)
{
if (bin >= 0 && bin < histo->n_bins)
return histo->bin_count[bin];
if (bin == UPPER_OVERFLOW_BIN)
return histo->bin_count[UOB(histo)];
if (bin == LOWER_OVERFLOW_BIN)
return histo->bin_count[LOB(histo)];
return 0;
}
/**
* @brief Shift the time-window of the model forward. Recalculate the current
* state of the model.
*
* @param histo: Input location for the model descriptor.
* @param n: Number of bins to shift.
*/
void ksmodel_shift_forward(struct kshark_trace_histo *histo, size_t n)
{
size_t last_row = 0;
int bin;
if (!histo->data_size)
return;
if (histo->map[UOB(histo)] == KS_EMPTY_BIN) {
/*
* The Upper Overflow bin is empty. This means that we are at
* the upper edge of the dataset already. Do nothing in this
* case.
*/
return;
}
histo->min += n * histo->bin_size;
histo->max += n * histo->bin_size;
if (n >= histo->n_bins) {
/*
* No overlap between the new and the old ranges. Recalculate
* all bins from scratch. First calculate the new range.
*/
ksmodel_set_bining(histo, histo->n_bins, histo->min,
histo->max);
ksmodel_fill(histo, histo->data, histo->data_size);
return;
}
/* Set the new Lower Overflow bin. */
ksmodel_set_lower_edge(histo);
/*
* Copy the the mapping indexes of all overlaping bins starting from
* bin "0" of the new histo. Note that the number of overlaping bins
* is histo->n_bins - n.
* We will do a sanity check. ksmodel_set_lower_edge() sets map[0]
* index of the new histo. This index should then be equal to map[n]
* index of the old histo.
*/
assert (histo->map[0] == histo->map[n]);
memmove(&histo->map[0], &histo->map[n],
sizeof(histo->map[0]) * (histo->n_bins - n));
/*
* The mapping index of the old Upper Overflow bin is now index of the
* first new bin.
*/
bin = UOB(histo) - n;
histo->map[bin] = histo->map[UOB(histo)];
/* Calculate only the content of the new (non-overlapping) bins. */
for (; bin < histo->n_bins; ++bin) {
ksmodel_set_next_bin_edge(histo, bin, last_row);
if (histo->map[bin + 1] > 0)
last_row = histo->map[bin + 1];
}
/*
* Set the new Upper Overflow bin and calculate the number of entries
* in each bin.
*/
ksmodel_set_upper_edge(histo);
ksmodel_set_bin_counts(histo);
}
/**
* @brief Shift the time-window of the model backward. Recalculate the current
* state of the model.
*
* @param histo: Input location for the model descriptor.
* @param n: Number of bins to shift.
*/
void ksmodel_shift_backward(struct kshark_trace_histo *histo, size_t n)
{
size_t last_row = 0;
int bin;
if (!histo->data_size)
return;
if (histo->map[LOB(histo)] == KS_EMPTY_BIN) {
/*
* The Lower Overflow bin is empty. This means that we are at
* the Lower edge of the dataset already. Do nothing in this
* case.
*/
return;
}
histo->min -= n * histo->bin_size;
histo->max -= n * histo->bin_size;
if (n >= histo->n_bins) {
/*
* No overlap between the new and the old range. Recalculate
* all bins from scratch. First calculate the new range.
*/
ksmodel_set_bining(histo, histo->n_bins, histo->min,
histo->max);
ksmodel_fill(histo, histo->data, histo->data_size);
return;
}
/* Set the new Lower Overflow bin. */
ksmodel_set_lower_edge(histo);
/*
* Copy the the mapping indexes of all overlaping bins starting from
* bin "0" of the old histo. Note that the number of overlaping bins
* is histo->n_bins - n.
*/
memmove(&histo->map[n], &histo->map[0],
sizeof(histo->map[0]) * (histo->n_bins - n));
/* Calculate only the content of the new (non-overlapping) bins. */
for (bin = 0; bin < n; ++bin) {
ksmodel_set_next_bin_edge(histo, bin, last_row);
if (histo->map[bin + 1] > 0)
last_row = histo->map[bin + 1];
}
/*
* Set the new Upper Overflow bin and calculate the number of entries
* in each bin.
*/
ksmodel_set_upper_edge(histo);
ksmodel_set_bin_counts(histo);
}
/**
* @brief Move the time-window of the model to a given location. Recalculate
* the current state of the model.
*
* @param histo: Input location for the model descriptor.
* @param ts: position in time to be visualized.
*/
void ksmodel_jump_to(struct kshark_trace_histo *histo, size_t ts)
{
size_t min, max, range_min;
if (ts > histo->min && ts < histo->max) {
/*
* The new position is already inside the range.
* Do nothing in this case.
*/
return;
}
/*
* Calculate the new range without changing the size and the number
* of bins.
*/
min = ts - histo->n_bins * histo->bin_size / 2;
/* Make sure that the range does not go outside of the dataset. */
if (min < histo->data[0]->ts) {
min = histo->data[0]->ts;
} else {
range_min = histo->data[histo->data_size - 1]->ts -
histo->n_bins * histo->bin_size;
if (min > range_min)
min = range_min;
}
max = min + histo->n_bins * histo->bin_size;
/* Use the new range to recalculate all bins from scratch. */
ksmodel_set_bining(histo, histo->n_bins, min, max);
ksmodel_fill(histo, histo->data, histo->data_size);
}
static void ksmodel_zoom(struct kshark_trace_histo *histo,
double r, int mark, bool zoom_in)
{
size_t range, min, max, delta_min;
double delta_tot;
if (!histo->data_size)
return;
/*
* If the marker is not set, assume that the focal point of the zoom
* is the center of the range.
*/
if (mark < 0)
mark = histo->n_bins / 2;
range = histo->max - histo->min;
/*
* Avoid overzooming. If needed, adjust the Scale factor to a the value
* which provides bin_size >= 5.
*/
if (zoom_in && range * (1 - r) < histo->n_bins * 5)
r = 1 - (histo->n_bins * 5) / range;
/*
* Now calculate the new range of the histo. Use the bin of the marker
* as a focal point for the zoomout. With this the maker will stay
* inside the same bin in the new histo.
*
* First we set delta_tot to increase by the percentage requested (r).
* Then we make delta_min equal to a percentage of delta_tot based on
* where the position of the mark is. After this we add / subtract the
* original min by the delta_min and subtract / add the max by
* delta_tot - delta_min.
*/
delta_tot = range * r;
if (mark == (int)histo->n_bins - 1)
delta_min = delta_tot;
else
delta_min = delta_tot * mark / histo->n_bins;
min = zoom_in ? histo->min + delta_min :
histo->min - delta_min;
max = zoom_in ? histo->max - (size_t) delta_tot + delta_min :
histo->max + (size_t) delta_tot - delta_min;
/* Make sure the new range doesn't go outside of the dataset. */
if (min < histo->data[0]->ts)
min = histo->data[0]->ts;
if (max > histo->data[histo->data_size - 1]->ts)
max = histo->data[histo->data_size - 1]->ts;
/*
* Use the new range to recalculate all bins from scratch. Enforce
* "In Range" adjustment of the range of the model, in order to avoid
* slowly drifting outside of the data-set in the case when the very
* first or the very last entry is used as a focal point.
*/
ksmodel_set_in_range_bining(histo, histo->n_bins, min, max, true);
ksmodel_fill(histo, histo->data, histo->data_size);
}
/**
* @brief Extend the time-window of the model. Recalculate the current state
* of the model.
*
* @param histo: Input location for the model descriptor.
* @param r: Scale factor of the zoom-out.
* @param mark: Focus point of the zoom-out.
*/
void ksmodel_zoom_out(struct kshark_trace_histo *histo,
double r, int mark)
{
ksmodel_zoom(histo, r, mark, false);
}
/**
* @brief Shrink the time-window of the model. Recalculate the current state
* of the model.
*
* @param histo: Input location for the model descriptor.
* @param r: Scale factor of the zoom-in.
* @param mark: Focus point of the zoom-in.
*/
void ksmodel_zoom_in(struct kshark_trace_histo *histo,
double r, int mark)
{
ksmodel_zoom(histo, r, mark, true);
}
/**
* @brief Get the index of the first entry in a given bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
*
* @returns Index of the first entry in this bin. If the bin is empty the
* function returns negative error identifier (KS_EMPTY_BIN).
*/
ssize_t ksmodel_first_index_at_bin(struct kshark_trace_histo *histo, int bin)
{
if (bin >= 0 && bin < (int) histo->n_bins)
return histo->map[bin];
if (bin == UPPER_OVERFLOW_BIN)
return histo->map[UOB(histo)];
if (bin == LOWER_OVERFLOW_BIN)
return histo->map[LOB(histo)];
return KS_EMPTY_BIN;
}
/**
* @brief Get the index of the last entry in a given bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
*
* @returns Index of the last entry in this bin. If the bin is empty the
* function returns negative error identifier (KS_EMPTY_BIN).
*/
ssize_t ksmodel_last_index_at_bin(struct kshark_trace_histo *histo, int bin)
{
ssize_t index = ksmodel_first_index_at_bin(histo, bin);
size_t count = ksmodel_bin_count(histo, bin);
if (index >= 0 && count)
index += count - 1;
return index;
}
static bool ksmodel_is_visible(struct kshark_entry *e)
{
if ((e->visible & KS_GRAPH_VIEW_FILTER_MASK) &&
(e->visible & KS_EVENT_VIEW_FILTER_MASK))
return true;
return false;
}
static struct kshark_entry_request *
ksmodel_entry_front_request_alloc(struct kshark_trace_histo *histo,
int bin, bool vis_only,
matching_condition_func func, int val)
{
size_t first, n;
/* Get the number of entries in this bin. */
n = ksmodel_bin_count(histo, bin);
if (!n)
return NULL;
first = ksmodel_first_index_at_bin(histo, bin);
return kshark_entry_request_alloc(first, n,
func, val,
vis_only, KS_GRAPH_VIEW_FILTER_MASK);
}
static struct kshark_entry_request *
ksmodel_entry_back_request_alloc(struct kshark_trace_histo *histo,
int bin, bool vis_only,
matching_condition_func func, int val)
{
size_t first, n;
/* Get the number of entries in this bin. */
n = ksmodel_bin_count(histo, bin);
if (!n)
return NULL;
first = ksmodel_last_index_at_bin(histo, bin);
return kshark_entry_request_alloc(first, n,
func, val,
vis_only, KS_GRAPH_VIEW_FILTER_MASK);
}
/**
* @brief Get the index of the first entry from a given Cpu in a given bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param cpu: Cpu Id.
*
* @returns Index of the first entry from a given Cpu in this bin.
*/
ssize_t ksmodel_first_index_at_cpu(struct kshark_trace_histo *histo,
int bin, int cpu)
{
size_t i, n, first, not_found = KS_EMPTY_BIN;
n = ksmodel_bin_count(histo, bin);
if (!n)
return not_found;
first = ksmodel_first_index_at_bin(histo, bin);
for (i = first; i < first + n; ++i) {
if (histo->data[i]->cpu == cpu) {
if (ksmodel_is_visible(histo->data[i]))
return i;
else
not_found = KS_FILTERED_BIN;
}
}
return not_found;
}
/**
* @brief Get the index of the first entry from a given Task in a given bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param pid: Process Id of a task.
*
* @returns Index of the first entry from a given Task in this bin.
*/
ssize_t ksmodel_first_index_at_pid(struct kshark_trace_histo *histo,
int bin, int pid)
{
size_t i, n, first, not_found = KS_EMPTY_BIN;
n = ksmodel_bin_count(histo, bin);
if (!n)
return not_found;
first = ksmodel_first_index_at_bin(histo, bin);
for (i = first; i < first + n; ++i) {
if (histo->data[i]->pid == pid) {
if (ksmodel_is_visible(histo->data[i]))
return i;
else
not_found = KS_FILTERED_BIN;
}
}
return not_found;
}
/**
* @brief In a given bin, start from the front end of the bin and go towards
* the back end, searching for an entry satisfying the Matching
* condition defined by a Matching condition function.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param vis_only: If true, a visible entry is requested.
* @param func: Matching condition function.
* @param val: Matching condition value, used by the Matching condition
* function.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL.
*/
const struct kshark_entry *
ksmodel_get_entry_front(struct kshark_trace_histo *histo,
int bin, bool vis_only,
matching_condition_func func, int val,
struct kshark_entry_collection *col,
ssize_t *index)
{
struct kshark_entry_request *req;
const struct kshark_entry *entry;
if (index)
*index = KS_EMPTY_BIN;
/* Set the position at the beginning of the bin and go forward. */
req = ksmodel_entry_front_request_alloc(histo, bin, vis_only,
func, val);
if (!req)
return NULL;
if (col && col->size)
entry = kshark_get_collection_entry_front(&req, histo->data,
col, index);
else
entry = kshark_get_entry_front(req, histo->data, index);
kshark_free_entry_request(req);
return entry;
}
/**
* @brief In a given bin, start from the back end of the bin and go towards
* the front end, searching for an entry satisfying the Matching
* condition defined by a Matching condition function.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param vis_only: If true, a visible entry is requested.
* @param func: Matching condition function.
* @param val: Matching condition value, used by the Matching condition
* function.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL.
*/
const struct kshark_entry *
ksmodel_get_entry_back(struct kshark_trace_histo *histo,
int bin, bool vis_only,
matching_condition_func func, int val,
struct kshark_entry_collection *col,
ssize_t *index)
{
struct kshark_entry_request *req;
const struct kshark_entry *entry;
if (index)
*index = KS_EMPTY_BIN;
/* Set the position at the end of the bin and go backwards. */
req = ksmodel_entry_back_request_alloc(histo, bin, vis_only,
func, val);
if (!req)
return NULL;
if (col && col->size)
entry = kshark_get_collection_entry_back(&req, histo->data,
col, index);
else
entry = kshark_get_entry_back(req, histo->data, index);
kshark_free_entry_request(req);
return entry;
}
static int ksmodel_get_entry_pid(const struct kshark_entry *entry)
{
if (!entry) {
/* No data has been found. */
return KS_EMPTY_BIN;
}
/*
* Note that if some data has been found, but this data is
* filtered-outa, the Dummy entry is returned. The PID of the Dummy
* entry is KS_FILTERED_BIN.
*/
return entry->pid;
}
/**
* @brief In a given bin, start from the front end of the bin and go towards
* the back end, searching for an entry from a given CPU. Return
* the Process Id of the task of the entry found.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param cpu: CPU Id.
* @param vis_only: If true, a visible entry is requested.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns Process Id of the task if an entry has been found. Else a negative
* Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
*/
int ksmodel_get_pid_front(struct kshark_trace_histo *histo,
int bin, int cpu, bool vis_only,
struct kshark_entry_collection *col,
ssize_t *index)
{
const struct kshark_entry *entry;
if (cpu < 0)
return KS_EMPTY_BIN;
entry = ksmodel_get_entry_front(histo, bin, vis_only,
kshark_match_cpu, cpu,
col, index);
return ksmodel_get_entry_pid(entry);
}
/**
* @brief In a given bin, start from the back end of the bin and go towards
* the front end, searching for an entry from a given CPU. Return
* the Process Id of the task of the entry found.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param cpu: CPU Id.
* @param vis_only: If true, a visible entry is requested.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns Process Id of the task if an entry has been found. Else a negative
* Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
*/
int ksmodel_get_pid_back(struct kshark_trace_histo *histo,
int bin, int cpu, bool vis_only,
struct kshark_entry_collection *col,
ssize_t *index)
{
const struct kshark_entry *entry;
if (cpu < 0)
return KS_EMPTY_BIN;
entry = ksmodel_get_entry_back(histo, bin, vis_only,
kshark_match_cpu, cpu,
col, index);
return ksmodel_get_entry_pid(entry);
}
static int ksmodel_get_entry_cpu(const struct kshark_entry *entry)
{
if (!entry) {
/* No data has been found. */
return KS_EMPTY_BIN;
}
/*
* Note that if some data has been found, but this data is
* filtered-outa, the Dummy entry is returned. The CPU Id of the Dummy
* entry is KS_FILTERED_BIN.
*/
return entry->cpu;
}
/**
* @brief In a given bin, start from the front end of the bin and go towards
* the back end, searching for an entry from a given PID. Return
* the CPU Id of the entry found.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param pid: Process Id.
* @param vis_only: If true, a visible entry is requested.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns The CPU Id of the task if an entry has been found. Else a negative
* Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
*/
int ksmodel_get_cpu_front(struct kshark_trace_histo *histo,
int bin, int pid, bool vis_only,
struct kshark_entry_collection *col,
ssize_t *index)
{
const struct kshark_entry *entry;
if (pid < 0)
return KS_EMPTY_BIN;
entry = ksmodel_get_entry_front(histo, bin, vis_only,
kshark_match_pid, pid,
col,
index);
return ksmodel_get_entry_cpu(entry);
}
/**
* @brief In a given bin, start from the back end of the bin and go towards
* the front end, searching for an entry from a given PID. Return
* the CPU Id of the entry found.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param pid: Process Id.
* @param vis_only: If true, a visible entry is requested.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns The CPU Id of the task if an entry has been found. Else a negative
* Identifier (KS_EMPTY_BIN or KS_FILTERED_BIN).
*/
int ksmodel_get_cpu_back(struct kshark_trace_histo *histo,
int bin, int pid, bool vis_only,
struct kshark_entry_collection *col,
ssize_t *index)
{
const struct kshark_entry *entry;
if (pid < 0)
return KS_EMPTY_BIN;
entry = ksmodel_get_entry_back(histo, bin, vis_only,
kshark_match_pid, pid,
col,
index);
return ksmodel_get_entry_cpu(entry);
}
/**
* @brief Check if a visible trace event from a given Cpu exists in this bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param cpu: Cpu Id.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns True, if a visible entry exists in this bin. Else false.
*/
bool ksmodel_cpu_visible_event_exist(struct kshark_trace_histo *histo,
int bin, int cpu,
struct kshark_entry_collection *col,
ssize_t *index)
{
struct kshark_entry_request *req;
const struct kshark_entry *entry;
if (index)
*index = KS_EMPTY_BIN;
/* Set the position at the beginning of the bin and go forward. */
req = ksmodel_entry_front_request_alloc(histo,
bin, true,
kshark_match_cpu, cpu);
if (!req)
return false;
/*
* The default visibility mask of the Model Data request is
* KS_GRAPH_VIEW_FILTER_MASK. Change the mask to
* KS_EVENT_VIEW_FILTER_MASK because we want to find a visible event.
*/
req->vis_mask = KS_EVENT_VIEW_FILTER_MASK;
if (col && col->size)
entry = kshark_get_collection_entry_front(&req, histo->data,
col, index);
else
entry = kshark_get_entry_front(req, histo->data, index);
kshark_free_entry_request(req);
if (!entry || !entry->visible) {
/* No visible entry has been found. */
return false;
}
return true;
}
/**
* @brief Check if a visible trace event from a given Task exists in this bin.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param pid: Process Id of the task.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns True, if a visible entry exists in this bin. Else false.
*/
bool ksmodel_task_visible_event_exist(struct kshark_trace_histo *histo,
int bin, int pid,
struct kshark_entry_collection *col,
ssize_t *index)
{
struct kshark_entry_request *req;
const struct kshark_entry *entry;
if (index)
*index = KS_EMPTY_BIN;
/* Set the position at the beginning of the bin and go forward. */
req = ksmodel_entry_front_request_alloc(histo,
bin, true,
kshark_match_pid, pid);
if (!req)
return false;
/*
* The default visibility mask of the Model Data request is
* KS_GRAPH_VIEW_FILTER_MASK. Change the mask to
* KS_EVENT_VIEW_FILTER_MASK because we want to find a visible event.
*/
req->vis_mask = KS_EVENT_VIEW_FILTER_MASK;
if (col && col->size)
entry = kshark_get_collection_entry_front(&req, histo->data,
col, index);
else
entry = kshark_get_entry_front(req, histo->data, index);
kshark_free_entry_request(req);
if (!entry || !entry->visible) {
/* No visible entry has been found. */
return false;
}
return true;
}
static bool match_cpu_missed_events(struct kshark_context *kshark_ctx,
struct kshark_entry *e, int cpu)
{
return e->event_id == -EOVERFLOW && e->cpu == cpu;
}
static bool match_pid_missed_events(struct kshark_context *kshark_ctx,
struct kshark_entry *e, int pid)
{
return e->event_id == -EOVERFLOW && e->pid == pid;
}
/**
* @brief In a given CPU and bin, start from the front end of the bin and go towards
* the back end, searching for a Missed Events entry.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param cpu: CPU Id.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL.
*/
const struct kshark_entry *
ksmodel_get_cpu_missed_events(struct kshark_trace_histo *histo,
int bin, int cpu,
struct kshark_entry_collection *col,
ssize_t *index)
{
return ksmodel_get_entry_front(histo, bin, true,
match_cpu_missed_events, cpu,
col, index);
}
/**
* @brief In a given task and bin, start from the front end of the bin and go towards
* the back end, searching for a Missed Events entry.
*
* @param histo: Input location for the model descriptor.
* @param bin: Bin id.
* @param pid: Process Id of the task.
* @param col: Optional input location for Data collection.
* @param index: Optional output location for the index of the requested
* entry inside the array.
*
* @returns Pointer ot a kshark_entry, if an entry has been found. Else NULL.
*/
const struct kshark_entry *
ksmodel_get_task_missed_events(struct kshark_trace_histo *histo,
int bin, int pid,
struct kshark_entry_collection *col,
ssize_t *index)
{
return ksmodel_get_entry_front(histo, bin, true,
match_pid_missed_events, pid,
col, index);
}