mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-12-29 17:25:38 +00:00
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: Andi Kleen <ak@linux.intel.com> Signed-off-by: Kan Liang <kan.liang@linux.intel.com> Acked-by: Namhyung 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: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
3a867a6dad
commit
1f2b7fbb04
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -735,8 +735,8 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
|
||||
perf_top__record_precise_ip(top, iter->he, iter->sample, evsel, al->addr);
|
||||
|
||||
hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
|
||||
!(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
|
||||
NULL);
|
||||
!(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
|
||||
NULL, evsel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
return branch->cycles_hist;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "spark.h"
|
||||
#include "hashmap.h"
|
||||
#include "disasm.h"
|
||||
#include "branch.h"
|
||||
|
||||
struct hist_browser_timer;
|
||||
struct hist_entry;
|
||||
@ -288,6 +289,9 @@ struct annotated_source {
|
||||
struct annotation_line *annotated_source__get_line(struct annotated_source *src,
|
||||
s64 offset);
|
||||
|
||||
/* A branch counter once saturated */
|
||||
#define ANNOTATION__BR_CNTR_SATURATED_FLAG (1ULL << 63)
|
||||
|
||||
/**
|
||||
* struct annotated_branch - basic block and IPC information for a symbol.
|
||||
*
|
||||
@ -297,6 +301,7 @@ struct annotation_line *annotated_source__get_line(struct annotated_source *src,
|
||||
* @cover_insn: Number of distinct, actually executed instructions.
|
||||
* @cycles_hist: Array of cyc_hist for each instruction.
|
||||
* @max_coverage: Maximum number of covered basic block (used for block-range).
|
||||
* @br_cntr: Array of the occurrences of events (branch counters) during a block.
|
||||
*
|
||||
* This struct is used by two different codes when the sample has branch stack
|
||||
* and cycles information. annotation__compute_ipc() calculates average IPC
|
||||
@ -313,6 +318,7 @@ struct annotated_branch {
|
||||
unsigned int cover_insn;
|
||||
struct cyc_hist *cycles_hist;
|
||||
u64 max_coverage;
|
||||
u64 *br_cntr;
|
||||
};
|
||||
|
||||
struct LOCKABLE annotation {
|
||||
@ -383,7 +389,9 @@ struct annotated_branch *annotation__get_branch(struct annotation *notes);
|
||||
|
||||
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);
|
||||
|
||||
int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample,
|
||||
struct evsel *evsel, u64 addr);
|
||||
|
@ -34,6 +34,7 @@ struct branch_info {
|
||||
struct addr_map_symbol from;
|
||||
struct addr_map_symbol to;
|
||||
struct branch_flags flags;
|
||||
u64 branch_stack_cntr;
|
||||
char *srcline_from;
|
||||
char *srcline_to;
|
||||
};
|
||||
|
@ -2677,7 +2677,7 @@ int hists__unlink(struct hists *hists)
|
||||
|
||||
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
|
||||
struct perf_sample *sample, bool nonany_branch_mode,
|
||||
u64 *total_cycles)
|
||||
u64 *total_cycles, struct evsel *evsel)
|
||||
{
|
||||
struct branch_info *bi;
|
||||
struct branch_entry *entries = perf_sample__branch_entries(sample);
|
||||
@ -2701,7 +2701,8 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
|
||||
for (int i = bs->nr - 1; i >= 0; i--) {
|
||||
addr_map_symbol__account_cycles(&bi[i].from,
|
||||
nonany_branch_mode ? NULL : prev,
|
||||
bi[i].flags.cycles);
|
||||
bi[i].flags.cycles, evsel,
|
||||
bi[i].branch_stack_cntr);
|
||||
prev = &bi[i].to;
|
||||
|
||||
if (total_cycles)
|
||||
|
@ -742,7 +742,7 @@ unsigned int hists__overhead_width(struct hists *hists);
|
||||
|
||||
void hist__account_cycles(struct branch_stack *bs, struct addr_location *al,
|
||||
struct perf_sample *sample, bool nonany_branch_mode,
|
||||
u64 *total_cycles);
|
||||
u64 *total_cycles, struct evsel *evsel);
|
||||
|
||||
struct option;
|
||||
int parse_filter_percentage(const struct option *opt, const char *arg, int unset);
|
||||
|
@ -2141,6 +2141,7 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
|
||||
unsigned int i;
|
||||
const struct branch_stack *bs = sample->branch_stack;
|
||||
struct branch_entry *entries = perf_sample__branch_entries(sample);
|
||||
u64 *branch_stack_cntr = sample->branch_stack_cntr;
|
||||
struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info));
|
||||
|
||||
if (!bi)
|
||||
@ -2150,6 +2151,8 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample,
|
||||
ip__resolve_ams(al->thread, &bi[i].to, entries[i].to);
|
||||
ip__resolve_ams(al->thread, &bi[i].from, entries[i].from);
|
||||
bi[i].flags = entries[i].flags;
|
||||
if (branch_stack_cntr)
|
||||
bi[i].branch_stack_cntr = branch_stack_cntr[i];
|
||||
}
|
||||
return bi;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user