// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <math.h>
#include <linux/compiler.h>
#include "../util/callchain.h"
#include "../util/debug.h"
#include "../util/hist.h"
#include "../util/util.h"
#include "../util/sort.h"
#include "../util/evsel.h"
#include "../util/evlist.h"
#include "../perf.h"
/* hist period print (hpp) functions */
#define hpp__call_print_fn(hpp, fn, fmt, ...) \
({ \
int __ret = fn(hpp, fmt, ##__VA_ARGS__); \
advance_hpp(hpp, __ret); \
__ret; \
})
static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he,
hpp_field_fn get_field, const char *fmt, int len,
hpp_snprint_fn print_fn, bool fmt_percent)
{
int ret;
struct hists *hists = he->hists;
struct evsel *evsel = hists_to_evsel(hists);
char *buf = hpp->buf;
size_t size = hpp->size;
if (fmt_percent) {
double percent = 0.0;
u64 total = hists__total_period(hists);
if (total)
percent = 100.0 * get_field(he) / total;
ret = hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
} else
ret = hpp__call_print_fn(hpp, print_fn, fmt, len, get_field(he));
if (perf_evsel__is_group_event(evsel)) {
int prev_idx, idx_delta;
struct hist_entry *pair;
int nr_members = evsel->core.nr_members;
prev_idx = perf_evsel__group_idx(evsel);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
u64 period = get_field(pair);
u64 total = hists__total_period(pair->hists);
if (!total)
continue;
evsel = hists_to_evsel(pair->hists);
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members in the middle which
* have no sample
*/
if (fmt_percent) {
ret += hpp__call_print_fn(hpp, print_fn,
fmt, len, 0.0);
} else {
ret += hpp__call_print_fn(hpp, print_fn,
fmt, len, 0ULL);
}
}
if (fmt_percent) {
ret += hpp__call_print_fn(hpp, print_fn, fmt, len,
100.0 * period / total);
} else {
ret += hpp__call_print_fn(hpp, print_fn, fmt,
len, period);
}
prev_idx = perf_evsel__group_idx(evsel);
}
idx_delta = nr_members - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members at last which have no sample
*/
if (fmt_percent) {
ret += hpp__call_print_fn(hpp, print_fn,
fmt, len, 0.0);
} else {
ret += hpp__call_print_fn(hpp, print_fn,
fmt, len, 0ULL);
}
}
}
/*
* Restore original buf and size as it's where caller expects
* the result will be saved.
*/
hpp->buf = buf;
hpp->size = size;
return ret;
}
int hpp__fmt(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he, hpp_field_fn get_field,
const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
{
int len = fmt->user_len ?: fmt->len;
if (symbol_conf.field_sep) {
return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
print_fn, fmt_percent);
}
if (fmt_percent)
len -= 2; /* 2 for a space and a % sign */
else
len -= 1;
return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmt_percent);
}
int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
struct hist_entry *he, hpp_field_fn get_field,
const char *fmtstr, hpp_snprint_fn print_fn, bool fmt_percent)
{
if (!symbol_conf.cumulate_callchain) {
int len = fmt->user_len ?: fmt->len;
return snprintf(hpp->buf, hpp->size, " %*s", len - 1, "N/A");
}
return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmt_percent);
}
static int field_cmp(u64 field_a, u64 field_b)
{
if (field_a > field_b)
return 1;
if (field_a < field_b)
return -1;
return 0;
}
static int __hpp__sort(struct hist_entry *a, struct hist_entry *b,
hpp_field_fn get_field)
{
s64 ret;
int i, nr_members;
struct evsel *evsel;
struct hist_entry *pair;
u64 *fields_a, *fields_b;
ret = field_cmp(get_field(a), get_field(b));
if (ret