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.c264
1 files changed, 171 insertions, 93 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 69618fb0110b..add6b1c2aaf0 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -70,7 +70,7 @@
#include <linux/mman.h>
#ifdef HAVE_LIBTRACEEVENT
-#include <traceevent/event-parse.h>
+#include <event-parse.h>
#endif
struct report {
@@ -112,6 +112,8 @@ struct report {
u64 nr_entries;
u64 queue_size;
u64 total_cycles;
+ u64 total_samples;
+ u64 singlethreaded_samples;
int socket_filter;
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
struct branch_type_stat brtype_stat;
@@ -238,10 +240,11 @@ static void setup_forced_leader(struct report *report,
evlist__force_leader(evlist);
}
-static int process_feature_event(struct perf_session *session,
+static int process_feature_event(const struct perf_tool *tool,
+ struct perf_session *session,
union perf_event *event)
{
- struct report *rep = container_of(session->tool, struct report, tool);
+ struct report *rep = container_of(tool, struct report, tool);
if (event->feat.feat_id < HEADER_LAST_FEATURE)
return perf_event__process_feature(session, event);
@@ -263,7 +266,7 @@ static int process_feature_event(struct perf_session *session,
return 0;
}
-static int process_sample_event(struct perf_tool *tool,
+static int process_sample_event(const struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct evsel *evsel,
@@ -328,9 +331,13 @@ static int process_sample_event(struct perf_tool *tool,
if (ui__has_annotation() || rep->symbol_ipc || rep->total_cycles_mode) {
hist__account_cycles(sample->branch_stack, &al, sample,
rep->nonany_branch_mode,
- &rep->total_cycles);
+ &rep->total_cycles, evsel);
}
+ rep->total_samples++;
+ if (al.parallelism == 1)
+ rep->singlethreaded_samples++;
+
ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
if (ret < 0)
pr_debug("problem adding hist entry, skipping event\n");
@@ -339,7 +346,7 @@ out_put:
return ret;
}
-static int process_read_event(struct perf_tool *tool,
+static int process_read_event(const struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample __maybe_unused,
struct evsel *evsel,
@@ -348,11 +355,9 @@ static int process_read_event(struct perf_tool *tool,
struct report *rep = container_of(tool, struct report, tool);
if (rep->show_threads) {
- const char *name = evsel__name(evsel);
int err = perf_read_values_add_value(&rep->show_threads_values,
event->read.pid, event->read.tid,
- evsel->core.idx,
- name,
+ evsel,
event->read.value);
if (err)
@@ -409,7 +414,7 @@ static int report__setup_sample_type(struct report *rep)
/* Silently ignore if callchain is missing */
if (!(sample_type & PERF_SAMPLE_CALLCHAIN)) {
symbol_conf.cumulate_callchain = false;
- perf_hpp__cancel_cumulate();
+ perf_hpp__cancel_cumulate(session->evlist);
}
}
@@ -443,7 +448,7 @@ static int report__setup_sample_type(struct report *rep)
}
}
- callchain_param_setup(sample_type, perf_env__arch(&rep->session->header.env));
+ callchain_param_setup(sample_type, perf_env__arch(perf_session__env(rep->session)));
if (rep->stitch_lbr && (callchain_param.record_mode != CALLCHAIN_LBR)) {
ui__warning("Can't find LBR callchain. Switch off --stitch-lbr.\n"
@@ -455,7 +460,7 @@ static int report__setup_sample_type(struct report *rep)
if (!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_ANY))
rep->nonany_branch_mode = true;
-#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_DWARF_SUPPORT)
+#if !defined(HAVE_LIBUNWIND_SUPPORT) && !defined(HAVE_LIBDW_SUPPORT)
if (dwarf_callchain_users) {
ui__warning("Please install libunwind or libdw "
"development packages during the perf build.\n");
@@ -525,7 +530,10 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
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);
+ if (sort_order || !field_order) {
+ ret += fprintf(fp, "\n# Sort order : %s",
+ sort_order ? : default_mem_sort_order);
+ }
} else
ret += fprintf(fp, "\n# Event count (approx.): %" PRIu64, nr_events);
@@ -543,7 +551,7 @@ static int evlist__tui_block_hists_browse(struct evlist *evlist, struct report *
evlist__for_each_entry(evlist, pos) {
ret = report__browse_block_hists(&rep->block_reports[i++].hist,
rep->min_percent, pos,
- &rep->session->header.env);
+ perf_session__env(rep->session));
if (ret != 0)
return ret;
}
@@ -565,6 +573,7 @@ static int evlist__tty_browse_hists(struct evlist *evlist, struct report *rep, c
struct hists *hists = evsel__hists(pos);
const char *evname = evsel__name(pos);
+ i++;
if (symbol_conf.event_group && !evsel__is_group_leader(pos))
continue;
@@ -574,7 +583,14 @@ static int evlist__tty_browse_hists(struct evlist *evlist, struct report *rep, c
hists__fprintf_nr_sample_events(hists, rep, evname, stdout);
if (rep->total_cycles_mode) {
- report__browse_block_hists(&rep->block_reports[i++].hist,
+ char *buf;
+
+ if (!annotation_br_cntr_abbr_list(&buf, pos, true)) {
+ fprintf(stdout, "%s", buf);
+ fprintf(stdout, "#\n");
+ free(buf);
+ }
+ report__browse_block_hists(&rep->block_reports[i - 1].hist,
rep->min_percent, pos, NULL);
continue;
}
@@ -670,7 +686,7 @@ static int report__browse_hists(struct report *rep)
}
ret = evlist__tui_browse_hists(evlist, help, NULL, rep->min_percent,
- &session->header.env, true);
+ perf_session__env(session), true);
/*
* Usually "ret" is the last pressed key, and we only
* care if the key notifies us to switch data file.
@@ -765,7 +781,7 @@ static void report__output_resort(struct report *rep)
ui_progress__finish();
}
-static int count_sample_event(struct perf_tool *tool __maybe_unused,
+static int count_sample_event(const struct perf_tool *tool __maybe_unused,
union perf_event *event __maybe_unused,
struct perf_sample *sample __maybe_unused,
struct evsel *evsel,
@@ -777,7 +793,7 @@ static int count_sample_event(struct perf_tool *tool __maybe_unused,
return 0;
}
-static int count_lost_samples_event(struct perf_tool *tool,
+static int count_lost_samples_event(const struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
struct machine *machine __maybe_unused)
@@ -787,22 +803,28 @@ static int count_lost_samples_event(struct perf_tool *tool,
evsel = evlist__id2evsel(rep->session->evlist, sample->id);
if (evsel) {
- hists__inc_nr_lost_samples(evsel__hists(evsel),
- event->lost_samples.lost);
+ struct hists *hists = evsel__hists(evsel);
+ u32 count = event->lost_samples.lost;
+
+ if (event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF)
+ hists__inc_nr_dropped_samples(hists, count);
+ else
+ hists__inc_nr_lost_samples(hists, count);
}
return 0;
}
-static int process_attr(struct perf_tool *tool __maybe_unused,
+static int process_attr(const struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct evlist **pevlist);
static void stats_setup(struct report *rep)
{
- memset(&rep->tool, 0, sizeof(rep->tool));
+ perf_tool__init(&rep->tool, /*ordered_events=*/false);
rep->tool.attr = process_attr;
rep->tool.sample = count_sample_event;
rep->tool.lost_samples = count_lost_samples_event;
+ rep->tool.event_update = perf_event__process_event_update;
rep->tool.no_warn = true;
}
@@ -810,15 +832,14 @@ static int stats_print(struct report *rep)
{
struct perf_session *session = rep->session;
- perf_session__fprintf_nr_events(session, stdout, rep->skip_empty);
- evlist__fprintf_nr_events(session->evlist, stdout, rep->skip_empty);
+ perf_session__fprintf_nr_events(session, stdout);
+ evlist__fprintf_nr_events(session->evlist, stdout);
return 0;
}
static void tasks_setup(struct report *rep)
{
- memset(&rep->tool, 0, sizeof(rep->tool));
- rep->tool.ordered_events = true;
+ perf_tool__init(&rep->tool, /*ordered_events=*/true);
if (rep->mmaps_mode) {
rep->tool.mmap = perf_event__process_mmap;
rep->tool.mmap2 = perf_event__process_mmap2;
@@ -841,17 +862,24 @@ static int maps__fprintf_task_cb(struct map *map, void *data)
struct maps__fprintf_task_args *args = data;
const struct dso *dso = map__dso(map);
u32 prot = map__prot(map);
+ const struct dso_id *dso_id = dso__id_const(dso);
int ret;
+ char buf[SBUILD_ID_SIZE];
+
+ if (dso_id->mmap2_valid)
+ snprintf(buf, sizeof(buf), "%" PRIu64, dso_id->ino);
+ else
+ build_id__snprintf(&dso_id->build_id, buf, sizeof(buf));
ret = fprintf(args->fp,
- "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n",
+ "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %s %s\n",
args->indent, "", map__start(map), map__end(map),
prot & PROT_READ ? 'r' : '-',
prot & PROT_WRITE ? 'w' : '-',
prot & PROT_EXEC ? 'x' : '-',
map__flags(map) ? 's' : 'p',
map__pgoff(map),
- dso__id_const(dso)->ino, dso__name(dso));
+ buf, dso__name(dso));
if (ret < 0)
return ret;
@@ -1068,6 +1096,11 @@ static int __cmd_report(struct report *rep)
return ret;
}
+ /* Don't show Latency column for non-parallel profiles by default. */
+ if (!symbol_conf.prefer_latency && rep->total_samples &&
+ rep->singlethreaded_samples * 100 / rep->total_samples >= 99)
+ perf_hpp__cancel_latency(session->evlist);
+
evlist__check_mem_load_aux(session->evlist);
if (rep->stats_mode)
@@ -1089,10 +1122,7 @@ static int __cmd_report(struct report *rep)
perf_session__fprintf_dsos(session, stdout);
if (dump_trace) {
- perf_session__fprintf_nr_events(session, stdout,
- rep->skip_empty);
- evlist__fprintf_nr_events(session->evlist, stdout,
- rep->skip_empty);
+ stats_print(rep);
return 0;
}
}
@@ -1122,18 +1152,23 @@ static int __cmd_report(struct report *rep)
report__output_resort(rep);
if (rep->total_cycles_mode) {
- int block_hpps[6] = {
+ int nr_hpps = 4;
+ int block_hpps[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT,
PERF_HPP_REPORT__BLOCK_LBR_CYCLES,
PERF_HPP_REPORT__BLOCK_CYCLES_PCT,
PERF_HPP_REPORT__BLOCK_AVG_CYCLES,
- PERF_HPP_REPORT__BLOCK_RANGE,
- PERF_HPP_REPORT__BLOCK_DSO,
};
+ if (session->evlist->nr_br_cntr > 0)
+ block_hpps[nr_hpps++] = PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER;
+
+ block_hpps[nr_hpps++] = PERF_HPP_REPORT__BLOCK_RANGE;
+ block_hpps[nr_hpps++] = PERF_HPP_REPORT__BLOCK_DSO;
+
rep->block_reports = block_info__create_report(session->evlist,
rep->total_cycles,
- block_hpps, 6,
+ block_hpps, nr_hpps,
&rep->nr_block_reports);
if (!rep->block_reports)
return -1;
@@ -1236,10 +1271,12 @@ parse_percent_limit(const struct option *opt, const char *str,
return 0;
}
-static int process_attr(struct perf_tool *tool __maybe_unused,
+static int process_attr(const struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct evlist **pevlist)
{
+ struct perf_session *session;
+ struct perf_env *env;
u64 sample_type;
int err;
@@ -1252,10 +1289,16 @@ static int process_attr(struct perf_tool *tool __maybe_unused,
* on events sample_type.
*/
sample_type = evlist__combined_sample_type(*pevlist);
- callchain_param_setup(sample_type, perf_env__arch((*pevlist)->env));
+ session = (*pevlist)->session;
+ env = perf_session__env(session);
+ callchain_param_setup(sample_type, perf_env__arch(env));
return 0;
}
+#define CALLCHAIN_BRANCH_SORT_ORDER \
+ "srcline,symbol,dso,callchain_branch_predicted," \
+ "callchain_branch_abort,callchain_branch_cycles"
+
int cmd_report(int argc, const char **argv)
{
struct perf_session *session;
@@ -1275,37 +1318,13 @@ int cmd_report(int argc, const char **argv)
NULL
};
struct report report = {
- .tool = {
- .sample = process_sample_event,
- .mmap = perf_event__process_mmap,
- .mmap2 = perf_event__process_mmap2,
- .comm = perf_event__process_comm,
- .namespaces = perf_event__process_namespaces,
- .cgroup = perf_event__process_cgroup,
- .exit = perf_event__process_exit,
- .fork = perf_event__process_fork,
- .lost = perf_event__process_lost,
- .read = process_read_event,
- .attr = process_attr,
-#ifdef HAVE_LIBTRACEEVENT
- .tracing_data = perf_event__process_tracing_data,
-#endif
- .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,
- .event_update = perf_event__process_event_update,
- .feature = process_feature_event,
- .ordered_events = true,
- .ordering_requires_timestamps = true,
- },
.max_stack = PERF_MAX_STACK_DEPTH,
.pretty_printing_style = "normal",
.socket_filter = -1,
.skip_empty = true,
};
- char *sort_order_help = sort_help("sort by key(s):");
- char *field_order_help = sort_help("output field(s): overhead period sample ");
+ char *sort_order_help = sort_help("sort by key(s):", SORT_MODE__NORMAL);
+ char *field_order_help = sort_help("output field(s):", SORT_MODE__NORMAL);
const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
const struct option options[] = {
OPT_STRING('i', "input", &input_name, "file",
@@ -1397,6 +1416,8 @@ int cmd_report(int argc, const char **argv)
symbol__config_symfs),
OPT_STRING('C', "cpu", &report.cpu_list, "cpu",
"list of cpus to profile"),
+ OPT_STRING(0, "parallelism", &symbol_conf.parallelism_list_str, "parallelism",
+ "only consider these parallelism levels (cpu set format)"),
OPT_BOOLEAN('I', "show-info", &report.show_full_info,
"Display extended information about perf.data file"),
OPT_BOOLEAN(0, "source", &annotate_opts.annotate_src,
@@ -1427,7 +1448,7 @@ int cmd_report(int argc, const char **argv)
OPT_STRING(0, "addr2line", &addr2line_path, "path",
"addr2line binary to use for line numbers"),
OPT_BOOLEAN(0, "demangle", &symbol_conf.demangle,
- "Disable symbol demangling"),
+ "Symbol demangling. Enabled by default, use --no-demangle to disable."),
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
"Enable kernel symbol demangling"),
OPT_BOOLEAN(0, "mem-mode", &report.mem_mode, "mem access profile"),
@@ -1473,6 +1494,10 @@ int cmd_report(int argc, const char **argv)
"Disable raw trace ordering"),
OPT_BOOLEAN(0, "skip-empty", &report.skip_empty,
"Do not display empty (or dummy) events in the output"),
+ OPT_BOOLEAN(0, "latency", &symbol_conf.prefer_latency,
+ "Show latency-centric profile rather than the default\n"
+ "\t\t\t CPU-consumption-centric profile\n"
+ "\t\t\t (requires perf record --latency flag)."),
OPT_END()
};
struct perf_data data = {
@@ -1480,6 +1505,7 @@ int cmd_report(int argc, const char **argv)
};
int ret = hists__init();
char sort_tmp[128];
+ bool ordered_events = true;
if (ret < 0)
goto exit;
@@ -1534,7 +1560,7 @@ int cmd_report(int argc, const char **argv)
report.tasks_mode = true;
if (dump_trace && report.disable_order)
- report.tool.ordered_events = false;
+ ordered_events = false;
if (quiet)
perf_quiet_option();
@@ -1559,10 +1585,37 @@ int cmd_report(int argc, const char **argv)
input_name = "perf.data";
}
+repeat:
data.path = input_name;
data.force = symbol_conf.force;
-repeat:
+ symbol_conf.skip_empty = report.skip_empty;
+
+ perf_tool__init(&report.tool, ordered_events);
+ report.tool.sample = process_sample_event;
+ report.tool.mmap = perf_event__process_mmap;
+ report.tool.mmap2 = perf_event__process_mmap2;
+ report.tool.comm = perf_event__process_comm;
+ report.tool.namespaces = perf_event__process_namespaces;
+ report.tool.cgroup = perf_event__process_cgroup;
+ report.tool.exit = perf_event__process_exit;
+ report.tool.fork = perf_event__process_fork;
+ report.tool.context_switch = perf_event__process_switch;
+ report.tool.lost = perf_event__process_lost;
+ report.tool.read = process_read_event;
+ report.tool.attr = process_attr;
+#ifdef HAVE_LIBTRACEEVENT
+ report.tool.tracing_data = perf_event__process_tracing_data;
+#endif
+ report.tool.build_id = perf_event__process_build_id;
+ report.tool.id_index = perf_event__process_id_index;
+ report.tool.auxtrace_info = perf_event__process_auxtrace_info;
+ report.tool.auxtrace = perf_event__process_auxtrace;
+ report.tool.event_update = perf_event__process_event_update;
+ report.tool.feature = process_feature_event;
+ report.tool.ordering_requires_timestamps = true;
+ report.tool.merge_deferred_callchains = !dump_trace;
+
session = perf_session__new(&data, &report.tool);
if (IS_ERR(session)) {
ret = PTR_ERR(session);
@@ -1622,7 +1675,7 @@ repeat:
symbol_conf.use_callchain = true;
callchain_register_param(&callchain_param);
if (sort_order == NULL)
- sort_order = "srcline,symbol,dso";
+ sort_order = CALLCHAIN_BRANCH_SORT_ORDER;
}
if (report.mem_mode) {
@@ -1635,16 +1688,10 @@ repeat:
}
if (symbol_conf.report_hierarchy) {
- /* disable incompatible options */
- symbol_conf.cumulate_callchain = false;
-
- if (field_order) {
- pr_err("Error: --hierarchy and --fields options cannot be used together\n");
- parse_options_usage(report_usage, options, "F", 1);
- parse_options_usage(NULL, options, "hierarchy", 0);
- goto error;
- }
-
+ /*
+ * The hist entries in hierarchy are added during the collpase
+ * phase. Let's enable it even if no sort keys require it.
+ */
perf_hpp_list.need_collapse = true;
}
@@ -1684,7 +1731,10 @@ repeat:
report.data_type = true;
annotate_opts.annotate_src = false;
-#ifndef HAVE_DWARF_GETLOCATIONS_SUPPORT
+ /* disable incompatible options */
+ symbol_conf.cumulate_callchain = false;
+
+#ifndef HAVE_LIBDW_SUPPORT
pr_err("Error: Data type profiling is disabled due to missing DWARF support\n");
goto error;
#endif
@@ -1700,26 +1750,49 @@ repeat:
symbol_conf.annotate_data_sample = true;
}
- if (sort_order && strstr(sort_order, "ipc")) {
- parse_options_usage(report_usage, options, "s", 1);
- goto error;
+ symbol_conf.enable_latency = true;
+ if (report.disable_order || !perf_session__has_switch_events(session)) {
+ if (symbol_conf.parallelism_list_str ||
+ symbol_conf.prefer_latency ||
+ (sort_order && (strstr(sort_order, "latency") ||
+ strstr(sort_order, "parallelism"))) ||
+ (field_order && (strstr(field_order, "latency") ||
+ strstr(field_order, "parallelism")))) {
+ if (report.disable_order)
+ ui__error("Use of latency profile or parallelism is incompatible with --disable-order.\n");
+ else
+ ui__error("Use of latency profile or parallelism requires --latency flag during record.\n");
+ return -1;
+ }
+ /*
+ * If user did not ask for anything related to
+ * latency/parallelism explicitly, just don't show it.
+ */
+ symbol_conf.enable_latency = false;
}
- if (sort_order && strstr(sort_order, "symbol")) {
- if (sort__mode == SORT_MODE__BRANCH) {
- snprintf(sort_tmp, sizeof(sort_tmp), "%s,%s",
- sort_order, "ipc_lbr");
- report.symbol_ipc = true;
- } else {
- snprintf(sort_tmp, sizeof(sort_tmp), "%s,%s",
- sort_order, "ipc_null");
+ if (last_key != K_SWITCH_INPUT_DATA) {
+ if (sort_order && strstr(sort_order, "ipc")) {
+ parse_options_usage(report_usage, options, "s", 1);
+ goto error;
}
- sort_order = sort_tmp;
+ if (sort_order && strstr(sort_order, "symbol")) {
+ if (sort__mode == SORT_MODE__BRANCH) {
+ snprintf(sort_tmp, sizeof(sort_tmp), "%s,%s",
+ sort_order, "ipc_lbr");
+ report.symbol_ipc = true;
+ } else {
+ snprintf(sort_tmp, sizeof(sort_tmp), "%s,%s",
+ sort_order, "ipc_null");
+ }
+
+ sort_order = sort_tmp;
+ }
}
if ((last_key != K_SWITCH_INPUT_DATA && last_key != K_RELOAD) &&
- (setup_sorting(session->evlist) < 0)) {
+ (setup_sorting(session->evlist, perf_session__env(session)) < 0)) {
if (sort_order)
parse_options_usage(report_usage, options, "s", 1);
if (field_order)
@@ -1775,7 +1848,7 @@ repeat:
annotation_config__init();
}
- if (symbol__init(&session->header.env) < 0)
+ if (symbol__init(perf_session__env(session)) < 0)
goto error;
if (report.time_str) {
@@ -1807,6 +1880,11 @@ repeat:
if (ret == K_SWITCH_INPUT_DATA || ret == K_RELOAD) {
perf_session__delete(session);
last_key = K_SWITCH_INPUT_DATA;
+ /*
+ * To support switching between data with and without callchains.
+ * report__setup_sample_type() will update it properly.
+ */
+ symbol_conf.use_callchain = false;
goto repeat;
} else
ret = 0;