summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-c2c.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-c2c.c')
-rw-r--r--tools/perf/builtin-c2c.c195
1 files changed, 168 insertions, 27 deletions
diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c
index 9e9ff471ddd1..d390ae4e3ec8 100644
--- a/tools/perf/builtin-c2c.c
+++ b/tools/perf/builtin-c2c.c
@@ -45,6 +45,8 @@
#include "pmus.h"
#include "string2.h"
#include "util/util.h"
+#include "util/symbol.h"
+#include "util/annotate.h"
struct c2c_hists {
struct hists hists;
@@ -62,6 +64,7 @@ struct compute_stats {
struct c2c_hist_entry {
struct c2c_hists *hists;
+ struct evsel *evsel;
struct c2c_stats stats;
unsigned long *cpuset;
unsigned long *nodeset;
@@ -225,6 +228,12 @@ he__get_c2c_hists(struct hist_entry *he,
return hists;
}
+static void c2c_he__set_evsel(struct c2c_hist_entry *c2c_he,
+ struct evsel *evsel)
+{
+ c2c_he->evsel = evsel;
+}
+
static void c2c_he__set_cpu(struct c2c_hist_entry *c2c_he,
struct perf_sample *sample)
{
@@ -275,6 +284,33 @@ static void compute_stats(struct c2c_hist_entry *c2c_he,
update_stats(&cstats->load, weight);
}
+/*
+ * Return true if annotation is possible. When list is NULL,
+ * it means that we are called at the c2c_browser level,
+ * in that case we allow annotation to be initialized. When list
+ * is non-NULL, it means that we are called at the cacheline_browser
+ * level, in that case we allow annotation only if use_browser
+ * is set and symbol information is available.
+ */
+static bool perf_c2c__has_annotation(struct perf_hpp_list *list)
+{
+ if (use_browser != 1)
+ return false;
+ return !list || list->sym;
+}
+
+static void perf_c2c__evsel_hists_inc_stats(struct evsel *evsel,
+ struct hist_entry *he,
+ struct perf_sample *sample)
+{
+ struct hists *evsel_hists = evsel__hists(evsel);
+
+ hists__inc_nr_samples(evsel_hists, he->filtered);
+ evsel_hists->stats.total_period += sample->period;
+ if (!he->filtered)
+ evsel_hists->stats.total_non_filtered_period += sample->period;
+}
+
static int process_sample_event(const struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
@@ -286,7 +322,7 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
struct c2c_stats stats = { .nr_entries = 0, };
struct hist_entry *he;
struct addr_location al;
- struct mem_info *mi, *mi_dup;
+ struct mem_info *mi = NULL;
struct callchain_cursor *cursor;
int ret;
@@ -313,20 +349,15 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
goto out;
}
- /*
- * The mi object is released in hists__add_entry_ops,
- * if it gets sorted out into existing data, so we need
- * to take the copy now.
- */
- mi_dup = mem_info__get(mi);
-
c2c_decode_stats(&stats, mi);
he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
&al, NULL, NULL, mi, NULL,
sample, true);
- if (he == NULL)
- goto free_mi;
+ if (he == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
c2c_he = container_of(he, struct c2c_hist_entry, he);
c2c_add_stats(&c2c_he->stats, &stats);
@@ -334,8 +365,15 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
+
+ if (perf_c2c__has_annotation(NULL)) {
+ perf_c2c__evsel_hists_inc_stats(evsel, he, sample);
+ addr_map_symbol__inc_samples(mem_info__iaddr(mi), sample, evsel);
+ }
+
ret = hist_entry__append_callchain(he, sample);
if (!ret) {
@@ -350,17 +388,19 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
int cpu = sample->cpu == (unsigned int) -1 ? 0 : sample->cpu;
int node = c2c.cpu2node[cpu];
- mi = mi_dup;
-
c2c_hists = he__get_c2c_hists(he, c2c.cl_sort, 2, machine->env);
- if (!c2c_hists)
- goto free_mi;
+ if (!c2c_hists) {
+ ret = -ENOMEM;
+ goto out;
+ }
he = hists__add_entry_ops(&c2c_hists->hists, &c2c_entry_ops,
&al, NULL, NULL, mi, NULL,
sample, true);
- if (he == NULL)
- goto free_mi;
+ if (he == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
c2c_he = container_of(he, struct c2c_hist_entry, he);
c2c_add_stats(&c2c_he->stats, &stats);
@@ -371,20 +411,16 @@ static int process_sample_event(const struct perf_tool *tool __maybe_unused,
c2c_he__set_cpu(c2c_he, sample);
c2c_he__set_node(c2c_he, sample);
+ c2c_he__set_evsel(c2c_he, evsel);
hists__inc_nr_samples(&c2c_hists->hists, he->filtered);
ret = hist_entry__append_callchain(he, sample);
}
out:
+ mem_info__put(mi);
addr_location__exit(&al);
return ret;
-
-free_mi:
- mem_info__put(mi_dup);
- mem_info__put(mi);
- ret = -ENOMEM;
- goto out;
}
static const char * const c2c_usage[] = {
@@ -1997,6 +2033,9 @@ static int c2c_hists__init_sort(struct perf_hpp_list *hpp_list, char *name, stru
if (dim == &dim_dso)
hpp_list->dso = 1;
+ if (dim == &dim_symbol || dim == &dim_iaddr)
+ hpp_list->sym = 1;
+
perf_hpp_list__register_sort_field(hpp_list, &c2c_fmt->fmt);
return 0;
}
@@ -2550,6 +2589,44 @@ static void perf_c2c__hists_fprintf(FILE *out, struct perf_session *session)
}
#ifdef HAVE_SLANG_SUPPORT
+
+static int perf_c2c__toggle_annotation(struct hist_browser *browser)
+{
+ struct hist_entry *he = browser->he_selection;
+ struct symbol *sym = NULL;
+ struct annotated_source *src = NULL;
+ struct c2c_hist_entry *c2c_he = NULL;
+ u64 al_addr = NO_ADDR;
+
+ if (!perf_c2c__has_annotation(he->hists->hpp_list)) {
+ ui_browser__help_window(&browser->b, "No annotation support");
+ return 0;
+ }
+
+ if (he == NULL) {
+ ui_browser__help_window(&browser->b, "No entry selected for annotation");
+ return 0;
+ }
+
+ sym = he->ms.sym;
+ if (sym == NULL) {
+ ui_browser__help_window(&browser->b, "Can not annotate, no symbol found");
+ return 0;
+ }
+
+ src = symbol__hists(sym, 0);
+ if (src == NULL) {
+ ui_browser__help_window(&browser->b, "Failed to initialize annotation source");
+ return 0;
+ }
+
+ if (he->mem_info)
+ al_addr = mem_info__iaddr(he->mem_info)->al_addr;
+
+ c2c_he = container_of(he, struct c2c_hist_entry, he);
+ return hist_entry__tui_annotate(he, c2c_he->evsel, NULL, al_addr);
+}
+
static void c2c_browser__update_nr_entries(struct hist_browser *hb)
{
u64 nr_entries = 0;
@@ -2617,6 +2694,7 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
" ENTER Toggle callchains (if present) \n"
" n Toggle Node details info \n"
" s Toggle full length of symbol and source line columns \n"
+ " a Toggle annotation view \n"
" q Return back to cacheline list \n";
if (!he)
@@ -2651,6 +2729,9 @@ static int perf_c2c__browse_cacheline(struct hist_entry *he)
c2c.node_info = (c2c.node_info + 1) % 3;
setup_nodes_header();
break;
+ case 'a':
+ perf_c2c__toggle_annotation(browser);
+ break;
case 'q':
goto out;
case '?':
@@ -3006,6 +3087,7 @@ static int perf_c2c__report(int argc, const char **argv)
const char *display = NULL;
const char *coalesce = NULL;
bool no_source = false;
+ const char *disassembler_style = NULL, *objdump_path = NULL;
const struct option options[] = {
OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
"file", "vmlinux pathname"),
@@ -3033,6 +3115,10 @@ static int perf_c2c__report(int argc, const char **argv)
OPT_BOOLEAN(0, "stitch-lbr", &c2c.stitch_lbr,
"Enable LBR callgraph stitching approach"),
OPT_BOOLEAN(0, "double-cl", &chk_double_cl, "Detect adjacent cacheline false sharing"),
+ OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
+ "Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
OPT_PARENT(c2c_options),
OPT_END()
};
@@ -3040,6 +3126,12 @@ static int perf_c2c__report(int argc, const char **argv)
const char *output_str, *sort_str = NULL;
struct perf_env *env;
+ annotation_options__init();
+
+ err = hists__init();
+ if (err < 0)
+ goto out;
+
argc = parse_options(argc, argv, options, report_c2c_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (argc)
@@ -3052,6 +3144,27 @@ static int perf_c2c__report(int argc, const char **argv)
if (c2c.stats_only)
c2c.use_stdio = true;
+ /**
+ * Annotation related options disassembler_style, objdump_path are set
+ * in the c2c_options, so we can use them here.
+ */
+ if (disassembler_style) {
+ annotate_opts.disassembler_style = strdup(disassembler_style);
+ if (!annotate_opts.disassembler_style) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+ if (objdump_path) {
+ annotate_opts.objdump_path = strdup(objdump_path);
+ if (!annotate_opts.objdump_path) {
+ err = -ENOMEM;
+ pr_err("Failed to allocate memory for annotation options\n");
+ goto out;
+ }
+ }
+
err = symbol__validate_sym_arguments();
if (err)
goto out;
@@ -3126,6 +3239,38 @@ static int perf_c2c__report(int argc, const char **argv)
if (err)
goto out_mem2node;
+ if (c2c.use_stdio)
+ use_browser = 0;
+ else
+ use_browser = 1;
+
+ /*
+ * Only in the TUI browser we are doing integrated annotation,
+ * so don't allocate extra space that won't be used in the stdio
+ * implementation.
+ */
+ if (perf_c2c__has_annotation(NULL)) {
+ int ret = symbol__annotation_init();
+
+ if (ret < 0)
+ goto out_mem2node;
+ /*
+ * For searching by name on the "Browse map details".
+ * providing it only in verbose mode not to bloat too
+ * much struct symbol.
+ */
+ if (verbose > 0) {
+ /*
+ * XXX: Need to provide a less kludgy way to ask for
+ * more space per symbol, the u32 is for the index on
+ * the ui browser.
+ * See symbol__browser_index.
+ */
+ symbol_conf.priv_size += sizeof(u32);
+ }
+ annotation_config__init();
+ }
+
if (symbol__init(env) < 0)
goto out_mem2node;
@@ -3135,11 +3280,6 @@ static int perf_c2c__report(int argc, const char **argv)
goto out_mem2node;
}
- if (c2c.use_stdio)
- use_browser = 0;
- else
- use_browser = 1;
-
setup_browser(false);
err = perf_session__process_events(session);
@@ -3210,6 +3350,7 @@ out_mem2node:
out_session:
perf_session__delete(session);
out:
+ annotation_options__exit();
return err;
}