blob: 44f829d6f36df0a434f07a5b9f40e0f4fefcdb23 [file] [log] [blame]
// SPDX-License-Identifier: LGPL-2.1
/*
* Copyright (C) 2017 VMware Inc, Yordan Karadzhov (VMware) <y.karadz@gmail.com>
*/
/**
* @file libkshark-model.c
* @brief Time series visualization model for tracing data.
*/
// C
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.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.
*/
memset(histo, 0, sizeof(*histo));
}
/**
* @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, int64_t min, int64_t max,
bool force_in_range)
{
int64_t corrected_range, delta_range, range = max - min;
struct kshark_entry *last;
if (n <= 0) {
histo->n_bins = histo->bin_size = 0;
histo->min = min;
histo->max = max;
free(histo->bin_count);
free(histo->map);
return;
}
/* The size of the bin must be >= 1, hence the range must be >= n. */
if (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, int64_t min, int64_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)
{
int64_t time_min, time_max;
size_t next_bin = bin + 1;
ssize_t row;
/* Calculate the beginning and the end of the next bin. */
time_min = histo->min + next_bin * histo->bin_size;
time_max = time_min + 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_max;
/*
* Find the index of the first entry inside
* the next bin (timestamp > time_min).
*/
row = kshark_find_entry_by_time(time_min, histo->data, last_row,
histo->data_size - 1);
if (row < 0 || histo->data[row]->ts >= time_max) {
/* 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;
ssize_t count_tmp = 0;
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 < histo->n_bins) {
++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.
*/
count_tmp = histo->map[i] - histo->map[prev_not_empty];
/*
* We will do a sanity check. The number of data rows
* in the previous not empty bin must be greater than
* zero.
*/
assert(count_tmp > 0);
histo->bin_count[prev_not_empty] = count_tmp;
if (prev_not_empty != LOB(histo))
histo->tot_count += count_tmp;
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.
*/
count_tmp = 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.
*/
count_tmp = histo->map[UOB(histo)] - histo->map[prev_not_empty];
}
/*
* We will do a sanity check. The number of data rows in the last not
* empty bin must be greater than zero.
*/
assert(count_tmp >= 0);
histo->tot_count += histo->bin_count[prev_not_empty] = count_tmp;
}
/**
* @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));
/*
* Calculate only the content of the new (non-overlapping) bins.
* Start from the last copied bin and set the edge of each consecutive
* bin.
*/
bin = histo->n_bins - n - 1;
for (; bin < histo->n_bins - 1; ++bin) {
/*
* Note that this function will set the bin having index
* "bin + 1".
*/
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;
}
/*
* Copy 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));
/* Set the new Lower Overflow bin. */
ksmodel_set_lower_edge(histo);
/* Calculate only the content of the new (non-overlapping) bins. */
for (bin = 0; bin < n - 1; ++bin) {
/*
* Note that this function will set the bin having index
* "bin + 1".
*/
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, int64_t ts)
{
int64_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 && (size_t) (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 sd, int *values)
{
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, sd, values,
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 sd, int *values)
{
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, sd, values,
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 sd: Data stream identifier.
* @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 sd, 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 &&
histo->data[i]->stream_id == sd) {
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 sd: Data stream identifier.
* @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 sd, 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 &&
histo->data[i]->stream_id == sd) {
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 sd: Data stream identifier.
* @param values: Matching condition values, 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 sd, int *values,
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, sd, values);
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 sd: Data stream identifier.
* @param values: Matching condition values, 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 sd, int *values,
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, sd, values);
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 sd: Data stream identifier.
* @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 sd, 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, sd, &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 sd: Data stream identifier.
* @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 sd, 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, sd, &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 sd: Data stream identifier.
* @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 sd, 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, sd, &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 sd: Data stream identifier.
* @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 sd, 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, sd, &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 sd: Data stream identifier.
* @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 sd, 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, sd, &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 sd: Data stream identifier.
* @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 sd, 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, sd, &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 sd, int *cpu)
{
return e->event_id == KS_EVENT_OVERFLOW &&
e->cpu == *cpu && e->stream_id == sd;
}
static bool match_pid_missed_events(struct kshark_context *kshark_ctx,
struct kshark_entry *e, int sd, int *pid)
{
return e->event_id == KS_EVENT_OVERFLOW &&
e->pid == *pid && e->stream_id == sd;
}
/**
* @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 sd: Data stream identifier.
* @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 sd, int cpu,
struct kshark_entry_collection *col,
ssize_t *index)
{
return ksmodel_get_entry_front(histo, bin, true,
match_cpu_missed_events, sd, &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 sd: Data stream identifier.
* @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 sd, int pid,
struct kshark_entry_collection *col,
ssize_t *index)
{
return ksmodel_get_entry_front(histo, bin, true,
match_pid_missed_events, sd, &pid,
col, index);
}
/**
* @brief Find the bin Id of a give entry.
*
* @param histo: Input location for the model descriptor.
* @param entry: Input location for entry.
*
* @returns If the timestamp of the entry is inside the range of the model the
* function returns the Id of the bin it belongs to.
* If the timestamp of the entry is outside of the range of the model
* the function returns UPPER_OVERFLOW_BIN or LOWER_OVERFLOW_BIN
* (negative values).
*/
int ksmodel_get_bin(struct kshark_trace_histo *histo,
const struct kshark_entry *entry)
{
if (entry->ts < histo->min)
return UPPER_OVERFLOW_BIN;
if (entry->ts > histo->max)
return LOWER_OVERFLOW_BIN;
if (entry->ts == histo->max)
return histo->n_bins - 1;
return (entry->ts - histo->min) / histo->bin_size;
}