Commit d4957633 authored by Andi Kleen's avatar Andi Kleen Committed by Arnaldo Carvalho de Melo

perf report: Add infrastructure for a cycles histogram

This adds the basic infrastructure to keep track of cycle counts per
basic block for annotate. We allocate an array similar to the normal
accounting, and then account branch cycles there.

We handle two cases:

cycles per basic block with start and cycles per branch (these are later
used for either IPC or just cycles per BB)

In the start case we cannot handle overlaps, so always the longest basic
block wins.

For the cycles per branch case everything is accurately accounted.

v2: Remove unnecessary checks. Slight restructure. Move
symbol__get_annotation to another patch. Move histogram allocation.
v3: Merged with current tree
Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
Acked-by: default avatarJiri Olsa <jolsa@kernel.org>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: http://lkml.kernel.org/r/1437233094-12844-4-git-send-email-andi@firstfloor.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent 98df858e
...@@ -187,6 +187,7 @@ static void hists__find_annotations(struct hists *hists, ...@@ -187,6 +187,7 @@ static void hists__find_annotations(struct hists *hists,
* symbol, free he->ms.sym->src to signal we already * symbol, free he->ms.sym->src to signal we already
* processed this symbol. * processed this symbol.
*/ */
zfree(&notes->src->cycles_hist);
zfree(&notes->src); zfree(&notes->src);
} }
} }
......
...@@ -473,17 +473,73 @@ int symbol__alloc_hist(struct symbol *sym) ...@@ -473,17 +473,73 @@ int symbol__alloc_hist(struct symbol *sym)
return 0; return 0;
} }
/* The cycles histogram is lazily allocated. */
static int symbol__alloc_hist_cycles(struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
const size_t size = symbol__size(sym);
notes->src->cycles_hist = calloc(size, sizeof(struct cyc_hist));
if (notes->src->cycles_hist == NULL)
return -1;
return 0;
}
void symbol__annotate_zero_histograms(struct symbol *sym) void symbol__annotate_zero_histograms(struct symbol *sym)
{ {
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
pthread_mutex_lock(&notes->lock); pthread_mutex_lock(&notes->lock);
if (notes->src != NULL) if (notes->src != NULL) {
memset(notes->src->histograms, 0, memset(notes->src->histograms, 0,
notes->src->nr_histograms * notes->src->sizeof_sym_hist); notes->src->nr_histograms * notes->src->sizeof_sym_hist);
if (notes->src->cycles_hist)
memset(notes->src->cycles_hist, 0,
symbol__size(sym) * sizeof(struct cyc_hist));
}
pthread_mutex_unlock(&notes->lock); pthread_mutex_unlock(&notes->lock);
} }
static int __symbol__account_cycles(struct annotation *notes,
u64 start,
unsigned offset, unsigned cycles,
unsigned have_start)
{
struct cyc_hist *ch;
ch = notes->src->cycles_hist;
/*
* For now we can only account one basic block per
* final jump. But multiple could be overlapping.
* Always account the longest one. So when
* a shorter one has been already seen throw it away.
*
* We separately always account the full cycles.
*/
ch[offset].num_aggr++;
ch[offset].cycles_aggr += cycles;
if (!have_start && ch[offset].have_start)
return 0;
if (ch[offset].num) {
if (have_start && (!ch[offset].have_start ||
ch[offset].start > start)) {
ch[offset].have_start = 0;
ch[offset].cycles = 0;
ch[offset].num = 0;
if (ch[offset].reset < 0xffff)
ch[offset].reset++;
} else if (have_start &&
ch[offset].start < start)
return 0;
}
ch[offset].have_start = have_start;
ch[offset].start = start;
ch[offset].cycles += cycles;
ch[offset].num++;
return 0;
}
static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
struct annotation *notes, int evidx, u64 addr) struct annotation *notes, int evidx, u64 addr)
{ {
...@@ -506,7 +562,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, ...@@ -506,7 +562,7 @@ static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map,
return 0; return 0;
} }
static struct annotation *symbol__get_annotation(struct symbol *sym) static struct annotation *symbol__get_annotation(struct symbol *sym, bool cycles)
{ {
struct annotation *notes = symbol__annotation(sym); struct annotation *notes = symbol__annotation(sym);
...@@ -514,6 +570,10 @@ static struct annotation *symbol__get_annotation(struct symbol *sym) ...@@ -514,6 +570,10 @@ static struct annotation *symbol__get_annotation(struct symbol *sym)
if (symbol__alloc_hist(sym) < 0) if (symbol__alloc_hist(sym) < 0)
return NULL; return NULL;
} }
if (!notes->src->cycles_hist && cycles) {
if (symbol__alloc_hist_cycles(sym) < 0)
return NULL;
}
return notes; return notes;
} }
...@@ -524,12 +584,73 @@ static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, ...@@ -524,12 +584,73 @@ static int symbol__inc_addr_samples(struct symbol *sym, struct map *map,
if (sym == NULL) if (sym == NULL)
return 0; return 0;
notes = symbol__get_annotation(sym); notes = symbol__get_annotation(sym, false);
if (notes == NULL) if (notes == NULL)
return -ENOMEM; return -ENOMEM;
return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); return __symbol__inc_addr_samples(sym, map, notes, evidx, addr);
} }
static int symbol__account_cycles(u64 addr, u64 start,
struct symbol *sym, unsigned cycles)
{
struct annotation *notes;
unsigned offset;
if (sym == NULL)
return 0;
notes = symbol__get_annotation(sym, true);
if (notes == NULL)
return -ENOMEM;
if (addr < sym->start || addr >= sym->end)
return -ERANGE;
if (start) {
if (start < sym->start || start >= sym->end)
return -ERANGE;
if (start >= addr)
start = 0;
}
offset = addr - sym->start;
return __symbol__account_cycles(notes,
start ? start - sym->start : 0,
offset, cycles,
!!start);
}
int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
struct addr_map_symbol *start,
unsigned cycles)
{
unsigned long saddr = 0;
int err;
if (!cycles)
return 0;
/*
* Only set start when IPC can be computed. We can only
* compute it when the basic block is completely in a single
* function.
* Special case the case when the jump is elsewhere, but
* it starts on the function start.
*/
if (start &&
(start->sym == ams->sym ||
(ams->sym &&
start->addr == ams->sym->start + ams->map->start)))
saddr = start->al_addr;
if (saddr == 0)
pr_debug2("BB with bad start: addr %lx start %lx sym %lx saddr %lx\n",
ams->addr,
start ? start->addr : 0,
ams->sym ? ams->sym->start + ams->map->start : 0,
saddr);
err = symbol__account_cycles(ams->al_addr, saddr, ams->sym, cycles);
if (err)
pr_debug2("account_cycles failed %d\n", err);
return err;
}
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx)
{ {
return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr);
......
...@@ -79,6 +79,17 @@ struct sym_hist { ...@@ -79,6 +79,17 @@ struct sym_hist {
u64 addr[0]; u64 addr[0];
}; };
struct cyc_hist {
u64 start;
u64 cycles;
u64 cycles_aggr;
u32 num;
u32 num_aggr;
u8 have_start;
/* 1 byte padding */
u16 reset;
};
struct source_line_samples { struct source_line_samples {
double percent; double percent;
double percent_sum; double percent_sum;
...@@ -97,6 +108,7 @@ struct source_line { ...@@ -97,6 +108,7 @@ struct source_line {
* @histogram: Array of addr hit histograms per event being monitored * @histogram: Array of addr hit histograms per event being monitored
* @lines: If 'print_lines' is specified, per source code line percentages * @lines: If 'print_lines' is specified, per source code line percentages
* @source: source parsed from a disassembler like objdump -dS * @source: source parsed from a disassembler like objdump -dS
* @cyc_hist: Average cycles per basic block
* *
* lines is allocated, percentages calculated and all sorted by percentage * lines is allocated, percentages calculated and all sorted by percentage
* when the annotation is about to be presented, so the percentages are for * when the annotation is about to be presented, so the percentages are for
...@@ -109,6 +121,7 @@ struct annotated_source { ...@@ -109,6 +121,7 @@ struct annotated_source {
struct source_line *lines; struct source_line *lines;
int nr_histograms; int nr_histograms;
int sizeof_sym_hist; int sizeof_sym_hist;
struct cyc_hist *cycles_hist;
struct sym_hist histograms[0]; struct sym_hist histograms[0];
}; };
...@@ -130,6 +143,10 @@ static inline struct annotation *symbol__annotation(struct symbol *sym) ...@@ -130,6 +143,10 @@ static inline struct annotation *symbol__annotation(struct symbol *sym)
int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx); int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx);
int addr_map_symbol__account_cycles(struct addr_map_symbol *ams,
struct addr_map_symbol *start,
unsigned cycles);
int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr);
int symbol__alloc_hist(struct symbol *sym); int symbol__alloc_hist(struct symbol *sym);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment