From: Namhyung Kim Date: Wed, 30 Apr 2025 20:55:42 +0000 (-0700) Subject: perf hist: Basic support for mem_stat accounting X-Git-Tag: v6.16-rc1~57^2~110 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9fcb43e27c0e2696f6315022972e75ec0da8eb86;p=thirdparty%2Flinux.git perf hist: Basic support for mem_stat accounting Add a logic to account he->mem_stat based on mem_stat_type in hists. Each mem_stat entry will have different meaning based on the type so the index in the array is calculated at runtime using the corresponding value in the sample.data_src. Still hists has no mem_stat_types yet so this code won't work for now. Later hists->mem_stat_types will be allocated based on what users want in the output actually. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Leo Yan Cc: Peter Zijlstra Cc: Ravi Bangoria Link: https://lore.kernel.org/r/20250430205548.789750-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index ec44633207aa3..2aad46bbd2ed4 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -11,6 +11,7 @@ #include "../util/sort.h" #include "../util/evsel.h" #include "../util/evlist.h" +#include "../util/mem-events.h" #include "../util/thread.h" #include "../util/util.h" @@ -500,6 +501,12 @@ static int64_t hpp__nop_cmp(struct perf_hpp_fmt *fmt __maybe_unused, return 0; } +static bool perf_hpp__is_mem_stat_entry(struct perf_hpp_fmt *fmt) +{ + (void)fmt; + return false; +} + static bool perf_hpp__is_hpp_entry(struct perf_hpp_fmt *a) { return a->header == hpp__header_fn; @@ -1022,3 +1029,35 @@ int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, 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++] = PERF_MEM_STAT_UNKNOWN; + } + + if (nr_mem_stats == 0) + return 0; + + 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; + + memcpy(hists->mem_stat_types, mst, nr_mem_stats * sizeof(*mst)); + hists->nr_mem_stats = nr_mem_stats; + } + return 0; +} diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index fcb9f0db0c92a..7759c1818c1ad 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -349,9 +349,10 @@ static int hists__update_mem_stat(struct hists *hists, struct hist_entry *he, } for (int i = 0; i < hists->nr_mem_stats; i++) { - int idx = 0; /* TODO: get correct index from mem info */ + int idx = mem_stat_index(hists->mem_stat_types[i], + mem_info__const_data_src(mi)->val); - (void)mi; + assert(0 <= idx && idx < MEM_STAT_LEN); he->mem_stat[i].entries[idx] += period; } return 0; @@ -3052,6 +3053,7 @@ static void hists_evsel__exit(struct evsel *evsel) struct perf_hpp_list_node *node, *tmp; hists__delete_all_entries(hists); + zfree(&hists->mem_stat_types); list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) { perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) { diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index aba1d84ca074f..509af09691b84 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -9,6 +9,7 @@ #include "events_stats.h" #include "evsel.h" #include "map_symbol.h" +#include "mem-events.h" #include "mutex.h" #include "sample.h" #include "spark.h" @@ -133,6 +134,7 @@ struct hists { struct list_head hpp_formats; int nr_hpp_node; int nr_mem_stats; + enum mem_stat_type *mem_stat_types; }; #define hists__has(__h, __f) (__h)->hpp_list->__f @@ -597,6 +599,8 @@ void perf_hpp__reset_output_field(struct perf_hpp_list *list); void perf_hpp__append_sort_keys(struct perf_hpp_list *list); int perf_hpp__setup_hists_formats(struct perf_hpp_list *list, struct evlist *evlist); +int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, + struct evlist *evlist); bool perf_hpp__is_sort_entry(struct perf_hpp_fmt *format); diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index 884d9aebce919..1bc60ad3dc312 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -799,3 +799,21 @@ void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add) stats->nomap += add->nomap; stats->noparse += add->noparse; } + +/* + * It returns an index in hist_entry->mem_stat array for the given val which + * represents a data-src based on the mem_stat_type. + * + * For example, when mst is about cache level, the index can be 1 for L1, 2 for + * L2 and so on. + */ +int mem_stat_index(const enum mem_stat_type mst, const u64 val) +{ + switch (mst) { + case PERF_MEM_STAT_UNKNOWN: /* placeholder */ + default: + break; + } + (void)val; + return -1; +} diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index a5c19d39ee371..2604464f98581 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -89,4 +89,10 @@ struct hist_entry; int c2c_decode_stats(struct c2c_stats *stats, struct mem_info *mi); void c2c_add_stats(struct c2c_stats *stats, struct c2c_stats *add); +enum mem_stat_type { + PERF_MEM_STAT_UNKNOWN, /* placeholder */ +}; + +int mem_stat_index(const enum mem_stat_type mst, const u64 data_src); + #endif /* __PERF_MEM_EVENTS_H */ diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index ae8b8ceb82f3d..6024f588f66f3 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -4163,6 +4163,10 @@ int setup_sorting(struct evlist *evlist) if (err < 0) return err; + err = perf_hpp__alloc_mem_stats(&perf_hpp_list, evlist); + if (err < 0) + return err; + /* copy sort keys to output fields */ perf_hpp__setup_output_field(&perf_hpp_list); /* and then copy output fields to sort keys */