diff options
author | Namhyung Kim <namhyung@kernel.org> | 2025-04-30 13:55:43 -0700 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2025-05-02 15:36:14 -0300 |
commit | b1fc83ca433e001e5cc0999c85c4ced5f72cf122 (patch) | |
tree | 5b804991d95df885108f9020bb97e9c6abadee66 | |
parent | 9fcb43e27c0e2696f6315022972e75ec0da8eb86 (diff) |
perf hist: Implement output fields for mem stats
This is a preparation for later changes to support mem_stat output. The
new fields will need two lines for the header - the first line will show
type of mem stat and the second line will show the name of each item
which is returned by mem_stat_name().
Each element in the mem_stat array will be printed in percentage for the
hist_entry and their sum would be 100%.
Add new output field dimension only for SORT_MODE__MEM using mem_stat.
To handle possible name conflict with existing sort keys, move the order
of checking output field dimensions after the sort dimensions when it
looks for sort keys.
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kan Liang <kan.liang@linux.intel.com>
Cc: Leo Yan <leo.yan@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Link: https://lore.kernel.org/r/20250430205548.789750-7-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 11 | ||||
-rw-r--r-- | tools/perf/ui/hist.c | 158 | ||||
-rw-r--r-- | tools/perf/util/hist.h | 4 | ||||
-rw-r--r-- | tools/perf/util/mem-events.c | 12 | ||||
-rw-r--r-- | tools/perf/util/mem-events.h | 3 | ||||
-rw-r--r-- | tools/perf/util/sort.c | 26 |
6 files changed, 202 insertions, 12 deletions
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 67cbdec90d0b..f6ab1310a0bd 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -1266,6 +1266,16 @@ hist_browser__hpp_color_##_type(struct perf_hpp_fmt *fmt, \ _fmttype); \ } +#define __HPP_COLOR_MEM_STAT_FN(_name, _type) \ +static int \ +hist_browser__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__slsmg_color_printf);\ +} + __HPP_COLOR_PERCENT_FN(overhead, period, PERF_HPP_FMT_TYPE__PERCENT) __HPP_COLOR_PERCENT_FN(latency, latency, PERF_HPP_FMT_TYPE__LATENCY) __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, PERF_HPP_FMT_TYPE__PERCENT) @@ -1277,6 +1287,7 @@ __HPP_COLOR_ACC_PERCENT_FN(latency_acc, latency, PERF_HPP_FMT_TYPE__LATENCY) #undef __HPP_COLOR_PERCENT_FN #undef __HPP_COLOR_ACC_PERCENT_FN +#undef __HPP_COLOR_MEM_STAT_FN void hist_browser__init_hpp(void) { diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 2aad46bbd2ed..2a5c9f2b328b 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -12,6 +12,7 @@ #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" @@ -151,6 +152,45 @@ int hpp__fmt_acc(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, return hpp__fmt(fmt, hpp, he, get_field, fmtstr, print_fn, fmtype); } +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); + + for (int i = 0; i < MEM_STAT_LEN; i++) + total += he->mem_stat[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]; + + 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; + + return ret; +} + static int field_cmp(u64 field_a, u64 field_b) { if (field_a > field_b) @@ -295,6 +335,23 @@ static int __hpp__sort_acc(struct hist_entry *a, struct hist_entry *b, return ret; } +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) +{ + if (!perf_hpp__is_mem_stat_entry(fmt)) + return -1; + + pr_debug("Should not reach here\n"); + return -1; +} + +static int64_t hpp__sort_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, + struct hist_entry *a, struct hist_entry *b) +{ + return a->stat.period - b->stat.period; +} + static int hpp__width_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp __maybe_unused, struct hists *hists) @@ -334,6 +391,45 @@ static int hpp__header_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, return scnprintf(hpp->buf, hpp->size, "%*s", len, hdr); } +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) +{ + char *buf = hpp->buf; + int ret = 0; + int len; + enum mem_stat_type mst = hpp__mem_stat_type(fmt); + + (void)hists; + if (line == 0) { + int left, right; + + len = fmt->len; + left = (len - strlen(fmt->name)) / 2 - 1; + right = len - left - strlen(fmt->name) - 2; + + 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; + + printed = scnprintf(buf, len, "%*s", MEM_STAT_PRINT_LEN, + mem_stat_name(mst, i)); + ret += printed; + buf += printed; + len -= printed; + } + return ret; +} + int hpp_color_scnprintf(struct perf_hpp *hpp, const char *fmt, ...) { va_list args; @@ -459,6 +555,23 @@ static int64_t hpp__sort_##_type(struct perf_hpp_fmt *fmt __maybe_unused, \ return __hpp__sort(a, b, he_get_##_field); \ } +#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); \ +} #define HPP_PERCENT_FNS(_type, _field, _fmttype) \ __HPP_COLOR_PERCENT_FN(_type, _field, _fmttype) \ @@ -478,6 +591,10 @@ __HPP_SORT_RAW_FN(_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) @@ -494,6 +611,8 @@ HPP_AVERAGE_FNS(weight1, weight1) HPP_AVERAGE_FNS(weight2, weight2) HPP_AVERAGE_FNS(weight3, weight3) +HPP_MEM_STAT_FNS(unknown, UNKNOWN) /* placeholder */ + 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) @@ -503,8 +622,7 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt) { - (void)fmt; - return false; + return fmt->sort == hpp__sort_mem_stat; } static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) @@ -520,6 +638,14 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) return a->idx == b->idx; } +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 a->entry == b->entry; +} + #define HPP__COLOR_PRINT_FNS(_name, _fn, _idx) \ { \ .name = _name, \ @@ -561,6 +687,20 @@ static bool hpp__equal(struct perf_hpp_fmt *a, struct perf_hpp_fmt *b) .equal = hpp__equal, \ } +#define HPP__MEM_STAT_PRINT_FNS(_name, _fn, _type) \ + { \ + .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("Overhead", overhead, OVERHEAD), HPP__COLOR_PRINT_FNS("Latency", latency, LATENCY), @@ -575,6 +715,7 @@ struct perf_hpp_fmt perf_hpp__format[] = { HPP__PRINT_FNS("Weight1", weight1, WEIGHT1), HPP__PRINT_FNS("Weight2", weight2, WEIGHT2), HPP__PRINT_FNS("Weight3", weight3, WEIGHT3), + HPP__MEM_STAT_PRINT_FNS("Unknown", unknown, UNKNOWN), /* placeholder */ }; struct perf_hpp_list perf_hpp_list = { @@ -586,11 +727,13 @@ struct perf_hpp_list perf_hpp_list = { #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 @@ -600,6 +743,9 @@ struct perf_hpp_list perf_hpp_list = { #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 @@ -924,6 +1070,10 @@ void perf_hpp__reset_width(struct perf_hpp_fmt *fmt, struct hists *hists) fmt->len = 8; break; + case PERF_HPP__MEM_STAT_UNKNOWN: /* placeholder */ + fmt->len = MEM_STAT_LEN * MEM_STAT_PRINT_LEN; + break; + default: break; } @@ -1042,12 +1192,14 @@ int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist) continue; assert(nr_mem_stats < ARRAY_SIZE(mst)); - mst[nr_mem_stats++] = PERF_MEM_STAT_UNKNOWN; + 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); diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 509af09691b8..18c696d8d568 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -587,6 +587,7 @@ enum { PERF_HPP__WEIGHT1, PERF_HPP__WEIGHT2, PERF_HPP__WEIGHT3, + PERF_HPP__MEM_STAT_UNKNOWN, /* placeholder */ PERF_HPP__MAX_INDEX }; @@ -656,6 +657,9 @@ 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); +int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp, + struct hist_entry *he, enum mem_stat_type mst, + const char *fmtstr, hpp_snprint_fn print_fn); static inline void advance_hpp(struct perf_hpp *hpp, int inc) { diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index 1bc60ad3dc31..a4c1e42de30f 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -817,3 +817,15 @@ int mem_stat_index(const enum mem_stat_type mst, const u64 val) (void)val; return -1; } + +/* To align output, returned string should be shorter than MEM_STAT_PRINT_LEN */ +const char *mem_stat_name(const enum mem_stat_type mst, const int idx) +{ + switch (mst) { + case PERF_MEM_STAT_UNKNOWN: + default: + break; + } + (void)idx; + return "N/A"; +} diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index 2604464f9858..7aeb4c5fefc8 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -93,6 +93,9 @@ enum mem_stat_type { PERF_MEM_STAT_UNKNOWN, /* placeholder */ }; +#define MEM_STAT_PRINT_LEN 7 /* 1 space + 5 digits + 1 percent sign */ + int mem_stat_index(const enum mem_stat_type mst, const u64 data_src); +const char *mem_stat_name(const enum mem_stat_type mst, const int idx); #endif /* __PERF_MEM_EVENTS_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 6024f588f66f..7c669ea27af2 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2598,9 +2598,11 @@ struct hpp_dimension { struct perf_hpp_fmt *fmt; int taken; int was_taken; + int mem_mode; }; #define DIM(d, n) { .name = n, .fmt = &perf_hpp__format[d], } +#define DIM_MEM(d, n) { .name = n, .fmt = &perf_hpp__format[d], .mem_mode = 1, } static struct hpp_dimension hpp_sort_dimensions[] = { DIM(PERF_HPP__OVERHEAD, "overhead"), @@ -2620,8 +2622,11 @@ static struct hpp_dimension hpp_sort_dimensions[] = { DIM(PERF_HPP__WEIGHT2, "ins_lat"), DIM(PERF_HPP__WEIGHT3, "retire_lat"), DIM(PERF_HPP__WEIGHT3, "p_stage_cyc"), + /* used for output only when SORT_MODE__MEM */ + DIM_MEM(PERF_HPP__MEM_STAT_UNKNOWN, "unknown"), /* placeholder */ }; +#undef DIM_MEM #undef DIM struct hpp_sort_entry { @@ -3608,15 +3613,6 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok, return __sort_dimension__add(sd, list, level); } - for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { - struct hpp_dimension *hd = &hpp_sort_dimensions[i]; - - if (strncasecmp(tok, hd->name, strlen(tok))) - continue; - - return __hpp_dimension__add(hd, list, level); - } - for (i = 0; i < ARRAY_SIZE(bstack_sort_dimensions); i++) { struct sort_dimension *sd = &bstack_sort_dimensions[i]; @@ -3658,6 +3654,15 @@ int sort_dimension__add(struct perf_hpp_list *list, const char *tok, return 0; } + for (i = 0; i < ARRAY_SIZE(hpp_sort_dimensions); i++) { + struct hpp_dimension *hd = &hpp_sort_dimensions[i]; + + if (strncasecmp(tok, hd->name, strlen(tok))) + continue; + + return __hpp_dimension__add(hd, list, level); + } + if (!add_dynamic_entry(evlist, tok, level)) return 0; @@ -4020,6 +4025,9 @@ int output_field_add(struct perf_hpp_list *list, const char *tok, int *level) if (!strcasecmp(tok, "weight")) ui__warning("--fields weight shows the average value unlike in the --sort key.\n"); + if (hd->mem_mode && sort__mode != SORT_MODE__MEMORY) + continue; + return __hpp_dimension__add_output(list, hd, *level); } |