summaryrefslogtreecommitdiffstats
path: root/kernel/tools/perf/builtin-report.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/tools/perf/builtin-report.c')
-rw-r--r--kernel/tools/perf/builtin-report.c154
1 files changed, 122 insertions, 32 deletions
diff --git a/kernel/tools/perf/builtin-report.c b/kernel/tools/perf/builtin-report.c
index b63aeda71..f256fac1e 100644
--- a/kernel/tools/perf/builtin-report.c
+++ b/kernel/tools/perf/builtin-report.c
@@ -36,13 +36,15 @@
#include "util/data.h"
#include "arch/common.h"
+#include "util/auxtrace.h"
+
#include <dlfcn.h>
#include <linux/bitmap.h>
struct report {
struct perf_tool tool;
struct perf_session *session;
- bool force, use_tui, use_gtk, use_stdio;
+ bool use_tui, use_gtk, use_stdio;
bool hide_unresolved;
bool dont_use_callchains;
bool show_full_info;
@@ -51,6 +53,7 @@ struct report {
bool mem_mode;
bool header;
bool header_only;
+ bool nonany_branch_mode;
int max_stack;
struct perf_read_values show_threads_values;
const char *pretty_printing_style;
@@ -59,6 +62,7 @@ struct report {
float min_percent;
u64 nr_entries;
u64 queue_size;
+ int socket_filter;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
};
@@ -100,6 +104,9 @@ static int hist_iter__report_callback(struct hist_entry_iter *iter,
if (!ui__has_annotation())
return 0;
+ hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
+ rep->nonany_branch_mode);
+
if (sort__mode == SORT_MODE__BRANCH) {
bi = he->branch_info;
err = addr_map_symbol__inc_samples(&bi->from, evsel->idx);
@@ -137,10 +144,12 @@ static int process_sample_event(struct perf_tool *tool,
struct report *rep = container_of(tool, struct report, tool);
struct addr_location al;
struct hist_entry_iter iter = {
- .hide_unresolved = rep->hide_unresolved,
- .add_entry_cb = hist_iter__report_callback,
+ .evsel = evsel,
+ .sample = sample,
+ .hide_unresolved = rep->hide_unresolved,
+ .add_entry_cb = hist_iter__report_callback,
};
- int ret;
+ int ret = 0;
if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) {
pr_debug("problem processing %d event, skipping it.\n",
@@ -149,28 +158,35 @@ static int process_sample_event(struct perf_tool *tool,
}
if (rep->hide_unresolved && al.sym == NULL)
- return 0;
+ goto out_put;
if (rep->cpu_list && !test_bit(sample->cpu, rep->cpu_bitmap))
- return 0;
+ goto out_put;
- if (sort__mode == SORT_MODE__BRANCH)
+ if (sort__mode == SORT_MODE__BRANCH) {
+ /*
+ * A non-synthesized event might not have a branch stack if
+ * branch stacks have been synthesized (using itrace options).
+ */
+ if (!sample->branch_stack)
+ goto out_put;
iter.ops = &hist_iter_branch;
- else if (rep->mem_mode)
+ } else if (rep->mem_mode) {
iter.ops = &hist_iter_mem;
- else if (symbol_conf.cumulate_callchain)
+ } else if (symbol_conf.cumulate_callchain) {
iter.ops = &hist_iter_cumulative;
- else
+ } else {
iter.ops = &hist_iter_normal;
+ }
if (al.map != NULL)
al.map->dso->hit = 1;
- ret = hist_entry_iter__add(&iter, &al, evsel, sample, rep->max_stack,
- rep);
+ ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
if (ret < 0)
pr_debug("problem adding hist entry, skipping event\n");
-
+out_put:
+ addr_location__put(&al);
return ret;
}
@@ -205,6 +221,15 @@ static int report__setup_sample_type(struct report *rep)
u64 sample_type = perf_evlist__combined_sample_type(session->evlist);
bool is_pipe = perf_data_file__is_pipe(session->file);
+ if (session->itrace_synth_opts->callchain ||
+ (!is_pipe &&
+ perf_header__has_feat(&session->header, HEADER_AUXTRACE) &&
+ !session->itrace_synth_opts->set))
+ sample_type |= PERF_SAMPLE_CALLCHAIN;
+
+ if (session->itrace_synth_opts->last_branch)
+ sample_type |= PERF_SAMPLE_BRANCH_STACK;
+
if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) {
ui__error("Selected --sort parent, but no "
@@ -254,6 +279,12 @@ static int report__setup_sample_type(struct report *rep)
else
callchain_param.record_mode = CALLCHAIN_FP;
}
+
+ /* ??? handle more cases than just ANY? */
+ if (!(perf_evlist__combined_branch_type(session->evlist) &
+ PERF_SAMPLE_BRANCH_ANY))
+ rep->nonany_branch_mode = true;
+
return 0;
}
@@ -272,6 +303,7 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
struct perf_evsel *evsel = hists_to_evsel(hists);
char buf[512];
size_t size = sizeof(buf);
+ int socked_id = hists->socket_filter;
if (symbol_conf.filter_relative) {
nr_samples = hists->stats.nr_non_filtered_samples;
@@ -302,11 +334,20 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
if (evname != NULL)
ret += fprintf(fp, " of event '%s'", evname);
+ if (symbol_conf.show_ref_callgraph &&
+ strstr(evname, "call-graph=no")) {
+ ret += fprintf(fp, ", show reference callgraph");
+ }
+
if (rep->mem_mode) {
ret += fprintf(fp, "\n# Total weight : %" PRIu64, nr_events);
ret += fprintf(fp, "\n# Sort order : %s", sort_order ? : default_mem_sort_order);
} else
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
+
+ if (socked_id > -1)
+ ret += fprintf(fp, "\n# Processor Socket: %d", socked_id);
+
return ret + fprintf(fp, "\n#\n");
}
@@ -316,6 +357,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
{
struct perf_evsel *pos;
+ fprintf(stdout, "#\n# Total Lost Samples: %" PRIu64 "\n#\n", evlist->stats.total_lost_samples);
evlist__for_each(evlist, pos) {
struct hists *hists = evsel__hists(pos);
const char *evname = perf_evsel__name(pos);
@@ -330,15 +372,14 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
}
if (sort_order == NULL &&
- parent_pattern == default_parent_pattern) {
+ parent_pattern == default_parent_pattern)
fprintf(stdout, "#\n# (%s)\n#\n", help);
- if (rep->show_threads) {
- bool style = !strcmp(rep->pretty_printing_style, "raw");
- perf_read_values_display(stdout, &rep->show_threads_values,
- style);
- perf_read_values_destroy(&rep->show_threads_values);
- }
+ if (rep->show_threads) {
+ bool style = !strcmp(rep->pretty_printing_style, "raw");
+ perf_read_values_display(stdout, &rep->show_threads_values,
+ style);
+ perf_read_values_destroy(&rep->show_threads_values);
}
return 0;
@@ -346,7 +387,7 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
static void report__warn_kptr_restrict(const struct report *rep)
{
- struct map *kernel_map = rep->session->machines.host.vmlinux_maps[MAP__FUNCTION];
+ struct map *kernel_map = machine__kernel_map(&rep->session->machines.host);
struct kmap *kernel_kmap = kernel_map ? map__kmap(kernel_map) : NULL;
if (kernel_map == NULL ||
@@ -431,6 +472,8 @@ static void report__collapse_hists(struct report *rep)
if (pos->idx == 0)
hists->symbol_filter_str = rep->symbol_filter_str;
+ hists->socket_filter = rep->socket_filter;
+
hists__collapse_resort(hists, &prog);
/* Non-group events are considered as leader */
@@ -582,14 +625,21 @@ parse_percent_limit(const struct option *opt, const char *str,
return 0;
}
+#define CALLCHAIN_DEFAULT_OPT "graph,0.5,caller,function"
+
+const char report_callchain_help[] = "Display call graph (stack chain/backtrace):\n\n"
+ CALLCHAIN_REPORT_HELP
+ "\n\t\t\t\tDefault: " CALLCHAIN_DEFAULT_OPT;
+
int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
{
struct perf_session *session;
+ struct itrace_synth_opts itrace_synth_opts = { .set = 0, };
struct stat st;
bool has_br_stack = false;
int branch_mode = -1;
bool branch_call_mode = false;
- char callchain_default_opt[] = "fractal,0.5,callee";
+ char callchain_default_opt[] = CALLCHAIN_DEFAULT_OPT;
const char * const report_usage[] = {
"perf report [<options>]",
NULL
@@ -607,11 +657,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
.attr = perf_event__process_attr,
.tracing_data = perf_event__process_tracing_data,
.build_id = perf_event__process_build_id,
+ .id_index = perf_event__process_id_index,
+ .auxtrace_info = perf_event__process_auxtrace_info,
+ .auxtrace = perf_event__process_auxtrace,
.ordered_events = true,
.ordering_requires_timestamps = true,
},
.max_stack = PERF_MAX_STACK_DEPTH,
.pretty_printing_style = "normal",
+ .socket_filter = -1,
};
const struct option options[] = {
OPT_STRING('i', "input", &input_name, "file",
@@ -624,7 +678,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"file", "vmlinux pathname"),
OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
"file", "kallsyms pathname"),
- OPT_BOOLEAN('f', "force", &report.force, "don't complain, do it"),
+ OPT_BOOLEAN('f', "force", &symbol_conf.force, "don't complain, do it"),
OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
"load module symbols - WARNING: use only with -k and LIVE kernel"),
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
@@ -645,15 +699,18 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
" Please refer the man page for the complete list."),
OPT_STRING('F', "fields", &field_order, "key[,keys...]",
"output field(s): overhead, period, sample plus all of sort keys"),
- OPT_BOOLEAN(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
+ OPT_BOOLEAN(0, "show-cpu-utilization", &symbol_conf.show_cpu_utilization,
"Show sample percentage for different cpu modes"),
+ OPT_BOOLEAN_FLAG(0, "showcpuutilization", &symbol_conf.show_cpu_utilization,
+ "Show sample percentage for different cpu modes", PARSE_OPT_HIDDEN),
OPT_STRING('p', "parent", &parent_pattern, "regex",
"regex filter to identify parent, see: '--sort parent'"),
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[,branch]",
- "Display callchains using output_type (graph, flat, fractal, or none) , min percent threshold, optional print limit, callchain order, key (function or address), add branches. "
- "Default: fractal,0.5,callee,function", &report_parse_callchain_opt, callchain_default_opt),
+ OPT_CALLBACK_DEFAULT('g', "call-graph", &report,
+ "print_type,threshold[,print_limit],order,sort_key[,branch]",
+ report_callchain_help, &report_parse_callchain_opt,
+ callchain_default_opt),
OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
"Accumulate callchains of children and show total overhead as well"),
OPT_INTEGER(0, "max-stack", &report.max_stack,
@@ -717,6 +774,15 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"Don't show entries under that percent", parse_percent_limit),
OPT_CALLBACK(0, "percentage", NULL, "relative|absolute",
"how to display percentage of filtered entries", parse_filter_percentage),
+ OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
+ "Instruction Tracing options",
+ itrace_parse_synth_opts),
+ OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename,
+ "Show full source file name path for source lines"),
+ OPT_BOOLEAN(0, "show-ref-call-graph", &symbol_conf.show_ref_callgraph,
+ "Show callgraph from reference event"),
+ OPT_INTEGER(0, "socket-filter", &report.socket_filter,
+ "only show processor socket that match with this filter"),
OPT_END()
};
struct perf_data_file file = {
@@ -731,6 +797,17 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
argc = parse_options(argc, argv, options, report_usage, 0);
+ if (symbol_conf.vmlinux_name &&
+ access(symbol_conf.vmlinux_name, R_OK)) {
+ pr_err("Invalid file: %s\n", symbol_conf.vmlinux_name);
+ return -EINVAL;
+ }
+ if (symbol_conf.kallsyms_name &&
+ access(symbol_conf.kallsyms_name, R_OK)) {
+ pr_err("Invalid file: %s\n", symbol_conf.kallsyms_name);
+ return -EINVAL;
+ }
+
if (report.use_stdio)
use_browser = 0;
else if (report.use_tui)
@@ -740,6 +817,12 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
if (report.inverted_callchain)
callchain_param.order = ORDER_CALLER;
+ if (symbol_conf.cumulate_callchain && !callchain_param.order_set)
+ callchain_param.order = ORDER_CALLER;
+
+ if (itrace_synth_opts.callchain &&
+ (int)itrace_synth_opts.callchain_sz > report.max_stack)
+ report.max_stack = itrace_synth_opts.callchain_sz;
if (!input_name || !strlen(input_name)) {
if (!fstat(STDIN_FILENO, &st) && S_ISFIFO(st.st_mode))
@@ -749,7 +832,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
}
file.path = input_name;
- file.force = report.force;
+ file.force = symbol_conf.force;
repeat:
session = perf_session__new(&file, false, &report.tool);
@@ -761,11 +844,16 @@ repeat:
report.queue_size);
}
+ session->itrace_synth_opts = &itrace_synth_opts;
+
report.session = session;
has_br_stack = perf_header__has_feat(&session->header,
HEADER_BRANCH_STACK);
+ if (itrace_synth_opts.last_branch)
+ has_br_stack = true;
+
/*
* Branch mode is a tristate:
* -1 means default, so decide based on the file having branch data.
@@ -803,8 +891,8 @@ repeat:
goto error;
}
- /* Force tty output for header output. */
- if (report.header || report.header_only)
+ /* Force tty output for header output and per-thread stat. */
+ if (report.header || report.header_only || report.show_threads)
use_browser = 0;
if (strcmp(input_name, "-") != 0)
@@ -815,8 +903,10 @@ repeat:
if (report.header || report.header_only) {
perf_session__fprintf_info(session, stdout,
report.show_full_info);
- if (report.header_only)
- return 0;
+ if (report.header_only) {
+ ret = 0;
+ goto error;
+ }
} else if (use_browser == 0) {
fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
stdout);