diff options
Diffstat (limited to 'tools/perf/ui/hist.c')
| -rw-r--r-- | tools/perf/ui/hist.c | 1404 |
1 files changed, 1082 insertions, 322 deletions
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 4bf91b09d62d..e58327595d37 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -1,486 +1,1037 @@ +// SPDX-License-Identifier: GPL-2.0 +#include <errno.h> +#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/util.h" #include "../util/sort.h" #include "../util/evsel.h" +#include "../util/evlist.h" +#include "../util/mem-events.h" +#include "../util/string2.h" +#include "../util/thread.h" +#include "../util/util.h" /* hist period print (hpp) functions */ -typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); +#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, - u64 (*get_field)(struct hist_entry *), - const char *fmt, hpp_snprint_fn print_fn, - bool fmt_percent) +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) { - int ret; - struct hists *hists = he->hists; - struct perf_evsel *evsel = hists_to_evsel(hists); - - if (fmt_percent) { + if (fmtype == PERF_HPP_FMT_TYPE__PERCENT || fmtype == PERF_HPP_FMT_TYPE__LATENCY) { double percent = 0.0; + u64 total = fmtype == PERF_HPP_FMT_TYPE__PERCENT ? hists__total_period(hists) : + hists__total_latency(hists); - if (hists->stats.total_period) - percent = 100.0 * get_field(he) / - hists->stats.total_period; + if (total) + percent = 100.0 * val / total; - ret = print_fn(hpp->buf, hpp->size, fmt, percent); - } else - ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he)); + return hpp__call_print_fn(hpp, print_fn, fmt, len, percent); + } - if (perf_evsel__is_group_event(evsel)) { - int prev_idx, idx_delta; - struct hist_entry *pair; - int nr_members = evsel->nr_members; + if (fmtype == PERF_HPP_FMT_TYPE__AVERAGE) { + double avg = nr_samples ? (1.0 * val / nr_samples) : 0; - prev_idx = perf_evsel__group_idx(evsel); + return hpp__call_print_fn(hpp, print_fn, fmt, len, avg); + } - list_for_each_entry(pair, &he->pairs.head, pairs.node) { - u64 period = get_field(pair); - u64 total = pair->hists->stats.total_period; + return hpp__call_print_fn(hpp, print_fn, fmt, len, val); +} - if (!total) - continue; +struct hpp_fmt_value { + struct hists *hists; + u64 val; + int samples; +}; - evsel = hists_to_evsel(pair->hists); - idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1; +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; - while (idx_delta--) { - /* - * zero-fill group members in the middle which - * have no sample - */ - ret += print_fn(hpp->buf + ret, hpp->size - ret, - fmt, 0); - } + values[0].hists = evsel__hists(evsel); + values[0].val = get_field(he); + values[0].samples = he->stat.nr_events; - if (fmt_percent) - ret += print_fn(hpp->buf + ret, hpp->size - ret, - fmt, 100.0 * period / total); - else - ret += print_fn(hpp->buf + ret, hpp->size - ret, - fmt, period); + if (evsel__is_group_event(evsel)) { + struct hist_entry *pair; - prev_idx = perf_evsel__group_idx(evsel); - } + for_each_group_member(pos, evsel) + values[++i].hists = evsel__hists(pos); - idx_delta = nr_members - prev_idx - 1; + list_for_each_entry(pair, &he->pairs.head, pairs.node) { + for (i = 0; i < nr_members; i++) { + if (values[i].hists != pair->hists) + continue; - while (idx_delta--) { - /* - * zero-fill group members at last which have no sample - */ - ret += print_fn(hpp->buf + ret, hpp->size - ret, - fmt, 0); + 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; } -#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ -static int hpp__header_##_type(struct perf_hpp *hpp) \ -{ \ - int len = _min_width; \ - \ - if (symbol_conf.event_group) { \ - struct perf_evsel *evsel = hpp->ptr; \ - \ - len = max(len, evsel->nr_members * _unit_width); \ - } \ - return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \ -} - -#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \ -static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \ -{ \ - int len = _min_width; \ - \ - if (symbol_conf.event_group) { \ - struct perf_evsel *evsel = hpp->ptr; \ - \ - len = max(len, evsel->nr_members * _unit_width); \ - } \ - return len; \ -} - -#define __HPP_COLOR_PERCENT_FN(_type, _field) \ -static u64 he_get_##_field(struct hist_entry *he) \ -{ \ - return he->stat._field; \ -} \ - \ -static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ -{ \ - return __hpp__fmt(hpp, he, he_get_##_field, " %6.2f%%", \ - (hpp_snprint_fn)percent_color_snprintf, true); \ -} +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 = max(fmt->user_len ?: fmt->len, (int)strlen(fmt->name)); -#define __HPP_ENTRY_PERCENT_FN(_type, _field) \ -static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ -{ \ - const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \ - return __hpp__fmt(hpp, he, he_get_##_field, fmt, \ - scnprintf, true); \ + if (symbol_conf.field_sep) { + return __hpp__fmt(hpp, he, get_field, fmtstr, 1, + print_fn, fmtype); + } + + if (fmtype == PERF_HPP_FMT_TYPE__PERCENT || fmtype == PERF_HPP_FMT_TYPE__LATENCY) + len -= 2; /* 2 for a space and a % sign */ + else + len -= 1; + + return __hpp__fmt(hpp, he, get_field, fmtstr, len, print_fn, fmtype); } -#define __HPP_ENTRY_RAW_FN(_type, _field) \ -static u64 he_get_raw_##_field(struct hist_entry *he) \ -{ \ - return he->stat._field; \ -} \ - \ -static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \ -{ \ - const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \ - return __hpp__fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf, false); \ +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); } -#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \ -__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ -__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ -__HPP_COLOR_PERCENT_FN(_type, _field) \ -__HPP_ENTRY_PERCENT_FN(_type, _field) +int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *hpp, + struct hist_entry *he, enum mem_stat_type mst, + const char *fmtstr, hpp_snprint_fn print_fn) +{ + struct hists *hists = he->hists; + int mem_stat_idx = -1; + char *buf = hpp->buf; + size_t size = hpp->size; + u64 total = 0; + int ret = 0; + + for (int i = 0; i < hists->nr_mem_stats; i++) { + if (hists->mem_stat_types[i] == mst) { + mem_stat_idx = i; + break; + } + } + assert(mem_stat_idx != -1); -#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \ -__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \ -__HPP_WIDTH_FN(_type, _min_width, _unit_width) \ -__HPP_ENTRY_RAW_FN(_type, _field) + for (int i = 0; i < MEM_STAT_LEN; i++) + total += hists->mem_stat_total[mem_stat_idx].entries[i]; + assert(total != 0); + for (int i = 0; i < MEM_STAT_LEN; i++) { + u64 val = he->mem_stat[mem_stat_idx].entries[i]; -HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) -HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) -HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) -HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) -HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) + if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0) + continue; -HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) -HPP_RAW_FNS(period, "Period", period, 12, 12) + ret += hpp__call_print_fn(hpp, print_fn, fmtstr, 100.0 * val / total); + } + /* + * Restore original buf and size as it's where caller expects + * the result will be saved. + */ + hpp->buf = buf; + hpp->size = size; -static int hpp__header_baseline(struct perf_hpp *hpp) -{ - return scnprintf(hpp->buf, hpp->size, "Baseline"); + return ret; } -static int hpp__width_baseline(struct perf_hpp *hpp __maybe_unused) +static int field_cmp(u64 field_a, u64 field_b) { - return 8; + if (field_a > field_b) + return 1; + if (field_a < field_b) + return -1; + return 0; } -static double baseline_percent(struct hist_entry *he) +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) { - struct hist_entry *pair = hist_entry__next_pair(he); - struct hists *pair_hists = pair ? pair->hists : NULL; - double percent = 0.0; + u64 *fa = calloc(nr_members, sizeof(*fa)), + *fb = calloc(nr_members, sizeof(*fb)); + struct hist_entry *pair; - if (pair) { - u64 total_period = pair_hists->stats.total_period; - u64 base_period = pair->stat.period; + if (!fa || !fb) + goto out_free; - percent = 100.0 * base_period / total_period; + 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); } - return percent; + list_for_each_entry(pair, &b->pairs.head, pairs.node) { + struct evsel *evsel = hists_to_evsel(pair->hists); + fb[evsel__group_idx(evsel)] = get_field(pair); + } + + *fields_a = fa; + *fields_b = fb; + return 0; +out_free: + free(fa); + free(fb); + *fields_a = *fields_b = NULL; + return -1; } -static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) +static int __hpp__group_sort_idx(struct hist_entry *a, struct hist_entry *b, + hpp_field_fn get_field, int idx) { - double percent = baseline_percent(he); + struct evsel *evsel = hists_to_evsel(a->hists); + u64 *fields_a, *fields_b; + int cmp, nr_members, ret, i; + + cmp = field_cmp(get_field(a), get_field(b)); + if (!evsel__is_group_event(evsel)) + return cmp; + + nr_members = evsel->core.nr_members; + if (idx < 1 || idx >= nr_members) + return cmp; + + ret = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b); + if (ret) { + ret = cmp; + goto out; + } - if (hist_entry__has_pairs(he) || symbol_conf.field_sep) - return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); - else - return scnprintf(hpp->buf, hpp->size, " "); + ret = field_cmp(fields_a[idx], fields_b[idx]); + if (ret) + goto out; + + for (i = 1; i < nr_members; i++) { + if (i != idx) { + ret = field_cmp(fields_a[i], fields_b[i]); + if (ret) + goto out; + } + } + +out: + free(fields_a); + free(fields_b); + + return ret; } -static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) +static int __hpp__sort(struct hist_entry *a, struct hist_entry *b, + hpp_field_fn get_field) { - double percent = baseline_percent(he); - const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%"; + s64 ret; + int i, nr_members; + struct evsel *evsel; + u64 *fields_a, *fields_b; + + if (symbol_conf.group_sort_idx && symbol_conf.event_group) { + return __hpp__group_sort_idx(a, b, get_field, + symbol_conf.group_sort_idx); + } - if (hist_entry__has_pairs(he) || symbol_conf.field_sep) - return scnprintf(hpp->buf, hpp->size, fmt, percent); - else - return scnprintf(hpp->buf, hpp->size, " "); + ret = field_cmp(get_field(a), get_field(b)); + if (ret || !symbol_conf.event_group) + return ret; + + evsel = hists_to_evsel(a->hists); + if (!evsel__is_group_event(evsel)) + return ret; + + nr_members = evsel->core.nr_members; + i = hist_entry__new_pair(a, b, get_field, nr_members, &fields_a, &fields_b); + if (i) + goto out; + + for (i = 1; i < nr_members; i++) { + ret = field_cmp(fields_a[i], fields_b[i]); + if (ret) + break; + } + +out: + free(fields_a); + free(fields_b); + + return ret; } -static int hpp__header_period_baseline(struct perf_hpp *hpp) +static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, + hpp_field_fn get_field) { - const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; + s64 ret = 0; - return scnprintf(hpp->buf, hpp->size, fmt, "Period Base"); + if (symbol_conf.cumulate_callchain) { + /* + * Put caller above callee when they have equal period. + */ + ret = field_cmp(get_field(a), get_field(b)); + if (ret) + return ret; + + if ((a->thread == NULL ? NULL : RC_CHK_ACCESS(a->thread)) != + (b->thread == NULL ? NULL : RC_CHK_ACCESS(b->thread)) || + !hist_entry__has_callchains(a) || !symbol_conf.use_callchain) + return 0; + + ret = b->callchain->max_depth - a->callchain->max_depth; + if (callchain_param.order == ORDER_CALLER) + ret = -ret; + } + return ret; } -static int hpp__width_period_baseline(struct perf_hpp *hpp __maybe_unused) +static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt); + +static enum mem_stat_type hpp__mem_stat_type(struct perf_hpp_fmt *fmt) { - return 12; + if (!perf_hpp__is_mem_stat_entry(fmt)) + return -1; + + switch (fmt->idx) { + case PERF_HPP__MEM_STAT_OP: + return PERF_MEM_STAT_OP; + case PERF_HPP__MEM_STAT_CACHE: + return PERF_MEM_STAT_CACHE; + case PERF_HPP__MEM_STAT_MEMORY: + return PERF_MEM_STAT_MEMORY; + case PERF_HPP__MEM_STAT_SNOOP: + return PERF_MEM_STAT_SNOOP; + case PERF_HPP__MEM_STAT_DTLB: + return PERF_MEM_STAT_DTLB; + default: + break; + } + pr_debug("Should not reach here\n"); + return -1; } -static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *he) +static int64_t hpp__sort_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *a, struct hist_entry *b) { - struct hist_entry *pair = hist_entry__next_pair(he); - u64 period = pair ? pair->stat.period : 0; - const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64; - - return scnprintf(hpp->buf, hpp->size, fmt, period); + return a->stat.period - b->stat.period; } -static int hpp__header_delta(struct perf_hpp *hpp) +static int hpp__width_fn(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp __maybe_unused, + struct hists *hists) { - const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; + int len = fmt->user_len ?: fmt->len; + struct evsel *evsel = hists_to_evsel(hists); + + if (symbol_conf.event_group) { + int nr = 0; + struct evsel *pos; + + for_each_group_evsel(pos, evsel) { + if (!symbol_conf.skip_empty || + evsel__hists(pos)->stats.nr_samples) + nr++; + } + + len = max(len, nr * fmt->len); + } - return scnprintf(hpp->buf, hpp->size, fmt, "Delta"); + if (len < (int)strlen(fmt->name)) + len = strlen(fmt->name); + + return len; } -static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused) +static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hists *hists, int line, + int *span __maybe_unused) { - return 7; + int len = hpp__width_fn(fmt, hpp, hists); + const char *hdr = ""; + + if (line == hists->hpp_list->nr_header_lines - 1) + hdr = fmt->name; + + return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr); } -static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he) +static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hists *hists, int line, + int *span __maybe_unused) { - struct hist_entry *pair = hist_entry__next_pair(he); - const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s"; - char buf[32] = " "; - double diff = 0.0; + char *buf = hpp->buf; + int ret = 0; + int len; + enum mem_stat_type mst = hpp__mem_stat_type(fmt); + int mem_stat_idx = -1; + + for (int i = 0; i < hists->nr_mem_stats; i++) { + if (hists->mem_stat_types[i] == mst) { + mem_stat_idx = i; + break; + } + } + assert(mem_stat_idx != -1); - if (pair) { - if (he->diff.computed) - diff = he->diff.period_ratio_delta; - else - diff = perf_diff__compute_delta(he, pair); - } else - diff = perf_diff__period_percent(he, he->stat.period); + if (line == 0) { + int left, right; - if (fabs(diff) >= 0.01) - scnprintf(buf, sizeof(buf), "%+4.2F%%", diff); + len = 0; + /* update fmt->len for acutally used columns only */ + for (int i = 0; i < MEM_STAT_LEN; i++) { + if (hists->mem_stat_total[mem_stat_idx].entries[i]) + len += MEM_STAT_PRINT_LEN; + } + fmt->len = len; - return scnprintf(hpp->buf, hpp->size, fmt, buf); -} + /* print header directly if single column only */ + if (len == MEM_STAT_PRINT_LEN) + return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name); -static int hpp__header_ratio(struct perf_hpp *hpp) -{ - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; + left = (len - strlen(fmt->name)) / 2 - 1; + right = len - left - strlen(fmt->name) - 2; - return scnprintf(hpp->buf, hpp->size, fmt, "Ratio"); + if (left < 0) + left = 0; + if (right < 0) + right = 0; + + return scnprintf(hpp->buf, hpp->size, "%.*s %s %.*s", + left, graph_dotted_line, fmt->name, right, graph_dotted_line); + } + + + len = hpp->size; + for (int i = 0; i < MEM_STAT_LEN; i++) { + int printed; + + if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0) + continue; + + printed = scnprintf(buf, len, "%*s", MEM_STAT_PRINT_LEN, + mem_stat_name(mst, i)); + ret += printed; + buf += printed; + len -= printed; + } + return ret; } -static int hpp__width_ratio(struct perf_hpp *hpp __maybe_unused) +int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) { - return 14; + va_list args; + ssize_t ssize = hpp->size; + double percent; + int ret, len; + + va_start(args, fmt); + len = va_arg(args, int); + percent = va_arg(args, double); + ret = percent_color_len_snprintf(hpp->buf, hpp->size, fmt, len, percent); + va_end(args); + + return (ret >= ssize) ? (ssize - 1) : ret; } -static int hpp__entry_ratio(struct perf_hpp *hpp, struct hist_entry *he) +static int hpp_entry_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) { - struct hist_entry *pair = hist_entry__next_pair(he); - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; - char buf[32] = " "; - double ratio = 0.0; + va_list args; + ssize_t ssize = hpp->size; + int ret; - if (pair) { - if (he->diff.computed) - ratio = he->diff.period_ratio; - else - ratio = perf_diff__compute_ratio(he, pair); - } + va_start(args, fmt); + ret = vsnprintf(hpp->buf, hpp->size, fmt, args); + va_end(args); - if (ratio > 0.0) - scnprintf(buf, sizeof(buf), "%+14.6F", ratio); + return (ret >= ssize) ? (ssize - 1) : ret; +} - return scnprintf(hpp->buf, hpp->size, fmt, buf); +#define __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \ +static u64 he_get_##_field(struct hist_entry *he) \ +{ \ + return he->stat._field; \ +} \ + \ +static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ + hpp_color_scnprintf, _fmttype); \ } -static int hpp__header_wdiff(struct perf_hpp *hpp) -{ - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; +#define __HPP_ENTRY_PERCENT_FN(_type, _field, _fmttype) \ +static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.2f%%", \ + hpp_entry_scnprintf, _fmttype); \ +} - return scnprintf(hpp->buf, hpp->size, fmt, "Weighted diff"); +#define __HPP_SORT_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ + struct hist_entry *a, struct hist_entry *b) \ +{ \ + return __hpp__sort(a, b, he_get_##_field); \ } -static int hpp__width_wdiff(struct perf_hpp *hpp __maybe_unused) -{ - return 14; +#define __HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \ +static u64 he_get_acc_##_field(struct hist_entry *he) \ +{ \ + return he->stat_acc->_field; \ +} \ + \ +static int hpp__color_##_type(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ + hpp_color_scnprintf, _fmttype); \ } -static int hpp__entry_wdiff(struct perf_hpp *hpp, struct hist_entry *he) -{ - struct hist_entry *pair = hist_entry__next_pair(he); - const char *fmt = symbol_conf.field_sep ? "%s" : "%14s"; - char buf[32] = " "; - s64 wdiff = 0; +#define __HPP_ENTRY_ACC_PERCENT_FN(_type, _field, _fmttype) \ +static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return hpp__fmt_acc(fmt, hpp, he, he_get_acc_##_field, " %*.2f%%", \ + hpp_entry_scnprintf, _fmttype); \ +} - if (pair) { - if (he->diff.computed) - wdiff = he->diff.wdiff; - else - wdiff = perf_diff__compute_wdiff(he, pair); - } +#define __HPP_SORT_ACC_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ + struct hist_entry *a, struct hist_entry *b) \ +{ \ + return __hpp__sort_acc(a, b, he_get_acc_##_field); \ +} - if (wdiff != 0) - scnprintf(buf, sizeof(buf), "%14ld", wdiff); +#define __HPP_ENTRY_RAW_FN(_type, _field) \ +static u64 he_get_raw_##_field(struct hist_entry *he) \ +{ \ + return he->stat._field; \ +} \ + \ +static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return hpp__fmt(fmt, hpp, he, he_get_raw_##_field, " %*"PRIu64, \ + hpp_entry_scnprintf, PERF_HPP_FMT_TYPE__RAW); \ +} + +#define __HPP_SORT_RAW_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ + struct hist_entry *a, struct hist_entry *b) \ +{ \ + return __hpp__sort(a, b, he_get_raw_##_field); \ +} + +#define __HPP_ENTRY_AVERAGE_FN(_type, _field) \ +static u64 he_get_##_field(struct hist_entry *he) \ +{ \ + return he->stat._field; \ +} \ + \ +static int hpp__entry_##_type(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, struct hist_entry *he) \ +{ \ + return hpp__fmt(fmt, hpp, he, he_get_##_field, " %*.1f", \ + hpp_entry_scnprintf, PERF_HPP_FMT_TYPE__AVERAGE); \ +} + +#define __HPP_SORT_AVERAGE_FN(_type, _field) \ +static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ + struct hist_entry *a, struct hist_entry *b) \ +{ \ + return __hpp__sort(a, b, he_get_##_field); \ +} - return scnprintf(hpp->buf, hpp->size, fmt, buf); +#define __HPP_COLOR_MEM_STAT_FN(_name, _type) \ +static int hpp__color_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, \ + struct hist_entry *he) \ +{ \ + return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ + " %5.1f%%", hpp_color_scnprintf); \ +} + +#define __HPP_ENTRY_MEM_STAT_FN(_name, _type) \ +static int hpp__entry_mem_stat_##_name(struct perf_hpp_fmt *fmt, \ + struct perf_hpp *hpp, \ + struct hist_entry *he) \ +{ \ + return hpp__fmt_mem_stat(fmt, hpp, he, PERF_MEM_STAT_##_type, \ + " %5.1f%%", hpp_entry_scnprintf); \ } -static int hpp__header_formula(struct perf_hpp *hpp) +#define HPP_PERCENT_FNS(_type, _field, _fmttype) \ +__HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \ +__HPP_ENTRY_PERCENT_FN(_type, _field, _fmttype) \ +__HPP_SORT_FN(_type, _field) + +#define HPP_PERCENT_ACC_FNS(_type, _field, _fmttype) \ +__HPP_COLOR_ACC_PERCENT_FN(_type, _field, _fmttype) \ +__HPP_ENTRY_ACC_PERCENT_FN(_type, _field, _fmttype) \ +__HPP_SORT_ACC_FN(_type, _field) + +#define HPP_RAW_FNS(_type, _field) \ +__HPP_ENTRY_RAW_FN(_type, _field) \ +__HPP_SORT_RAW_FN(_type, _field) + +#define HPP_AVERAGE_FNS(_type, _field) \ +__HPP_ENTRY_AVERAGE_FN(_type, _field) \ +__HPP_SORT_AVERAGE_FN(_type, _field) + +#define HPP_MEM_STAT_FNS(_name, _type) \ +__HPP_COLOR_MEM_STAT_FN(_name, _type) \ +__HPP_ENTRY_MEM_STAT_FN(_name, _type) + +HPP_PERCENT_FNS(overhead, period, PERF_HPP_FMT_TYPE__PERCENT) +HPP_PERCENT_FNS(latency, latency, PERF_HPP_FMT_TYPE__LATENCY) +HPP_PERCENT_FNS(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT) +HPP_PERCENT_FNS(overhead_us, period_us, PERF_HPP_FMT_TYPE__PERCENT) +HPP_PERCENT_FNS(overhead_guest_sys, period_guest_sys, PERF_HPP_FMT_TYPE__PERCENT) +HPP_PERCENT_FNS(overhead_guest_us, period_guest_us, PERF_HPP_FMT_TYPE__PERCENT) +HPP_PERCENT_ACC_FNS(overhead_acc, period, PERF_HPP_FMT_TYPE__PERCENT) +HPP_PERCENT_ACC_FNS(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY) + +HPP_RAW_FNS(samples, nr_events) +HPP_RAW_FNS(period, period) + +HPP_AVERAGE_FNS(weight1, weight1) +HPP_AVERAGE_FNS(weight2, weight2) +HPP_AVERAGE_FNS(weight3, weight3) + +HPP_MEM_STAT_FNS(op, OP) +HPP_MEM_STAT_FNS(cache, CACHE) +HPP_MEM_STAT_FNS(memory, MEMORY) +HPP_MEM_STAT_FNS(snoop, SNOOP) +HPP_MEM_STAT_FNS(dtlb, DTLB) + +static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *a __maybe_unused, + struct hist_entry *b __maybe_unused) { - const char *fmt = symbol_conf.field_sep ? "%s" : "%70s"; + return 0; +} - return scnprintf(hpp->buf, hpp->size, fmt, "Formula"); +static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt) +{ + return fmt->sort == hpp__sort_mem_stat; } -static int hpp__width_formula(struct perf_hpp *hpp __maybe_unused) +static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) { - return 70; + return a->header == hpp__header_fn; } -static int hpp__entry_formula(struct perf_hpp *hpp, struct hist_entry *he) +static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) { - struct hist_entry *pair = hist_entry__next_pair(he); - const char *fmt = symbol_conf.field_sep ? "%s" : "%-70s"; - char buf[96] = " "; + if (!perf_hpp__is_hpp_entry(a) || !perf_hpp__is_hpp_entry(b)) + return false; + + return a->idx == b->idx; +} - if (pair) - perf_diff__formula(he, pair, buf, sizeof(buf)); +static bool hpp__equal_mem_stat(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) +{ + if (!perf_hpp__is_mem_stat_entry(a) || !perf_hpp__is_mem_stat_entry(b)) + return false; - return scnprintf(hpp->buf, hpp->size, fmt, buf); + return a->entry == b->entry; } -#define HPP__COLOR_PRINT_FNS(_name) \ +#define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ + { \ + .name = _name, \ + .header = hpp__header_fn, \ + .width = hpp__width_fn, \ + .color = hpp__color_ ## _fn, \ + .entry = hpp__entry_ ## _fn, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_ ## _fn, \ + .idx = PERF_HPP__ ## _idx, \ + .equal = hpp__equal, \ + } + +#define HPP__COLOR_ACC_PRINT_FNS(_name, _fn, _idx) \ + { \ + .name = _name, \ + .header = hpp__header_fn, \ + .width = hpp__width_fn, \ + .color = hpp__color_ ## _fn, \ + .entry = hpp__entry_ ## _fn, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_ ## _fn, \ + .idx = PERF_HPP__ ## _idx, \ + .equal = hpp__equal, \ + } + +#define HPP__PRINT_FNS(_name, _fn, _idx) \ { \ - .header = hpp__header_ ## _name, \ - .width = hpp__width_ ## _name, \ - .color = hpp__color_ ## _name, \ - .entry = hpp__entry_ ## _name \ + .name = _name, \ + .header = hpp__header_fn, \ + .width = hpp__width_fn, \ + .entry = hpp__entry_ ## _fn, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_ ## _fn, \ + .idx = PERF_HPP__ ## _idx, \ + .equal = hpp__equal, \ } -#define HPP__PRINT_FNS(_name) \ +#define HPP__MEM_STAT_PRINT_FNS(_name, _fn, _type) \ { \ - .header = hpp__header_ ## _name, \ - .width = hpp__width_ ## _name, \ - .entry = hpp__entry_ ## _name \ + .name = _name, \ + .header = hpp__header_mem_stat_fn, \ + .width = hpp__width_fn, \ + .color = hpp__color_mem_stat_ ## _fn, \ + .entry = hpp__entry_mem_stat_ ## _fn, \ + .cmp = hpp__nop_cmp, \ + .collapse = hpp__nop_cmp, \ + .sort = hpp__sort_mem_stat, \ + .idx = PERF_HPP__MEM_STAT_ ## _type, \ + .equal = hpp__equal_mem_stat, \ } struct perf_hpp_fmt perf_hpp__format[] = { - HPP__COLOR_PRINT_FNS(baseline), - HPP__COLOR_PRINT_FNS(overhead), - HPP__COLOR_PRINT_FNS(overhead_sys), - HPP__COLOR_PRINT_FNS(overhead_us), - HPP__COLOR_PRINT_FNS(overhead_guest_sys), - HPP__COLOR_PRINT_FNS(overhead_guest_us), - HPP__PRINT_FNS(samples), - HPP__PRINT_FNS(period), - HPP__PRINT_FNS(period_baseline), - HPP__PRINT_FNS(delta), - HPP__PRINT_FNS(ratio), - HPP__PRINT_FNS(wdiff), - HPP__PRINT_FNS(formula) + HPP__COLOR_PRINT_FNS("Overhead", overhead, OVERHEAD), + HPP__COLOR_PRINT_FNS("Latency", latency, LATENCY), + HPP__COLOR_PRINT_FNS("sys", overhead_sys, OVERHEAD_SYS), + HPP__COLOR_PRINT_FNS("usr", overhead_us, OVERHEAD_US), + HPP__COLOR_PRINT_FNS("guest sys", overhead_guest_sys, OVERHEAD_GUEST_SYS), + HPP__COLOR_PRINT_FNS("guest usr", overhead_guest_us, OVERHEAD_GUEST_US), + HPP__COLOR_ACC_PRINT_FNS("Children", overhead_acc, OVERHEAD_ACC), + HPP__COLOR_ACC_PRINT_FNS("Latency", latency_acc, LATENCY_ACC), + HPP__PRINT_FNS("Samples", samples, SAMPLES), + HPP__PRINT_FNS("Period", period, PERIOD), + HPP__PRINT_FNS("Weight1", weight1, WEIGHT1), + HPP__PRINT_FNS("Weight2", weight2, WEIGHT2), + HPP__PRINT_FNS("Weight3", weight3, WEIGHT3), + HPP__MEM_STAT_PRINT_FNS("Mem Op", op, OP), + HPP__MEM_STAT_PRINT_FNS("Cache", cache, CACHE), + HPP__MEM_STAT_PRINT_FNS("Memory", memory, MEMORY), + HPP__MEM_STAT_PRINT_FNS("Snoop", snoop, SNOOP), + HPP__MEM_STAT_PRINT_FNS("D-TLB", dtlb, DTLB), }; -LIST_HEAD(perf_hpp__list); - +struct perf_hpp_list perf_hpp_list = { + .fields = LIST_HEAD_INIT(perf_hpp_list.fields), + .sorts = LIST_HEAD_INIT(perf_hpp_list.sorts), + .nr_header_lines = 1, +}; #undef HPP__COLOR_PRINT_FNS +#undef HPP__COLOR_ACC_PRINT_FNS #undef HPP__PRINT_FNS +#undef HPP__MEM_STAT_PRINT_FNS #undef HPP_PERCENT_FNS +#undef HPP_PERCENT_ACC_FNS #undef HPP_RAW_FNS +#undef HPP_AVERAGE_FNS +#undef HPP_MEM_STAT_FNS #undef __HPP_HEADER_FN #undef __HPP_WIDTH_FN #undef __HPP_COLOR_PERCENT_FN #undef __HPP_ENTRY_PERCENT_FN +#undef __HPP_COLOR_ACC_PERCENT_FN +#undef __HPP_ENTRY_ACC_PERCENT_FN #undef __HPP_ENTRY_RAW_FN +#undef __HPP_ENTRY_AVERAGE_FN +#undef __HPP_COLOR_MEM_STAT_FN +#undef __HPP_ENTRY_MEM_STAT_FN + +#undef __HPP_SORT_FN +#undef __HPP_SORT_ACC_FN +#undef __HPP_SORT_RAW_FN +#undef __HPP_SORT_AVERAGE_FN + +static void fmt_free(struct perf_hpp_fmt *fmt) +{ + /* + * At this point fmt should be completely + * unhooked, if not it's a bug. + */ + BUG_ON(!list_empty(&fmt->list)); + BUG_ON(!list_empty(&fmt->sort_list)); + + if (fmt->free) + fmt->free(fmt); +} +static bool fmt_equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) +{ + return a->equal && a->equal(a, b); +} void perf_hpp__init(void) { + int i; + + for (i = 0; i < PERF_HPP__MAX_INDEX; i++) { + struct perf_hpp_fmt *fmt = &perf_hpp__format[i]; + + INIT_LIST_HEAD(&fmt->list); + + /* sort_list may be linked by setup_sorting() */ + if (fmt->sort_list.next == NULL) + INIT_LIST_HEAD(&fmt->sort_list); + } + + /* + * If user specified field order, no need to setup default fields. + */ + if (is_strict_order(field_order)) + return; + + /* + * Overhead and latency columns are added in setup_overhead(), + * so they are added implicitly here only if they were added + * by setup_overhead() before (have was_taken flag set). + * This is required because setup_overhead() has more complex + * logic, in particular it does not add "overhead" if user + * specified "latency" in sort order, and vise versa. + */ + if (symbol_conf.cumulate_callchain) { + /* + * Addition of fields is idempotent, so we add latency + * column twice to get desired order with simpler logic. + */ + if (symbol_conf.prefer_latency) + hpp_dimension__add_output(PERF_HPP__LATENCY_ACC, true); + hpp_dimension__add_output(PERF_HPP__OVERHEAD_ACC, true); + if (symbol_conf.enable_latency) + hpp_dimension__add_output(PERF_HPP__LATENCY_ACC, true); + perf_hpp__format[PERF_HPP__OVERHEAD].name = "Self"; + } + + if (symbol_conf.prefer_latency) + hpp_dimension__add_output(PERF_HPP__LATENCY, true); + hpp_dimension__add_output(PERF_HPP__OVERHEAD, true); + if (symbol_conf.enable_latency) + hpp_dimension__add_output(PERF_HPP__LATENCY, true); + if (symbol_conf.show_cpu_utilization) { - perf_hpp__column_enable(PERF_HPP__OVERHEAD_SYS); - perf_hpp__column_enable(PERF_HPP__OVERHEAD_US); + hpp_dimension__add_output(PERF_HPP__OVERHEAD_SYS, false); + hpp_dimension__add_output(PERF_HPP__OVERHEAD_US, false); if (perf_guest) { - perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_SYS); - perf_hpp__column_enable(PERF_HPP__OVERHEAD_GUEST_US); + hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_SYS, false); + hpp_dimension__add_output(PERF_HPP__OVERHEAD_GUEST_US, false); } } if (symbol_conf.show_nr_samples) - perf_hpp__column_enable(PERF_HPP__SAMPLES); + hpp_dimension__add_output(PERF_HPP__SAMPLES, false); if (symbol_conf.show_total_period) - perf_hpp__column_enable(PERF_HPP__PERIOD); + hpp_dimension__add_output(PERF_HPP__PERIOD, false); } -void perf_hpp__column_register(struct perf_hpp_fmt *format) +void perf_hpp_list__column_register(struct perf_hpp_list *list, + struct perf_hpp_fmt *format) { - list_add_tail(&format->list, &perf_hpp__list); + list_add_tail(&format->list, &list->fields); } -void perf_hpp__column_enable(unsigned col) +void perf_hpp_list__register_sort_field(struct perf_hpp_list *list, + struct perf_hpp_fmt *format) { - BUG_ON(col >= PERF_HPP__MAX_INDEX); - perf_hpp__column_register(&perf_hpp__format[col]); + list_add_tail(&format->sort_list, &list->sorts); } -static inline void advance_hpp(struct perf_hpp *hpp, int inc) +void perf_hpp_list__prepend_sort_field(struct perf_hpp_list *list, + struct perf_hpp_fmt *format) { - hpp->buf += inc; - hpp->size -= inc; + list_add(&format->sort_list, &list->sorts); } -int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he, - bool color) +static void perf_hpp__column_unregister(struct perf_hpp_fmt *format) { - const char *sep = symbol_conf.field_sep; - struct perf_hpp_fmt *fmt; - char *start = hpp->buf; - int ret; - bool first = true; + list_del_init(&format->list); + list_del_init(&format->sort_list); + fmt_free(format); +} - if (symbol_conf.exclude_other && !he->parent) - return 0; +void perf_hpp__cancel_cumulate(struct evlist *evlist) +{ + struct perf_hpp_fmt *fmt, *acc, *ovh, *acc_lat, *tmp; + struct evsel *evsel; - perf_hpp__for_each_format(fmt) { - /* - * If there's no field_sep, we still need - * to display initial ' '. - */ - if (!sep || !first) { - ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " "); - advance_hpp(hpp, ret); - } else - first = false; + if (is_strict_order(field_order)) + return; - if (color && fmt->color) - ret = fmt->color(hpp, he); - else - ret = fmt->entry(hpp, he); + ovh = &perf_hpp__format[PERF_HPP__OVERHEAD]; + acc = &perf_hpp__format[PERF_HPP__OVERHEAD_ACC]; + acc_lat = &perf_hpp__format[PERF_HPP__LATENCY_ACC]; + + perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { + if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) { + perf_hpp__column_unregister(fmt); + continue; + } - advance_hpp(hpp, ret); + if (fmt_equal(ovh, fmt)) + fmt->name = "Overhead"; } - return hpp->buf - start; + evlist__for_each_entry(evlist, evsel) { + struct hists *hists = evsel__hists(evsel); + struct perf_hpp_list_node *node; + + list_for_each_entry(node, &hists->hpp_formats, list) { + perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) { + if (fmt_equal(acc, fmt) || fmt_equal(acc_lat, fmt)) { + perf_hpp__column_unregister(fmt); + continue; + } + + if (fmt_equal(ovh, fmt)) + fmt->name = "Overhead"; + } + } + } } -int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, - struct hists *hists) +void perf_hpp__cancel_latency(struct evlist *evlist) { - const char *sep = symbol_conf.field_sep; - struct sort_entry *se; - int ret = 0; + struct perf_hpp_fmt *fmt, *lat, *acc, *tmp; + struct evsel *evsel; + + if (is_strict_order(field_order)) + return; + if (sort_order && strstr(sort_order, "latency")) + return; + + lat = &perf_hpp__format[PERF_HPP__LATENCY]; + acc = &perf_hpp__format[PERF_HPP__LATENCY_ACC]; + + perf_hpp_list__for_each_format_safe(&perf_hpp_list, fmt, tmp) { + if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt)) + perf_hpp__column_unregister(fmt); + } + + evlist__for_each_entry(evlist, evsel) { + struct hists *hists = evsel__hists(evsel); + struct perf_hpp_list_node *node; - list_for_each_entry(se, &hist_entry__sort_list, list) { - if (se->elide) + list_for_each_entry(node, &hists->hpp_formats, list) { + perf_hpp_list__for_each_format_safe(&node->hpp, fmt, tmp) { + if (fmt_equal(lat, fmt) || fmt_equal(acc, fmt)) + perf_hpp__column_unregister(fmt); + } + } + } +} + +void perf_hpp__setup_output_field(struct perf_hpp_list *list) +{ + struct perf_hpp_fmt *fmt; + + /* append sort keys to output field */ + perf_hpp_list__for_each_sort_list(list, fmt) { + struct perf_hpp_fmt *pos; + + /* skip sort-only fields ("sort_compute" in perf diff) */ + if (!fmt->entry && !fmt->color) continue; - ret += scnprintf(s + ret, size - ret, "%s", sep ?: " "); - ret += se->se_snprintf(he, s + ret, size - ret, - hists__col_len(hists, se->se_width_idx)); + perf_hpp_list__for_each_format(list, pos) { + if (fmt_equal(fmt, pos)) + goto next; + } + + perf_hpp__column_register(fmt); +next: + continue; } +} - return ret; +void perf_hpp__append_sort_keys(struct perf_hpp_list *list) +{ + struct perf_hpp_fmt *fmt; + + /* append output fields to sort keys */ + perf_hpp_list__for_each_format(list, fmt) { + struct perf_hpp_fmt *pos; + + perf_hpp_list__for_each_sort_list(list, pos) { + if (fmt_equal(fmt, pos)) + goto next; + } + + perf_hpp__register_sort_field(fmt); +next: + continue; + } +} + + +void perf_hpp__reset_output_field(struct perf_hpp_list *list) +{ + struct perf_hpp_fmt *fmt, *tmp; + + /* reset output fields */ + perf_hpp_list__for_each_format_safe(list, fmt, tmp) + perf_hpp__column_unregister(fmt); + + /* reset sort keys */ + perf_hpp_list__for_each_sort_list_safe(list, fmt, tmp) + perf_hpp__column_unregister(fmt); } /* @@ -489,25 +1040,234 @@ int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size, unsigned int hists__sort_list_width(struct hists *hists) { struct perf_hpp_fmt *fmt; - struct sort_entry *se; - int i = 0, ret = 0; - struct perf_hpp dummy_hpp = { - .ptr = hists_to_evsel(hists), - }; - - perf_hpp__for_each_format(fmt) { - if (i) + int ret = 0; + bool first = true; + struct perf_hpp dummy_hpp; + + hists__for_each_format(hists, fmt) { + if (perf_hpp__should_skip(fmt, hists)) + continue; + + if (first) + first = false; + else ret += 2; - ret += fmt->width(&dummy_hpp); + ret += fmt->width(fmt, &dummy_hpp, hists); } - list_for_each_entry(se, &hist_entry__sort_list, list) - if (!se->elide) - ret += 2 + hists__col_len(hists, se->se_width_idx); - - if (verbose) /* Addr + origin */ + if (verbose > 0 && hists__has(hists, sym)) /* Addr + origin */ ret += 3 + BITS_PER_LONG / 4; return ret; } + +unsigned int hists__overhead_width(struct hists *hists) +{ + struct perf_hpp_fmt *fmt; + int ret = 0; + bool first = true; + struct perf_hpp dummy_hpp; + + hists__for_each_format(hists, fmt) { + if (perf_hpp__is_sort_entry(fmt) || perf_hpp__is_dynamic_entry(fmt)) + break; + + if (first) + first = false; + else + ret += 2; + + ret += fmt->width(fmt, &dummy_hpp, hists); + } + + return ret; +} + +void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) +{ + if (perf_hpp__is_sort_entry(fmt)) + return perf_hpp__reset_sort_width(fmt, hists); + + if (perf_hpp__is_dynamic_entry(fmt)) + return; + + BUG_ON(fmt->idx >= PERF_HPP__MAX_INDEX); + + switch (fmt->idx) { + case PERF_HPP__OVERHEAD: + case PERF_HPP__LATENCY: + case PERF_HPP__OVERHEAD_SYS: + case PERF_HPP__OVERHEAD_US: + case PERF_HPP__OVERHEAD_ACC: + fmt->len = 8; + break; + + case PERF_HPP__OVERHEAD_GUEST_SYS: + case PERF_HPP__OVERHEAD_GUEST_US: + fmt->len = 9; + break; + + case PERF_HPP__SAMPLES: + case PERF_HPP__PERIOD: + fmt->len = 12; + break; + + case PERF_HPP__WEIGHT1: + case PERF_HPP__WEIGHT2: + case PERF_HPP__WEIGHT3: + fmt->len = 8; + break; + + case PERF_HPP__MEM_STAT_OP: + case PERF_HPP__MEM_STAT_CACHE: + case PERF_HPP__MEM_STAT_MEMORY: + case PERF_HPP__MEM_STAT_SNOOP: + case PERF_HPP__MEM_STAT_DTLB: + fmt->len = MEM_STAT_LEN * MEM_STAT_PRINT_LEN; + break; + + default: + break; + } +} + +void hists__reset_column_width(struct hists *hists) +{ + struct perf_hpp_fmt *fmt; + struct perf_hpp_list_node *node; + + hists__for_each_format(hists, fmt) + perf_hpp__reset_width(fmt, hists); + + /* hierarchy entries have their own hpp list */ + list_for_each_entry(node, &hists->hpp_formats, list) { + perf_hpp_list__for_each_format(&node->hpp, fmt) + perf_hpp__reset_width(fmt, hists); + } +} + +void perf_hpp__set_user_width(const char *width_list_str) +{ + struct perf_hpp_fmt *fmt; + const char *ptr = width_list_str; + + perf_hpp_list__for_each_format(&perf_hpp_list, fmt) { + char *p; + + int len = strtol(ptr, &p, 10); + fmt->user_len = len; + + if (*p == ',') + ptr = p + 1; + else + break; + } +} + +static int add_hierarchy_fmt(struct hists *hists, struct perf_hpp_fmt *fmt) +{ + struct perf_hpp_list_node *node = NULL; + struct perf_hpp_fmt *fmt_copy; + bool found = false; + bool skip = perf_hpp__should_skip(fmt, hists); + + list_for_each_entry(node, &hists->hpp_formats, list) { + if (node->level == fmt->level) { + found = true; + break; + } + } + + if (!found) { + node = malloc(sizeof(*node)); + if (node == NULL) + return -1; + + node->skip = skip; + node->level = fmt->level; + perf_hpp_list__init(&node->hpp); + + hists->nr_hpp_node++; + list_add_tail(&node->list, &hists->hpp_formats); + } + + fmt_copy = perf_hpp_fmt__dup(fmt); + if (fmt_copy == NULL) + return -1; + + if (!skip) + node->skip = false; + + list_add_tail(&fmt_copy->list, &node->hpp.fields); + list_add_tail(&fmt_copy->sort_list, &node->hpp.sorts); + + return 0; +} + +int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, + struct evlist *evlist) +{ + struct evsel *evsel; + struct perf_hpp_fmt *fmt; + struct hists *hists; + int ret; + + if (!symbol_conf.report_hierarchy) + return 0; + + evlist__for_each_entry(evlist, evsel) { + hists = evsel__hists(evsel); + + perf_hpp_list__for_each_sort_list(list, fmt) { + if (perf_hpp__is_dynamic_entry(fmt) && + !perf_hpp__defined_dynamic_entry(fmt, hists)) + continue; + + ret = add_hierarchy_fmt(hists, fmt); + if (ret < 0) + return ret; + } + } + + return 0; +} + +int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist) +{ + struct perf_hpp_fmt *fmt; + struct evsel *evsel; + enum mem_stat_type mst[16]; + unsigned nr_mem_stats = 0; + + perf_hpp_list__for_each_format(list, fmt) { + if (!perf_hpp__is_mem_stat_entry(fmt)) + continue; + + assert(nr_mem_stats < ARRAY_SIZE(mst)); + mst[nr_mem_stats++] = hpp__mem_stat_type(fmt); + } + + if (nr_mem_stats == 0) + return 0; + + list->nr_header_lines = 2; + + evlist__for_each_entry(evlist, evsel) { + struct hists *hists = evsel__hists(evsel); + + hists->mem_stat_types = calloc(nr_mem_stats, + sizeof(*hists->mem_stat_types)); + if (hists->mem_stat_types == NULL) + return -ENOMEM; + + hists->mem_stat_total = calloc(nr_mem_stats, + sizeof(*hists->mem_stat_total)); + if (hists->mem_stat_total == NULL) + return -ENOMEM; + + memcpy(hists->mem_stat_types, mst, nr_mem_stats * sizeof(*mst)); + hists->nr_mem_stats = nr_mem_stats; + } + return 0; +} |
