perf report: Add 'typeoff' sort key

The typeoff sort key shows the data type name, offset and the name of
the field.  This is useful to see which field in the struct is accessed
most frequently.

  $ perf report -s type,typeoff --hierarchy --stdio
  ...
  #     Overhead  Data Type / Data Type Offset
  # ............  ............................
  #
  ...
        1.23%     struct cfs_rq
           0.19%    struct cfs_rq +404 (throttle_count)
           0.19%    struct cfs_rq +0 (load.weight)
           0.19%    struct cfs_rq +336 (leaf_cfs_rq_list.next)
           0.09%    struct cfs_rq +272 (propagate)
           0.09%    struct cfs_rq +196 (removed.nr)
           0.09%    struct cfs_rq +80 (curr)
           0.09%    struct cfs_rq +544 (lt_b_children_throttled)
           0.06%    struct cfs_rq +320 (rq)

Committer testing:

Again with the perf.data from the previous csets:

  # perf report --stdio -s type,typeoff
  # To display the perf.data header info, please use --header/--header-only options.
  #
  #
  # Total Lost Samples: 0
  #
  # Samples: 4  of event 'cpu_atom/mem-loads,ldlat=30/P'
  # Event count (approx.): 7
  #
  # Overhead  Data Type  Data Type Offset
  # ........  .........  ................
  #
      42.86%  struct list_head  struct list_head +8 (prev)
      42.86%  (unknown)  (unknown) +0 (no field)
      14.29%  char       char +0 (no field)

  #
  # (Tip: To see callchains in a more compact form: perf report -g folded)
  #
  # perf report --stdio -s dso,type,typeoff
  # To display the perf.data header info, please use --header/--header-only options.
  #
  #
  # Total Lost Samples: 0
  #
  # Samples: 4  of event 'cpu_atom/mem-loads,ldlat=30/P'
  # Event count (approx.): 7
  #
  # Overhead  Shared Object         Data Type  Data Type Offset
  # ........  ....................  .........  ................
  #
      42.86%  [kernel.kallsyms]     struct list_head  struct list_head +8 (prev)
      28.57%  libc.so.6             (unknown)  (unknown) +0 (no field)
      14.29%  [kernel.kallsyms]     char       char +0 (no field)
      14.29%  ld-linux-x86-64.so.2  (unknown)  (unknown) +0 (no field)

  #
  # (Tip: If you have debuginfo enabled, try: perf report -s sym,srcline)
  #
  #

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: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Cc: linux-toolchains@vger.kernel.org
Cc: linux-trace-devel@vger.kernel.org
Link: https://lore.kernel.org/r/20231213001323.718046-13-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Namhyung Kim 2023-12-12 16:13:18 -08:00 committed by Arnaldo Carvalho de Melo
parent 9bd7ddd157
commit 871304a79f
5 changed files with 87 additions and 1 deletions

View File

@ -119,6 +119,7 @@ OPTIONS
to the previous instruction in cycles. And currently supported only on X86
- simd: Flags describing a SIMD operation. "e" for empty Arm SVE predicate. "p" for partial Arm SVE predicate
- type: Data type of sample memory access.
- typeoff: Offset in the data type of sample memory access.
By default, comm, dso and symbol keys are used.
(i.e. --sort comm,dso,symbol)

View File

@ -3716,6 +3716,7 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he)
op_loc->offset,
he->stat.nr_events,
he->stat.period);
he->mem_type_off = op_loc->offset;
return mem_type;
}
return NULL;

View File

@ -83,6 +83,7 @@ enum hist_column {
HISTC_ADDR,
HISTC_SIMD,
HISTC_TYPE,
HISTC_TYPE_OFFSET,
HISTC_NR_COLS, /* Last entry */
};

View File

@ -2153,8 +2153,10 @@ static void sort__type_init(struct hist_entry *he)
return;
he->mem_type = hist_entry__get_data_type(he);
if (he->mem_type == NULL)
if (he->mem_type == NULL) {
he->mem_type = &unknown_type;
he->mem_type_off = 0;
}
}
static int64_t
@ -2198,6 +2200,84 @@ struct sort_entry sort_type = {
.se_width_idx = HISTC_TYPE,
};
/* --sort typeoff */
static int64_t
sort__typeoff_sort(struct hist_entry *left, struct hist_entry *right)
{
struct annotated_data_type *left_type = left->mem_type;
struct annotated_data_type *right_type = right->mem_type;
int64_t ret;
if (!left_type) {
sort__type_init(left);
left_type = left->mem_type;
}
if (!right_type) {
sort__type_init(right);
right_type = right->mem_type;
}
ret = strcmp(left_type->self.type_name, right_type->self.type_name);
if (ret)
return ret;
return left->mem_type_off - right->mem_type_off;
}
static void fill_member_name(char *buf, size_t sz, struct annotated_member *m,
int offset, bool first)
{
struct annotated_member *child;
if (list_empty(&m->children))
return;
list_for_each_entry(child, &m->children, node) {
if (child->offset <= offset && offset < child->offset + child->size) {
int len = 0;
/* It can have anonymous struct/union members */
if (child->var_name) {
len = scnprintf(buf, sz, "%s%s",
first ? "" : ".", child->var_name);
first = false;
}
fill_member_name(buf + len, sz - len, child, offset, first);
return;
}
}
}
static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf,
size_t size, unsigned int width __maybe_unused)
{
struct annotated_data_type *he_type = he->mem_type;
char buf[4096];
buf[0] = '\0';
if (list_empty(&he_type->self.children))
snprintf(buf, sizeof(buf), "no field");
else
fill_member_name(buf, sizeof(buf), &he_type->self,
he->mem_type_off, true);
buf[4095] = '\0';
return repsep_snprintf(bf, size, "%s %+d (%s)", he_type->self.type_name,
he->mem_type_off, buf);
}
struct sort_entry sort_type_offset = {
.se_header = "Data Type Offset",
.se_cmp = sort__type_cmp,
.se_collapse = sort__typeoff_sort,
.se_sort = sort__typeoff_sort,
.se_init = sort__type_init,
.se_snprintf = hist_entry__typeoff_snprintf,
.se_width_idx = HISTC_TYPE_OFFSET,
};
struct sort_dimension {
const char *name;
@ -2254,6 +2334,7 @@ static struct sort_dimension common_sort_dimensions[] = {
DIM(SORT_GLOBAL_RETIRE_LAT, "retire_lat", sort_global_p_stage_cyc),
DIM(SORT_SIMD, "simd", sort_simd),
DIM(SORT_ANNOTATE_DATA_TYPE, "type", sort_type),
DIM(SORT_ANNOTATE_DATA_TYPE_OFFSET, "typeoff", sort_type_offset),
};
#undef DIM

View File

@ -113,6 +113,7 @@ struct hist_entry {
u64 p_stage_cyc;
u8 cpumode;
u8 depth;
int mem_type_off;
struct simd_flags simd_flags;
/* We are added by hists__add_dummy_entry. */
@ -247,6 +248,7 @@ enum sort_type {
SORT_GLOBAL_RETIRE_LAT,
SORT_SIMD,
SORT_ANNOTATE_DATA_TYPE,
SORT_ANNOTATE_DATA_TYPE_OFFSET,
/* branch stack specific sort keys */
__SORT_BRANCH_STACK,