Commit 871304a7 authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo
Browse files

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: default avatarNamhyung Kim <namhyung@kernel.org>
Tested-by: default avatarArnaldo 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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 9bd7ddd1
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -83,6 +83,7 @@ enum hist_column {
	HISTC_ADDR,
	HISTC_SIMD,
	HISTC_TYPE,
	HISTC_TYPE_OFFSET,
	HISTC_NR_COLS, /* Last entry */
};

+82 −1
Original line number Diff line number Diff line
@@ -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
+2 −0
Original line number Diff line number Diff line
@@ -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,