perf hist: Hide unused mem stat columns

Some mem_stat types don't use all 8 columns.  And there are cases only
samples in certain kinds of mem_stat types are available only.  For that
case hide columns which has no samples.

The new output for the previous data would be:

  $ perf mem report -F overhead,op,comm --stdio
  ...
  #           ------ Mem Op -------
  # Overhead     Load  Store  Other  Command
  # ........  .....................  ...............
  #
      44.85%    21.1%  30.7%  48.3%  swapper
      26.82%    98.8%   0.3%   0.9%  netsli-prober
       7.19%    51.7%  13.7%  34.6%  perf
       5.81%    89.7%   2.2%   8.1%  qemu-system-ppc
       4.77%   100.0%   0.0%   0.0%  notifications_c
       1.77%    95.9%   1.2%   3.0%  MemoryReleaser
       0.77%    71.6%   4.1%  24.3%  DefaultEventMan
       0.19%    66.7%  22.2%  11.1%  gnome-shell
       ...

On Intel machines, the event is only for loads or stores so it'll have
only one column:

  #            Mem Op
  # Overhead     Load  Command
  # ........  .......  ...............
  #
      20.55%   100.0%  swapper
      17.13%   100.0%  chrome
       9.02%   100.0%  data-loop.0
       6.26%   100.0%  pipewire-pulse
       5.63%   100.0%  threaded-ml
       5.47%   100.0%  GraphRunner
       5.37%   100.0%  AudioIP~allback
       5.30%   100.0%  Chrome_ChildIOT
       3.17%   100.0%  Isolated Web Co
       ...

Committer testing:

  # grep "model name" -m1 /proc/cpuinfo
  model name	: AMD Ryzen 9 9950X3D 16-Core Processo
  # perf mem report -F overhead,op,comm --stdio
  # Total Lost Samples: 0
  #
  # Samples: 2K of event 'cycles:P'
  # Total weight : 2637
  # Sort order   : local_weight,mem,sym,dso,symbol_daddr,dso_daddr,snoop,tlb,locked,blocked,local_ins_lat,local_p_stage_cyc
  #
  #           ------ Mem Op -------
  # Overhead     Load  Store  Other  Command
  # ........  .....................  ...............
  #
      61.02%    14.4%  25.5%  60.1%  swapper
       5.61%    26.4%  13.5%  60.1%  Isolated Web Co
       5.50%    21.4%  29.7%  49.0%  perf
       4.74%    27.2%  15.2%  57.6%  gnome-shell
       4.63%    33.6%  11.5%  54.9%  mdns_service
       4.29%    28.3%  12.4%  59.3%  ptyxis
       2.16%    24.6%  19.3%  56.1%  DOM Worker
       0.99%    23.1%  34.6%  42.3%  firefox
       0.72%    26.3%  15.8%  57.9%  IPC I/O Parent
       0.61%    12.5%  12.5%  75.0%  kworker/u130:20
       0.61%    37.5%  18.8%  43.8%  podman
       0.57%    33.3%   6.7%  60.0%  Timer
       0.53%    14.3%   7.1%  78.6%  KMS thread
       0.49%    30.8%   7.7%  61.5%  kworker/u130:3-
       0.46%    41.7%  33.3%  25.0%  IPDL Background

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
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-9-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2025-04-30 13:55:45 -07:00 committed by Arnaldo Carvalho de Melo
parent 1e6569dca5
commit 225772c17c
3 changed files with 36 additions and 2 deletions

View File

@ -178,6 +178,9 @@ int hpp__fmt_mem_stat(struct perf_hpp_fmt *fmt __maybe_unused, struct perf_hpp *
for (int i = 0; i < MEM_STAT_LEN; i++) {
u64 val = he->mem_stat[mem_stat_idx].entries[i];
if (hists->mem_stat_total[mem_stat_idx].entries[i] == 0)
continue;
ret += hpp__call_print_fn(hpp, print_fn, fmtstr, 100.0 * val / total);
}
@ -405,12 +408,31 @@ static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hp
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);
(void)hists;
if (line == 0) {
int left, right;
len = fmt->len;
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;
/* print header directly if single column only */
if (len == MEM_STAT_PRINT_LEN)
return scnprintf(hpp->buf, hpp->size, "%*s", len, fmt->name);
left = (len - strlen(fmt->name)) / 2 - 1;
right = len - left - strlen(fmt->name) - 2;
@ -423,10 +445,14 @@ static int hpp__header_mem_stat_fn(struct perf_hpp_fmt *fmt, struct perf_hpp *hp
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;
@ -1214,6 +1240,11 @@ int perf_hpp__alloc_mem_stats(struct perf_hpp_list *list, struct evlist *evlist)
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;
}

View File

@ -354,6 +354,7 @@ static int hists__update_mem_stat(struct hists *hists, struct hist_entry *he,
assert(0 <= idx && idx < MEM_STAT_LEN);
he->mem_stat[i].entries[idx] += period;
hists->mem_stat_total[i].entries[idx] += period;
}
return 0;
}
@ -3054,6 +3055,7 @@ static void hists_evsel__exit(struct evsel *evsel)
hists__delete_all_entries(hists);
zfree(&hists->mem_stat_types);
zfree(&hists->mem_stat_total);
list_for_each_entry_safe(node, tmp, &hists->hpp_formats, list) {
perf_hpp_list__for_each_format_safe(&node->hpp, fmt, pos) {

View File

@ -135,6 +135,7 @@ struct hists {
int nr_hpp_node;
int nr_mem_stats;
enum mem_stat_type *mem_stat_types;
struct he_mem_stat *mem_stat_total;
};
#define hists__has(__h, __f) (__h)->hpp_list->__f