summaryrefslogtreecommitdiff
path: root/tools/perf/builtin-report.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/builtin-report.c')
-rw-r--r--tools/perf/builtin-report.c193
1 files changed, 103 insertions, 90 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index bd0ca81eeaca..9725aa375414 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -49,9 +49,9 @@ struct perf_report {
bool mem_mode;
struct perf_read_values show_threads_values;
const char *pretty_printing_style;
- symbol_filter_t annotate_init;
const char *cpu_list;
const char *symbol_filter_str;
+ float min_percent;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
@@ -61,6 +61,11 @@ static int perf_report_config(const char *var, const char *value, void *cb)
symbol_conf.event_group = perf_config_bool(var, value);
return 0;
}
+ if (!strcmp(var, "report.percent-limit")) {
+ struct perf_report *rep = cb;
+ rep->min_percent = strtof(value, NULL);
+ return 0;
+ }
return perf_default_config(var, value, cb);
}
@@ -83,7 +88,7 @@ static int perf_report__add_mem_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread,
- sample, &parent);
+ sample, &parent, al);
if (err)
return err;
}
@@ -174,7 +179,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain)
&& sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread,
- sample, &parent);
+ sample, &parent, al);
if (err)
return err;
}
@@ -187,6 +192,9 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
for (i = 0; i < sample->branch_stack->nr; i++) {
if (rep->hide_unresolved && !(bi[i].from.sym && bi[i].to.sym))
continue;
+
+ err = -ENOMEM;
+
/*
* The report shows the percentage of total branches captured
* and not events sampled. Thus we use a pseudo period of 1.
@@ -195,7 +203,6 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
&bi[i], 1, 1);
if (he) {
struct annotation *notes;
- err = -ENOMEM;
bx = he->branch_info;
if (bx->from.sym && use_browser == 1 && sort__has_sym) {
notes = symbol__annotation(bx->from.sym);
@@ -226,11 +233,12 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
}
evsel->hists.stats.total_period += 1;
hists__inc_nr_events(&evsel->hists, PERF_RECORD_SAMPLE);
- err = 0;
} else
- return -ENOMEM;
+ goto out;
}
+ err = 0;
out:
+ free(bi);
return err;
}
@@ -245,7 +253,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
err = machine__resolve_callchain(machine, evsel, al->thread,
- sample, &parent);
+ sample, &parent, al);
if (err)
return err;
}
@@ -294,9 +302,9 @@ static int process_sample_event(struct perf_tool *tool,
{
struct perf_report *rep = container_of(tool, struct perf_report, tool);
struct addr_location al;
+ int ret;
- if (perf_event__preprocess_sample(event, machine, &al, sample,
- rep->annotate_init) < 0) {
+ if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
fprintf(stderr, "problem processing %d event, skipping it.\n",
event->header.type);
return -1;
@@ -308,28 +316,25 @@ static int process_sample_event(struct perf_tool *tool,
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
return 0;
- if (sort__branch_mode == 1) {
- if (perf_report__add_branch_hist_entry(tool, &al, sample,
- evsel, machine)) {
+ if (sort__mode == SORT_MODE__BRANCH) {
+ ret = perf_report__add_branch_hist_entry(tool, &al, sample,
+ evsel, machine);
+ if (ret < 0)
pr_debug("problem adding lbr entry, skipping event\n");
- return -1;
- }
} else if (rep->mem_mode == 1) {
- if (perf_report__add_mem_hist_entry(tool, &al, sample,
- evsel, machine, event)) {
+ ret = perf_report__add_mem_hist_entry(tool, &al, sample,
+ evsel, machine, event);
+ if (ret < 0)
pr_debug("problem adding mem entry, skipping event\n");
- return -1;
- }
} else {
if (al.map != NULL)
al.map->dso->hit = 1;
- if (perf_evsel__add_hist_entry(evsel, &al, sample, machine)) {
+ ret = perf_evsel__add_hist_entry(evsel, &al, sample, machine);
+ if (ret < 0)
pr_debug("problem incrementing symbol period, skipping event\n");
- return -1;
- }
}
- return 0;
+ return ret;
}
static int process_read_event(struct perf_tool *tool,
@@ -360,7 +365,7 @@ static int process_read_event(struct perf_tool *tool,
static int perf_report__setup_sample_type(struct perf_report *rep)
{
struct perf_session *self = rep->session;
- u64 sample_type = perf_evlist__sample_type(self->evlist);
+ u64 sample_type = perf_evlist__combined_sample_type(self->evlist);
if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) {
@@ -384,7 +389,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
}
}
- if (sort__branch_mode == 1) {
+ if (sort__mode == SORT_MODE__BRANCH) {
if (!self->fd_pipe &&
!(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
ui__error("Selected -b but no branch data. "
@@ -455,7 +460,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
continue;
hists__fprintf_nr_sample_events(rep, hists, evname, stdout);
- hists__fprintf(hists, true, 0, 0, stdout);
+ hists__fprintf(hists, true, 0, 0, rep->min_percent, stdout);
fprintf(stdout, "\n\n");
}
@@ -490,7 +495,7 @@ static int __cmd_report(struct perf_report *rep)
ret = perf_session__cpu_bitmap(session, rep->cpu_list,
rep->cpu_bitmap);
if (ret)
- goto out_delete;
+ return ret;
}
if (use_browser <= 0)
@@ -501,11 +506,11 @@ static int __cmd_report(struct perf_report *rep)
ret = perf_report__setup_sample_type(rep);
if (ret)
- goto out_delete;
+ return ret;
ret = perf_session__process_events(session, &rep->tool);
if (ret)
- goto out_delete;
+ return ret;
kernel_map = session->machines.host.vmlinux_maps[MAP__FUNCTION];
kernel_kmap = map__kmap(kernel_map);
@@ -540,7 +545,7 @@ static int __cmd_report(struct perf_report *rep)
if (dump_trace) {
perf_session__fprintf_nr_events(session, stdout);
- goto out_delete;
+ return 0;
}
nr_samples = 0;
@@ -565,7 +570,7 @@ static int __cmd_report(struct perf_report *rep)
if (nr_samples == 0) {
ui__error("The %s file has no samples!\n", session->filename);
- goto out_delete;
+ return 0;
}
list_for_each_entry(pos, &session->evlist->entries, node)
@@ -574,8 +579,8 @@ static int __cmd_report(struct perf_report *rep)
if (use_browser > 0) {
if (use_browser == 1) {
ret = perf_evlist__tui_browse_hists(session->evlist,
- help,
- NULL,
+ help, NULL,
+ rep->min_percent,
&session->header.env);
/*
* Usually "ret" is the last pressed key, and we only
@@ -586,24 +591,11 @@ static int __cmd_report(struct perf_report *rep)
} else if (use_browser == 2) {
perf_evlist__gtk_browse_hists(session->evlist, help,
- NULL);
+ NULL, rep->min_percent);
}
} else
perf_evlist__tty_browse_hists(session->evlist, rep, help);
-out_delete:
- /*
- * Speed up the exit process, for large files this can
- * take quite a while.
- *
- * XXX Enable this when using valgrind or if we ever
- * librarize this command.
- *
- * Also experiment with obstacks to see how much speed
- * up we'll get here.
- *
- * perf_session__delete(session);
- */
return ret;
}
@@ -673,12 +665,23 @@ parse_callchain_opt(const struct option *opt, const char *arg, int unset)
}
/* get the call chain order */
- if (!strcmp(tok2, "caller"))
+ if (!strncmp(tok2, "caller", strlen("caller")))
callchain_param.order = ORDER_CALLER;
- else if (!strcmp(tok2, "callee"))
+ else if (!strncmp(tok2, "callee", strlen("callee")))
callchain_param.order = ORDER_CALLEE;
else
return -1;
+
+ /* Get the sort key */
+ tok2 = strtok(NULL, ",");
+ if (!tok2)
+ goto setup;
+ if (!strncmp(tok2, "function", strlen("function")))
+ callchain_param.key = CCKEY_FUNCTION;
+ else if (!strncmp(tok2, "address", strlen("address")))
+ callchain_param.key = CCKEY_ADDRESS;
+ else
+ return -1;
setup:
if (callchain_register_param(&callchain_param) < 0) {
fprintf(stderr, "Can't register callchain params\n");
@@ -687,11 +690,41 @@ setup:
return 0;
}
+int
+report_parse_ignore_callees_opt(const struct option *opt __maybe_unused,
+ const char *arg, int unset __maybe_unused)
+{
+ if (arg) {
+ int err = regcomp(&ignore_callees_regex, arg, REG_EXTENDED);
+ if (err) {
+ char buf[BUFSIZ];
+ regerror(err, &ignore_callees_regex, buf, sizeof(buf));
+ pr_err("Invalid --ignore-callees regex: %s\n%s", arg, buf);
+ return -1;
+ }
+ have_ignore_callees = 1;
+ }
+
+ return 0;
+}
+
static int
parse_branch_mode(const struct option *opt __maybe_unused,
const char *str __maybe_unused, int unset)
{
- sort__branch_mode = !unset;
+ int *branch_mode = opt->value;
+
+ *branch_mode = !unset;
+ return 0;
+}
+
+static int
+parse_percent_limit(const struct option *opt, const char *str,
+ int unset __maybe_unused)
+{
+ struct perf_report *rep = opt->value;
+
+ rep->min_percent = strtof(str, NULL);
return 0;
}
@@ -700,6 +733,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
struct perf_session *session;
struct stat st;
bool has_br_stack = false;
+ int branch_mode = -1;
int ret = -1;
char callchain_default_opt[] = "fractal,0.5,callee";
const char * const report_usage[] = {
@@ -716,7 +750,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
.lost = perf_event__process_lost,
.read = process_read_event,
.attr = perf_event__process_attr,
- .event_type = perf_event__process_event_type,
.tracing_data = perf_event__process_tracing_data,
.build_id = perf_event__process_build_id,
.ordered_samples = true,
@@ -760,10 +793,13 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
"Only display entries with parent-match"),
OPT_CALLBACK_DEFAULT('g', "call-graph", &report, "output_type,min_percent[,print_limit],call_order",
- "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit and callchain order. "
- "Default: fractal,0.5,callee", &parse_callchain_opt, callchain_default_opt),
+ "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address). "
+ "Default: fractal,0.5,callee,function", &parse_callchain_opt, callchain_default_opt),
OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
"alias for inverted call graph"),
+ OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
+ "ignore callees of these functions in call graphs",
+ report_parse_ignore_callees_opt),
OPT_STRING('d', "dsos", &symbol_conf.dso_list_str, "dso[,dso...]",
"only consider symbols in these dsos"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
@@ -796,17 +832,19 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"Show a column with the sum of periods"),
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
"Show event group information together"),
- OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
+ OPT_CALLBACK_NOOPT('b', "branch-stack", &branch_mode, "",
"use branch records for histogram filling", parse_branch_mode),
OPT_STRING(0, "objdump", &objdump_path, "path",
"objdump binary to use for disassembly and annotations"),
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
"Disable symbol demangling"),
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
+ OPT_CALLBACK(0, "percent-limit", &report, "percent",
+ "Don't show entries under that percent", parse_percent_limit),
OPT_END()
};
- perf_config(perf_report_config, NULL);
+ perf_config(perf_report_config, &report);
argc = parse_options(argc, argv, options, report_usage, 0);
@@ -831,7 +869,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
setup_browser(true);
else {
use_browser = 0;
- perf_hpp__column_enable(PERF_HPP__OVERHEAD);
perf_hpp__init();
}
@@ -846,11 +883,11 @@ repeat:
has_br_stack = perf_header__has_feat(&session->header,
HEADER_BRANCH_STACK);
- if (sort__branch_mode == -1 && has_br_stack)
- sort__branch_mode = 1;
+ if (branch_mode == -1 && has_br_stack)
+ sort__mode = SORT_MODE__BRANCH;
- /* sort__branch_mode could be 0 if --no-branch-stack */
- if (sort__branch_mode == 1) {
+ /* sort__mode could be NORMAL if --no-branch-stack */
+ if (sort__mode == SORT_MODE__BRANCH) {
/*
* if no sort_order is provided, then specify
* branch-mode specific order
@@ -861,10 +898,12 @@ repeat:
}
if (report.mem_mode) {
- if (sort__branch_mode == 1) {
+ if (sort__mode == SORT_MODE__BRANCH) {
fprintf(stderr, "branch and mem mode incompatible\n");
goto error;
}
+ sort__mode = SORT_MODE__MEMORY;
+
/*
* if no sort_order is provided, then specify
* branch-mode specific order
@@ -883,7 +922,8 @@ repeat:
*/
if (use_browser == 1 && sort__has_sym) {
symbol_conf.priv_size = sizeof(struct annotation);
- report.annotate_init = symbol__annotate_init;
+ machines__set_symbol_filter(&session->machines,
+ symbol__annotate_init);
/*
* For searching by name on the "Browse map details".
* providing it only in verbose mode not to bloat too
@@ -907,16 +947,7 @@ repeat:
if (parent_pattern != default_parent_pattern) {
if (sort_dimension__add("parent") < 0)
goto error;
-
- /*
- * Only show the parent fields if we explicitly
- * sort that way. If we only use parent machinery
- * for filtering, we don't want it.
- */
- if (!strstr(sort_order, "parent"))
- sort_parent.elide = 1;
- } else
- symbol_conf.exclude_other = false;
+ }
if (argc) {
/*
@@ -929,25 +960,7 @@ repeat:
report.symbol_filter_str = argv[0];
}
- sort_entry__setup_elide(&sort_comm, symbol_conf.comm_list, "comm", stdout);
-
- if (sort__branch_mode == 1) {
- sort_entry__setup_elide(&sort_dso_from, symbol_conf.dso_from_list, "dso_from", stdout);
- sort_entry__setup_elide(&sort_dso_to, symbol_conf.dso_to_list, "dso_to", stdout);
- sort_entry__setup_elide(&sort_sym_from, symbol_conf.sym_from_list, "sym_from", stdout);
- sort_entry__setup_elide(&sort_sym_to, symbol_conf.sym_to_list, "sym_to", stdout);
- } else {
- if (report.mem_mode) {
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "symbol_daddr", stdout);
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso_daddr", stdout);
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "mem", stdout);
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "local_weight", stdout);
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "tlb", stdout);
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "snoop", stdout);
- }
- sort_entry__setup_elide(&sort_dso, symbol_conf.dso_list, "dso", stdout);
- sort_entry__setup_elide(&sort_sym, symbol_conf.sym_list, "symbol", stdout);
- }
+ sort__setup_elide(stdout);
ret = __cmd_report(&report);
if (ret == K_SWITCH_INPUT_DATA) {