Commit 05274770 authored by Stephane Eranian's avatar Stephane Eranian Committed by Arnaldo Carvalho de Melo
Browse files

perf report: Add "addr_from" and "addr_to" sort dimensions



With the existing symbol_from/symbol_to, branches captured in the same
function would be collapsed into a single function if the latencies
associated with the each branch (cycles) were all the same.  That is the
case on Intel Broadwell, for instance. Since Intel Skylake, the latency
is captured by hardware and therefore is used to disambiguate branches.

Add addr_from/addr_to sort dimensions to sort branches based on their
addresses and not the function there are in. The output is still the
function name but the offset within the function is provided to uniquely
identify each branch.  These new sort dimensions also help with annotate
because they create different entries in the histogram which, in turn,
generates proper branch annotations.

Here is an example using AMD's branch sampling:

  $ perf record -a -b -c 1000037 -e cpu/branch-brs/ test_prg

  $ perf report
  Samples: 6M of event 'cpu/branch-brs/', Event count (approx.): 6901276
  Overhead  Command          Source Shared Object  Source Symbol                                   Target Symbol                                   Basic Block Cycle
    99.65%  test_prg	   test_prg              [.] test_thread                                 [.] test_thread                                 -
     0.02%  test_prg         [kernel.vmlinux]      [k] asm_sysvec_apic_timer_interrupt             [k] error_entry                                 -

  $ perf report -F overhead,comm,dso,addr_from,addr_to
  Samples: 6M of event 'cpu/branch-brs/', Event count (approx.): 6901276
  Overhead  Command          Shared Object     Source Address          Target Address
     4.22%  test_prg         test_prg          [.] test_thread+0x3c    [.] test_thread+0x4
     4.13%  test_prg         test_prg          [.] test_thread+0x4     [.] test_thread+0x3a
     4.09%  test_prg         test_prg          [.] test_thread+0x3a    [.] test_thread+0x6
     4.08%  test_prg         test_prg          [.] test_thread+0x2     [.] test_thread+0x3c
     4.06%  test_prg         test_prg          [.] test_thread+0x3e    [.] test_thread+0x2
     3.87%  test_prg         test_prg          [.] test_thread+0x6     [.] test_thread+0x38
     3.84%  test_prg         test_prg          [.] test_thread         [.] test_thread+0x3e
     3.76%  test_prg         test_prg          [.] test_thread+0x1e    [.] test_thread
     3.76%  test_prg         test_prg          [.] test_thread+0x38    [.] test_thread+0x8
     3.56%  test_prg         test_prg          [.] test_thread+0x22    [.] test_thread+0x1e
     3.54%  test_prg         test_prg          [.] test_thread+0x8     [.] test_thread+0x36
     3.47%  test_prg         test_prg          [.] test_thread+0x1c    [.] test_thread+0x22
     3.45%  test_prg         test_prg          [.] test_thread+0x36    [.] test_thread+0xa
     3.28%  test_prg         test_prg          [.] test_thread+0x24    [.] test_thread+0x1c
     3.25%  test_prg         test_prg          [.] test_thread+0xa     [.] test_thread+0x34
     3.24%  test_prg         test_prg          [.] test_thread+0x1a    [.] test_thread+0x24
     3.20%  test_prg         test_prg          [.] test_thread+0x34    [.] test_thread+0xc
     3.04%  test_prg         test_prg          [.] test_thread+0x26    [.] test_thread+0x1a
     3.01%  test_prg         test_prg          [.] test_thread+0xc     [.] test_thread+0x32
     2.98%  test_prg         test_prg          [.] test_thread+0x18    [.] test_thread+0x26
     2.94%  test_prg         test_prg          [.] test_thread+0x32    [.] test_thread+0xe
     2.76%  test_prg         test_prg          [.] test_thread+0x28    [.] test_thread+0x18
     2.73%  test_prg         test_prg          [.] test_thread+0xe     [.] test_thread+0x30
     2.67%  test_prg         test_prg          [.] test_thread+0x30    [.] test_thread+0x10
     2.67%  test_prg         test_prg          [.] test_thread+0x16    [.] test_thread+0x28
     2.46%  test_prg         test_prg          [.] test_thread+0x10    [.] test_thread+0x2e
     2.44%  test_prg         test_prg          [.] test_thread+0x2a    [.] test_thread+0x16
     2.38%  test_prg         test_prg          [.] test_thread+0x14    [.] test_thread+0x2a
     2.32%  test_prg         test_prg          [.] test_thread+0x2e    [.] test_thread+0x12
     2.28%  test_prg         test_prg          [.] test_thread+0x12    [.] test_thread+0x2c
     2.16%  test_prg         test_prg          [.] test_thread+0x2c    [.] test_thread+0x14
     0.02%  test_prg         [kernel.vmlinux]  [k] asm_sysvec_apic_ti+0x5  [k] error_entry

Signed-off-by: default avatarStephane Eranian <eranian@google.com>
Cc: Jiri Olsa <jolsa@redhat.com>
Cc: Kim Phillips <kim.phillips@amd.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Song Liu <songliubraving@fb.com>
Link: http://lore.kernel.org/lkml/20220208211637.2221872-13-eranian@google.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent b47f18d8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -124,6 +124,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
		} else {
			symlen = unresolved_col_width + 4 + 2;
			hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
			hists__new_col_len(hists, HISTC_ADDR_FROM, symlen);
			hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
		}

@@ -138,6 +139,7 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
		} else {
			symlen = unresolved_col_width + 4 + 2;
			hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
			hists__new_col_len(hists, HISTC_ADDR_TO, symlen);
			hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
		}

+2 −0
Original line number Diff line number Diff line
@@ -77,6 +77,8 @@ enum hist_column {
	HISTC_GLOBAL_INS_LAT,
	HISTC_LOCAL_P_STAGE_CYC,
	HISTC_GLOBAL_P_STAGE_CYC,
	HISTC_ADDR_FROM,
	HISTC_ADDR_TO,
	HISTC_NR_COLS, /* Last entry */
};

+128 −0
Original line number Diff line number Diff line
@@ -990,6 +990,128 @@ struct sort_entry sort_sym_to = {
	.se_width_idx	= HISTC_SYMBOL_TO,
};

static int _hist_entry__addr_snprintf(struct map_symbol *ms,
				     u64 ip, char level, char *bf, size_t size,
				     unsigned int width)
{
	struct symbol *sym = ms->sym;
	struct map *map = ms->map;
	size_t ret = 0, offs;

	ret += repsep_snprintf(bf + ret, size - ret, "[%c] ", level);
	if (sym && map) {
		if (sym->type == STT_OBJECT) {
			ret += repsep_snprintf(bf + ret, size - ret, "%s", sym->name);
			ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx",
					ip - map->unmap_ip(map, sym->start));
		} else {
			ret += repsep_snprintf(bf + ret, size - ret, "%.*s",
					       width - ret,
					       sym->name);
			offs = ip - sym->start;
			if (offs)
				ret += repsep_snprintf(bf + ret, size - ret, "+0x%llx", offs);
		}
	} else {
		size_t len = BITS_PER_LONG / 4;
		ret += repsep_snprintf(bf + ret, size - ret, "%-#.*llx",
				       len, ip);
	}

	return ret;
}

static int hist_entry__addr_from_snprintf(struct hist_entry *he, char *bf,
					 size_t size, unsigned int width)
{
	if (he->branch_info) {
		struct addr_map_symbol *from = &he->branch_info->from;

		return _hist_entry__addr_snprintf(&from->ms, from->al_addr,
						 he->level, bf, size, width);
	}

	return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int hist_entry__addr_to_snprintf(struct hist_entry *he, char *bf,
				       size_t size, unsigned int width)
{
	if (he->branch_info) {
		struct addr_map_symbol *to = &he->branch_info->to;

		return _hist_entry__addr_snprintf(&to->ms, to->al_addr,
						 he->level, bf, size, width);
	}

	return repsep_snprintf(bf, size, "%-*.*s", width, width, "N/A");
}

static int64_t
sort__addr_from_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct addr_map_symbol *from_l;
	struct addr_map_symbol *from_r;
	int64_t ret;

	if (!left->branch_info || !right->branch_info)
		return cmp_null(left->branch_info, right->branch_info);

	from_l = &left->branch_info->from;
	from_r = &right->branch_info->from;

	/*
	 * comparing symbol address alone is not enough since it's a
	 * relative address within a dso.
	 */
	ret = _sort__dso_cmp(from_l->ms.map, from_r->ms.map);
	if (ret != 0)
		return ret;

	return _sort__addr_cmp(from_l->addr, from_r->addr);
}

static int64_t
sort__addr_to_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct addr_map_symbol *to_l;
	struct addr_map_symbol *to_r;
	int64_t ret;

	if (!left->branch_info || !right->branch_info)
		return cmp_null(left->branch_info, right->branch_info);

	to_l = &left->branch_info->to;
	to_r = &right->branch_info->to;

	/*
	 * comparing symbol address alone is not enough since it's a
	 * relative address within a dso.
	 */
	ret = _sort__dso_cmp(to_l->ms.map, to_r->ms.map);
	if (ret != 0)
		return ret;

	return _sort__addr_cmp(to_l->addr, to_r->addr);
}

struct sort_entry sort_addr_from = {
	.se_header	= "Source Address",
	.se_cmp		= sort__addr_from_cmp,
	.se_snprintf	= hist_entry__addr_from_snprintf,
	.se_filter	= hist_entry__sym_from_filter, /* shared with sym_from */
	.se_width_idx	= HISTC_ADDR_FROM,
};

struct sort_entry sort_addr_to = {
	.se_header	= "Target Address",
	.se_cmp		= sort__addr_to_cmp,
	.se_snprintf	= hist_entry__addr_to_snprintf,
	.se_filter	= hist_entry__sym_to_filter, /* shared with sym_to */
	.se_width_idx	= HISTC_ADDR_TO,
};


static int64_t
sort__mispredict_cmp(struct hist_entry *left, struct hist_entry *right)
{
@@ -1893,6 +2015,8 @@ static struct sort_dimension bstack_sort_dimensions[] = {
	DIM(SORT_SRCLINE_FROM, "srcline_from", sort_srcline_from),
	DIM(SORT_SRCLINE_TO, "srcline_to", sort_srcline_to),
	DIM(SORT_SYM_IPC, "ipc_lbr", sort_sym_ipc),
	DIM(SORT_ADDR_FROM, "addr_from", sort_addr_from),
	DIM(SORT_ADDR_TO, "addr_to", sort_addr_to),
};

#undef DIM
@@ -3126,6 +3250,10 @@ static bool get_elide(int idx, FILE *output)
		return __get_elide(symbol_conf.dso_from_list, "dso_from", output);
	case HISTC_DSO_TO:
		return __get_elide(symbol_conf.dso_to_list, "dso_to", output);
	case HISTC_ADDR_FROM:
		return __get_elide(symbol_conf.sym_from_list, "addr_from", output);
	case HISTC_ADDR_TO:
		return __get_elide(symbol_conf.sym_to_list, "addr_to", output);
	default:
		break;
	}
+2 −0
Original line number Diff line number Diff line
@@ -251,6 +251,8 @@ enum sort_type {
	SORT_SRCLINE_FROM,
	SORT_SRCLINE_TO,
	SORT_SYM_IPC,
	SORT_ADDR_FROM,
	SORT_ADDR_TO,

	/* memory mode specific sort keys */
	__SORT_MEMORY_MODE,