Commit 1f2b7fbb authored by Kan Liang's avatar Kan Liang Committed by Arnaldo Carvalho de Melo
Browse files

perf annotate: Save branch counters for each block



When annotating a basic block, it's useful to display the occurrences
of other events in the block.

The branch counter feature is only available for newer Intel platforms.

So a dedicated option to display the branch counters is not introduced.

Reuse the existing --total-cycles option, which triggers the annotation
of a basic block and displays the cycle-related annotation.

When the branch counters information is available, the branch counters
are automatically appended after all the cycle-related annotation.

Accounting the branch counters as well when accounting the cycles in
hist__account_cycles().

In 'struct annotated_branch', introduce a br_cntr array to save the
accumulation of each branch counter.

In a sample, all the branch counters for a branch are saved in a u64
space.

Because the saturation of a branch counter is small, e.g., for Intel
Sierra Forest, the saturation is only 3.

Add ANNOTATION__BR_CNTR_SATURATED_FLAG to indicate if a branch counter
once saturated. That can be used to indicate a potential event lost
because of the saturation.

Reviewed-by: default avatarAndi Kleen <ak@linux.intel.com>
Signed-off-by: default avatarKan Liang <kan.liang@linux.intel.com>
Acked-by: default avatarNamhyung Kim <namhyung@kernel.org>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: https://lore.kernel.org/r/20240813160208.2493643-5-kan.liang@linux.intel.com


Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 3a867a6d
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -221,7 +221,8 @@ static int process_branch_callback(struct evsel *evsel,
	if (a.map != NULL)
		dso__set_hit(map__dso(a.map));

	hist__account_cycles(sample->branch_stack, al, sample, false, NULL);
	hist__account_cycles(sample->branch_stack, al, sample, false,
			     NULL, evsel);

	ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann);
out:
+2 −2
Original line number Diff line number Diff line
@@ -431,8 +431,8 @@ static int diff__process_sample_event(const struct perf_tool *tool,
			goto out;
		}

		hist__account_cycles(sample->branch_stack, &al, sample, false,
				     NULL);
		hist__account_cycles(sample->branch_stack, &al, sample,
				     false, NULL, evsel);
		break;

	case COMPUTE_STREAM:
+1 −1
Original line number Diff line number Diff line
@@ -328,7 +328,7 @@ static int process_sample_event(const struct perf_tool *tool,
	if (ui__has_annotation() || rep->symbol_ipc || rep->total_cycles_mode) {
		hist__account_cycles(sample->branch_stack, &al, sample,
				     rep->nonany_branch_mode,
				     &rep->total_cycles);
				     &rep->total_cycles, evsel);
	}

	ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
+2 −2
Original line number Diff line number Diff line
@@ -736,7 +736,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,

	hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
			     !(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
		     NULL);
			     NULL, evsel);
	return 0;
}

+56 −12
Original line number Diff line number Diff line
@@ -266,22 +266,30 @@ struct annotated_branch *annotation__get_branch(struct annotation *notes)
	return notes->branch;
}

static struct cyc_hist *symbol__cycles_hist(struct symbol *sym)
static struct annotated_branch *symbol__find_branch_hist(struct symbol *sym,
							 unsigned int br_cntr_nr)
{
	struct annotation *notes = symbol__annotation(sym);
	struct annotated_branch *branch;
	const size_t size = symbol__size(sym);

	branch = annotation__get_branch(notes);
	if (branch == NULL)
		return NULL;

	if (branch->cycles_hist == NULL) {
		const size_t size = symbol__size(sym);

		branch->cycles_hist = calloc(size, sizeof(struct cyc_hist));
		if (!branch->cycles_hist)
			return NULL;
	}

	if (br_cntr_nr && branch->br_cntr == NULL) {
		branch->br_cntr = calloc(br_cntr_nr * size, sizeof(u64));
		if (!branch->br_cntr)
			return NULL;
	}

	return branch->cycles_hist;
	return branch;
}

struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists)
@@ -316,16 +324,44 @@ static int symbol__inc_addr_samples(struct map_symbol *ms,
	return src ? __symbol__inc_addr_samples(ms, src, evsel->core.idx, addr, sample) : 0;
}

static int symbol__account_cycles(u64 addr, u64 start,
				  struct symbol *sym, unsigned cycles)
static int symbol__account_br_cntr(struct annotated_branch *branch,
				   struct evsel *evsel,
				   unsigned offset,
				   u64 br_cntr)
{
	struct cyc_hist *cycles_hist;
	unsigned int br_cntr_nr = evsel__leader(evsel)->br_cntr_nr;
	unsigned int base = evsel__leader(evsel)->br_cntr_idx;
	unsigned int width = evsel__env(evsel)->br_cntr_width;
	unsigned int off = offset * evsel->evlist->nr_br_cntr;
	unsigned int i, mask = (1L << width) - 1;
	u64 *branch_br_cntr = branch->br_cntr;

	if (!br_cntr || !branch_br_cntr)
		return 0;

	for (i = 0; i < br_cntr_nr; i++) {
		u64 cntr = (br_cntr >> i * width) & mask;

		branch_br_cntr[off + i + base] += cntr;
		if (cntr == mask)
			branch_br_cntr[off + i + base] |= ANNOTATION__BR_CNTR_SATURATED_FLAG;
	}

	return 0;
}

static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym,
				  unsigned cycles, struct evsel *evsel,
				  u64 br_cntr)
{
	struct annotated_branch *branch;
	unsigned offset;
	int ret;

	if (sym == NULL)
		return 0;
	cycles_hist = symbol__cycles_hist(sym);
	if (cycles_hist == NULL)
	branch = symbol__find_branch_hist(sym, evsel->evlist->nr_br_cntr);
	if (!branch)
		return -ENOMEM;
	if (addr < sym->start || addr >= sym->end)
		return -ERANGE;
@@ -337,15 +373,22 @@ static int symbol__account_cycles(u64 addr, u64 start,
			start = 0;
	}
	offset = addr - sym->start;
	return __symbol__account_cycles(cycles_hist,
	ret = __symbol__account_cycles(branch->cycles_hist,
					start ? start - sym->start : 0,
					offset, cycles,
					!!start);

	if (ret)
		return ret;

	return symbol__account_br_cntr(branch, evsel, offset, br_cntr);
}

int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
				    struct addr_map_symbol *start,
				    unsigned cycles)
				    unsigned cycles,
				    struct evsel *evsel,
				    u64 br_cntr)
{
	u64 saddr = 0;
	int err;
@@ -371,7 +414,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
			start ? start->addr : 0,
			ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0,
			saddr);
	err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles);
	err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr);
	if (err)
		pr_debug2("account_cycles failed %d\n", err);
	return err;
@@ -412,6 +455,7 @@ static void annotated_branch__delete(struct annotated_branch *branch)
{
	if (branch) {
		zfree(&branch->cycles_hist);
		free(branch->br_cntr);
		free(branch);
	}
}
Loading