// SPDX-License-Identifier: GPL-2.0
#include <inttypes.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <linux/compiler.h>
#include "../util/callchain.h"
#include "../util/debug.h"
#include "../util/hist.h"
#include "../util/sort.h"
#include "../util/evsel.h"
#include "../util/evlist.h"
#include "../util/thread.h"
#include "../util/util.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_print(struct perf_hpp *hpp, struct hists *hists, u64 val,
int nr_samples, const char *fmt, int len,
hpp_snprint_fn print_fn, enum perf_hpp_fmt_type fmtype)
{
if (fmtype == PERF_HPP_FMT_TYPE__PERCENT) {
double percent = 0.0;
u64 total = hists__total_period(hists);
if (total)
percent = 100.0 * val / total;
return hpp__call_print_fn(hpp, print_fn, fmt, len, percent);
}
if (fmtype == PERF_HPP_FMT_TYPE__AVERAGE) {
double avg = nr_samples ? (1.0 * val / nr_samples) : 0;
return hpp__call_print_fn(hpp, print_fn, fmt, len, avg);
}
return hpp__call_print_fn(hpp, print_fn, fmt, len, val);
}
struct hpp_fmt_value {
struct hists *hists;
u64 val;
int samples;
};
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, enum perf_hpp_fmt_type fmtype)
{
int ret = 0;
struct hists *hists = he->hists;
struct evsel *evsel = hists_to_evsel(hists);
struct evsel *pos;
char *buf = hpp->buf;
size_t size = hpp->size;
int i = 0, nr_members = 1;
struct hpp_fmt_value *values;
if (evsel__is_group_event(evsel))
nr_members = evsel->core.nr_members;
values = calloc(nr_members, sizeof(*values));
if (values == NULL)
return 0;
values[0].hists = evsel__hists(evsel);
values[0].val = get_field(he);
values[0].samples = he->stat.nr_events;
if (evsel__is_group_event(evsel)) {
struct hist_entry *pair;
for_each_group_member(pos, evsel)
values[++i].hists = evsel__hists(pos);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
for (i = 0; i < nr_members; i++) {
if (values[i].hists != pair->hists)
continue;
values[i].val = get_field(pair);
values[i].samples = pair->stat.nr_events;
break;
}
}
}
for (i = 0; i < nr_members; i++) {
if (symbol_conf.skip_empty &&
values[i].hists->stats.nr_samples == 0)
continue;
ret += __hpp__fmt_print(hpp, values[i].hists, values[i].val,
values[i].samples, fmt, len,
print_fn, fmtype);
}
free(values);
/*
* 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,
enum perf_hpp_fmt_type fmtype)
{
int len = fmt->user_len ?: fmt->len;
if (symbol_conf.field_sep) {
return __hpp__fmt(hpp, he, get_field, fmtstr, 1,
print_fn, fmtype);
}
if (fmtype == PERF_HPP_FMT_TYPE__PERCENT)
len -= 2; /* 2 for a space and a % sign */
else
len -= 1;
return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmtype);
}
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,
enum perf_hpp_fmt_type fmtype)
{
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, fmtype);
}
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 hist_entry__new_pair(struct hist_entry *a, struct hist_entry *b,
hpp_field_fn get_field, int nr_members,
u64 **fields_a, u64 **fields_b)
{
u64 *fa = calloc(nr_members, sizeof(*fa)),
*fb = calloc(nr_members, sizeof(*fb));
struct hist_entry *pair;
if (!fa || !fb)
goto out_free;
list_for_each_entry(pair, &a->pairs.head, pairs.node) {
struct evsel *evsel = hists_to_evsel(pair->hists);
fa[evsel__group_idx(evsel)] = get_field(pair);
}
list_for_each_entry(