Commit b5387528 authored by Roberto Agostino Vitillo's avatar Roberto Agostino Vitillo Committed by Ingo Molnar

perf tools: Add code to support PERF_SAMPLE_BRANCH_STACK

This patch adds:

 - ability to parse samples with PERF_SAMPLE_BRANCH_STACK
 - sort on branches (dso_from, symbol_from, dso_to, symbol_to, mispredict)
 - build histograms on branches
Signed-off-by: default avatarRoberto Agostino Vitillo <ravitillo@lbl.gov>
Signed-off-by: default avatarStephane Eranian <eranian@google.com>
Cc: peterz@infradead.org
Cc: acme@redhat.com
Cc: robert.richter@amd.com
Cc: ming.m.lin@intel.com
Cc: andi@firstfloor.org
Cc: asharma@fb.com
Cc: vweaver1@eecs.utk.edu
Cc: khandual@linux.vnet.ibm.com
Cc: dsahern@gmail.com
Link: http://lkml.kernel.org/r/1328826068-11713-12-git-send-email-eranian@google.comSigned-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent d010b332
...@@ -179,6 +179,23 @@ struct ip_callchain { ...@@ -179,6 +179,23 @@ struct ip_callchain {
u64 ips[0]; u64 ips[0];
}; };
struct branch_flags {
u64 mispred:1;
u64 predicted:1;
u64 reserved:62;
};
struct branch_entry {
u64 from;
u64 to;
struct branch_flags flags;
};
struct branch_stack {
u64 nr;
struct branch_entry entries[0];
};
extern bool perf_host, perf_guest; extern bool perf_host, perf_guest;
extern const char perf_version_string[]; extern const char perf_version_string[];
......
...@@ -81,6 +81,7 @@ struct perf_sample { ...@@ -81,6 +81,7 @@ struct perf_sample {
u32 raw_size; u32 raw_size;
void *raw_data; void *raw_data;
struct ip_callchain *callchain; struct ip_callchain *callchain;
struct branch_stack *branch_stack;
}; };
#define BUILD_ID_SIZE 20 #define BUILD_ID_SIZE 20
......
...@@ -576,6 +576,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type, ...@@ -576,6 +576,16 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,
data->raw_data = (void *) pdata; data->raw_data = (void *) pdata;
} }
if (type & PERF_SAMPLE_BRANCH_STACK) {
u64 sz;
data->branch_stack = (struct branch_stack *)array;
array++; /* nr */
sz = data->branch_stack->nr * sizeof(struct branch_entry);
sz /= sizeof(u64);
array += sz;
}
return 0; return 0;
} }
......
...@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists) ...@@ -50,21 +50,25 @@ static void hists__reset_col_len(struct hists *hists)
hists__set_col_len(hists, col, 0); hists__set_col_len(hists, col, 0);
} }
static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
{
const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
if (hists__col_len(hists, dso) < unresolved_col_width &&
!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
!symbol_conf.dso_list)
hists__set_col_len(hists, dso, unresolved_col_width);
}
static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
{ {
const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
u16 len; u16 len;
if (h->ms.sym) if (h->ms.sym)
hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen); hists__new_col_len(hists, HISTC_SYMBOL, h->ms.sym->namelen + 4);
else { else
const unsigned int unresolved_col_width = BITS_PER_LONG / 4; hists__set_unres_dso_col_len(hists, HISTC_DSO);
if (hists__col_len(hists, HISTC_DSO) < unresolved_col_width &&
!symbol_conf.col_width_list_str && !symbol_conf.field_sep &&
!symbol_conf.dso_list)
hists__set_col_len(hists, HISTC_DSO,
unresolved_col_width);
}
len = thread__comm_len(h->thread); len = thread__comm_len(h->thread);
if (hists__new_col_len(hists, HISTC_COMM, len)) if (hists__new_col_len(hists, HISTC_COMM, len))
...@@ -74,6 +78,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h) ...@@ -74,6 +78,37 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
len = dso__name_len(h->ms.map->dso); len = dso__name_len(h->ms.map->dso);
hists__new_col_len(hists, HISTC_DSO, len); hists__new_col_len(hists, HISTC_DSO, len);
} }
if (h->branch_info) {
int symlen;
/*
* +4 accounts for '[x] ' priv level info
* +2 account of 0x prefix on raw addresses
*/
if (h->branch_info->from.sym) {
symlen = (int)h->branch_info->from.sym->namelen + 4;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
symlen = dso__name_len(h->branch_info->from.map->dso);
hists__new_col_len(hists, HISTC_DSO_FROM, symlen);
} else {
symlen = unresolved_col_width + 4 + 2;
hists__new_col_len(hists, HISTC_SYMBOL_FROM, symlen);
hists__set_unres_dso_col_len(hists, HISTC_DSO_FROM);
}
if (h->branch_info->to.sym) {
symlen = (int)h->branch_info->to.sym->namelen + 4;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
symlen = dso__name_len(h->branch_info->to.map->dso);
hists__new_col_len(hists, HISTC_DSO_TO, symlen);
} else {
symlen = unresolved_col_width + 4 + 2;
hists__new_col_len(hists, HISTC_SYMBOL_TO, symlen);
hists__set_unres_dso_col_len(hists, HISTC_DSO_TO);
}
}
} }
static void hist_entry__add_cpumode_period(struct hist_entry *he, static void hist_entry__add_cpumode_period(struct hist_entry *he,
...@@ -195,26 +230,14 @@ static u8 symbol__parent_filter(const struct symbol *parent) ...@@ -195,26 +230,14 @@ static u8 symbol__parent_filter(const struct symbol *parent)
return 0; return 0;
} }
struct hist_entry *__hists__add_entry(struct hists *hists, static struct hist_entry *add_hist_entry(struct hists *hists,
struct hist_entry *entry,
struct addr_location *al, struct addr_location *al,
struct symbol *sym_parent, u64 period) u64 period)
{ {
struct rb_node **p; struct rb_node **p;
struct rb_node *parent = NULL; struct rb_node *parent = NULL;
struct hist_entry *he; struct hist_entry *he;
struct hist_entry entry = {
.thread = al->thread,
.ms = {
.map = al->map,
.sym = al->sym,
},
.cpu = al->cpu,
.ip = al->addr,
.level = al->level,
.period = period,
.parent = sym_parent,
.filtered = symbol__parent_filter(sym_parent),
};
int cmp; int cmp;
pthread_mutex_lock(&hists->lock); pthread_mutex_lock(&hists->lock);
...@@ -225,7 +248,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, ...@@ -225,7 +248,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
parent = *p; parent = *p;
he = rb_entry(parent, struct hist_entry, rb_node_in); he = rb_entry(parent, struct hist_entry, rb_node_in);
cmp = hist_entry__cmp(&entry, he); cmp = hist_entry__cmp(entry, he);
if (!cmp) { if (!cmp) {
he->period += period; he->period += period;
...@@ -239,7 +262,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, ...@@ -239,7 +262,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
p = &(*p)->rb_right; p = &(*p)->rb_right;
} }
he = hist_entry__new(&entry); he = hist_entry__new(entry);
if (!he) if (!he)
goto out_unlock; goto out_unlock;
...@@ -252,6 +275,51 @@ struct hist_entry *__hists__add_entry(struct hists *hists, ...@@ -252,6 +275,51 @@ struct hist_entry *__hists__add_entry(struct hists *hists,
return he; return he;
} }
struct hist_entry *__hists__add_branch_entry(struct hists *self,
struct addr_location *al,
struct symbol *sym_parent,
struct branch_info *bi,
u64 period)
{
struct hist_entry entry = {
.thread = al->thread,
.ms = {
.map = bi->to.map,
.sym = bi->to.sym,
},
.cpu = al->cpu,
.ip = bi->to.addr,
.level = al->level,
.period = period,
.parent = sym_parent,
.filtered = symbol__parent_filter(sym_parent),
.branch_info = bi,
};
return add_hist_entry(self, &entry, al, period);
}
struct hist_entry *__hists__add_entry(struct hists *self,
struct addr_location *al,
struct symbol *sym_parent, u64 period)
{
struct hist_entry entry = {
.thread = al->thread,
.ms = {
.map = al->map,
.sym = al->sym,
},
.cpu = al->cpu,
.ip = al->addr,
.level = al->level,
.period = period,
.parent = sym_parent,
.filtered = symbol__parent_filter(sym_parent),
};
return add_hist_entry(self, &entry, al, period);
}
int64_t int64_t
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right) hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)
{ {
......
...@@ -42,6 +42,11 @@ enum hist_column { ...@@ -42,6 +42,11 @@ enum hist_column {
HISTC_COMM, HISTC_COMM,
HISTC_PARENT, HISTC_PARENT,
HISTC_CPU, HISTC_CPU,
HISTC_MISPREDICT,
HISTC_SYMBOL_FROM,
HISTC_SYMBOL_TO,
HISTC_DSO_FROM,
HISTC_DSO_TO,
HISTC_NR_COLS, /* Last entry */ HISTC_NR_COLS, /* Last entry */
}; };
...@@ -74,6 +79,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size, ...@@ -74,6 +79,12 @@ int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
struct hists *hists); struct hists *hists);
void hist_entry__free(struct hist_entry *); void hist_entry__free(struct hist_entry *);
struct hist_entry *__hists__add_branch_entry(struct hists *self,
struct addr_location *al,
struct symbol *sym_parent,
struct branch_info *bi,
u64 period);
void hists__output_resort(struct hists *self); void hists__output_resort(struct hists *self);
void hists__output_resort_threaded(struct hists *hists); void hists__output_resort_threaded(struct hists *hists);
void hists__collapse_resort(struct hists *self); void hists__collapse_resort(struct hists *self);
......
...@@ -229,6 +229,63 @@ static bool symbol__match_parent_regex(struct symbol *sym) ...@@ -229,6 +229,63 @@ static bool symbol__match_parent_regex(struct symbol *sym)
return 0; return 0;
} }
static const u8 cpumodes[] = {
PERF_RECORD_MISC_USER,
PERF_RECORD_MISC_KERNEL,
PERF_RECORD_MISC_GUEST_USER,
PERF_RECORD_MISC_GUEST_KERNEL
};
#define NCPUMODES (sizeof(cpumodes)/sizeof(u8))
static void ip__resolve_ams(struct machine *self, struct thread *thread,
struct addr_map_symbol *ams,
u64 ip)
{
struct addr_location al;
size_t i;
u8 m;
memset(&al, 0, sizeof(al));
for (i = 0; i < NCPUMODES; i++) {
m = cpumodes[i];
/*
* We cannot use the header.misc hint to determine whether a
* branch stack address is user, kernel, guest, hypervisor.
* Branches may straddle the kernel/user/hypervisor boundaries.
* Thus, we have to try consecutively until we find a match
* or else, the symbol is unknown
*/
thread__find_addr_location(thread, self, m, MAP__FUNCTION,
ip, &al, NULL);
if (al.sym)
goto found;
}
found:
ams->addr = ip;
ams->sym = al.sym;
ams->map = al.map;
}
struct branch_info *machine__resolve_bstack(struct machine *self,
struct thread *thr,
struct branch_stack *bs)
{
struct branch_info *bi;
unsigned int i;
bi = calloc(bs->nr, sizeof(struct branch_info));
if (!bi)
return NULL;
for (i = 0; i < bs->nr; i++) {
ip__resolve_ams(self, thr, &bi[i].to, bs->entries[i].to);
ip__resolve_ams(self, thr, &bi[i].from, bs->entries[i].from);
bi[i].flags = bs->entries[i].flags;
}
return bi;
}
int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel, int machine__resolve_callchain(struct machine *self, struct perf_evsel *evsel,
struct thread *thread, struct thread *thread,
struct ip_callchain *chain, struct ip_callchain *chain,
...@@ -697,6 +754,18 @@ static void callchain__printf(struct perf_sample *sample) ...@@ -697,6 +754,18 @@ static void callchain__printf(struct perf_sample *sample)
i, sample->callchain->ips[i]); i, sample->callchain->ips[i]);
} }
static void branch_stack__printf(struct perf_sample *sample)
{
uint64_t i;
printf("... branch stack: nr:%" PRIu64 "\n", sample->branch_stack->nr);
for (i = 0; i < sample->branch_stack->nr; i++)
printf("..... %2"PRIu64": %016" PRIx64 " -> %016" PRIx64 "\n",
i, sample->branch_stack->entries[i].from,
sample->branch_stack->entries[i].to);
}
static void perf_session__print_tstamp(struct perf_session *session, static void perf_session__print_tstamp(struct perf_session *session,
union perf_event *event, union perf_event *event,
struct perf_sample *sample) struct perf_sample *sample)
...@@ -744,6 +813,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event, ...@@ -744,6 +813,9 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
if (session->sample_type & PERF_SAMPLE_CALLCHAIN) if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
callchain__printf(sample); callchain__printf(sample);
if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
branch_stack__printf(sample);
} }
static struct machine * static struct machine *
......
...@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel ...@@ -73,6 +73,10 @@ int perf_session__resolve_callchain(struct perf_session *self, struct perf_evsel
struct ip_callchain *chain, struct ip_callchain *chain,
struct symbol **parent); struct symbol **parent);
struct branch_info *machine__resolve_bstack(struct machine *self,
struct thread *thread,
struct branch_stack *bs);
bool perf_session__has_traces(struct perf_session *self, const char *msg); bool perf_session__has_traces(struct perf_session *self, const char *msg);
void mem_bswap_64(void *src, int byte_size); void mem_bswap_64(void *src, int byte_size);
......
This diff is collapsed.
...@@ -31,11 +31,14 @@ extern const char *parent_pattern; ...@@ -31,11 +31,14 @@ extern const char *parent_pattern;
extern const char default_sort_order[]; extern const char default_sort_order[];
extern int sort__need_collapse; extern int sort__need_collapse;
extern int sort__has_parent; extern int sort__has_parent;
extern bool sort__branch_mode;
extern char *field_sep; extern char *field_sep;
extern struct sort_entry sort_comm; extern struct sort_entry sort_comm;
extern struct sort_entry sort_dso; extern struct sort_entry sort_dso;
extern struct sort_entry sort_sym; extern struct sort_entry sort_sym;
extern struct sort_entry sort_parent; extern struct sort_entry sort_parent;
extern struct sort_entry sort_lbr_dso;
extern struct sort_entry sort_lbr_sym;
extern enum sort_type sort__first_dimension; extern enum sort_type sort__first_dimension;
/** /**
...@@ -72,6 +75,7 @@ struct hist_entry { ...@@ -72,6 +75,7 @@ struct hist_entry {
struct hist_entry *pair; struct hist_entry *pair;
struct rb_root sorted_chain; struct rb_root sorted_chain;
}; };
struct branch_info *branch_info;
struct callchain_root callchain[0]; struct callchain_root callchain[0];
}; };
...@@ -82,6 +86,11 @@ enum sort_type { ...@@ -82,6 +86,11 @@ enum sort_type {
SORT_SYM, SORT_SYM,
SORT_PARENT, SORT_PARENT,
SORT_CPU, SORT_CPU,
SORT_DSO_FROM,
SORT_DSO_TO,
SORT_SYM_FROM,
SORT_SYM_TO,
SORT_MISPREDICT,
}; };
/* /*
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "map.h" #include "map.h"
#include "../perf.h"
#include <linux/list.h> #include <linux/list.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <stdio.h> #include <stdio.h>
...@@ -120,6 +121,18 @@ struct map_symbol { ...@@ -120,6 +121,18 @@ struct map_symbol {
bool has_children; bool has_children;
}; };
struct addr_map_symbol {
struct map *map;
struct symbol *sym;
u64 addr;
};
struct branch_info {
struct addr_map_symbol from;
struct addr_map_symbol to;
struct branch_flags flags;
};
struct addr_location { struct addr_location {
struct thread *thread; struct thread *thread;
struct map *map; struct map *map;
......
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