aboutsummaryrefslogtreecommitdiffstats
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.c283
1 files changed, 267 insertions, 16 deletions
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index af5dd038195e..42a52dcc41cd 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -15,6 +15,7 @@
15#include "util/color.h" 15#include "util/color.h"
16#include <linux/list.h> 16#include <linux/list.h>
17#include <linux/rbtree.h> 17#include <linux/rbtree.h>
18#include <linux/err.h>
18#include "util/symbol.h" 19#include "util/symbol.h"
19#include "util/callchain.h" 20#include "util/callchain.h"
20#include "util/values.h" 21#include "util/values.h"
@@ -51,6 +52,7 @@
51#include <sys/types.h> 52#include <sys/types.h>
52#include <sys/stat.h> 53#include <sys/stat.h>
53#include <unistd.h> 54#include <unistd.h>
55#include <linux/mman.h>
54 56
55struct report { 57struct report {
56 struct perf_tool tool; 58 struct perf_tool tool;
@@ -60,6 +62,9 @@ struct report {
60 bool show_threads; 62 bool show_threads;
61 bool inverted_callchain; 63 bool inverted_callchain;
62 bool mem_mode; 64 bool mem_mode;
65 bool stats_mode;
66 bool tasks_mode;
67 bool mmaps_mode;
63 bool header; 68 bool header;
64 bool header_only; 69 bool header_only;
65 bool nonany_branch_mode; 70 bool nonany_branch_mode;
@@ -69,7 +74,9 @@ struct report {
69 const char *cpu_list; 74 const char *cpu_list;
70 const char *symbol_filter_str; 75 const char *symbol_filter_str;
71 const char *time_str; 76 const char *time_str;
72 struct perf_time_interval ptime; 77 struct perf_time_interval *ptime_range;
78 int range_size;
79 int range_num;
73 float min_percent; 80 float min_percent;
74 u64 nr_entries; 81 u64 nr_entries;
75 u64 queue_size; 82 u64 queue_size;
@@ -162,12 +169,28 @@ static int hist_iter__branch_callback(struct hist_entry_iter *iter,
162 struct hist_entry *he = iter->he; 169 struct hist_entry *he = iter->he;
163 struct report *rep = arg; 170 struct report *rep = arg;
164 struct branch_info *bi; 171 struct branch_info *bi;
172 struct perf_sample *sample = iter->sample;
173 struct perf_evsel *evsel = iter->evsel;
174 int err;
175
176 if (!ui__has_annotation())
177 return 0;
178
179 hist__account_cycles(sample->branch_stack, al, sample,
180 rep->nonany_branch_mode);
165 181
166 bi = he->branch_info; 182 bi = he->branch_info;
183 err = addr_map_symbol__inc_samples(&bi->from, sample, evsel->idx);
184 if (err)
185 goto out;
186
187 err = addr_map_symbol__inc_samples(&bi->to, sample, evsel->idx);
188
167 branch_type_count(&rep->brtype_stat, &bi->flags, 189 branch_type_count(&rep->brtype_stat, &bi->flags,
168 bi->from.addr, bi->to.addr); 190 bi->from.addr, bi->to.addr);
169 191
170 return 0; 192out:
193 return err;
171} 194}
172 195
173static int process_sample_event(struct perf_tool *tool, 196static int process_sample_event(struct perf_tool *tool,
@@ -186,8 +209,10 @@ static int process_sample_event(struct perf_tool *tool,
186 }; 209 };
187 int ret = 0; 210 int ret = 0;
188 211
189 if (perf_time__skip_sample(&rep->ptime, sample->time)) 212 if (perf_time__ranges_skip_sample(rep->ptime_range, rep->range_num,
213 sample->time)) {
190 return 0; 214 return 0;
215 }
191 216
192 if (machine__resolve(machine, &al, sample) < 0) { 217 if (machine__resolve(machine, &al, sample) < 0) {
193 pr_debug("problem processing %d event, skipping it.\n", 218 pr_debug("problem processing %d event, skipping it.\n",
@@ -312,9 +337,10 @@ static int report__setup_sample_type(struct report *rep)
312 337
313 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) { 338 if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain) {
314 if ((sample_type & PERF_SAMPLE_REGS_USER) && 339 if ((sample_type & PERF_SAMPLE_REGS_USER) &&
315 (sample_type & PERF_SAMPLE_STACK_USER)) 340 (sample_type & PERF_SAMPLE_STACK_USER)) {
316 callchain_param.record_mode = CALLCHAIN_DWARF; 341 callchain_param.record_mode = CALLCHAIN_DWARF;
317 else if (sample_type & PERF_SAMPLE_BRANCH_STACK) 342 dwarf_callchain_users = true;
343 } else if (sample_type & PERF_SAMPLE_BRANCH_STACK)
318 callchain_param.record_mode = CALLCHAIN_LBR; 344 callchain_param.record_mode = CALLCHAIN_LBR;
319 else 345 else
320 callchain_param.record_mode = CALLCHAIN_FP; 346 callchain_param.record_mode = CALLCHAIN_FP;
@@ -377,6 +403,9 @@ static size_t hists__fprintf_nr_sample_events(struct hists *hists, struct report
377 if (evname != NULL) 403 if (evname != NULL)
378 ret += fprintf(fp, " of event '%s'", evname); 404 ret += fprintf(fp, " of event '%s'", evname);
379 405
406 if (rep->time_str)
407 ret += fprintf(fp, " (time slices: %s)", rep->time_str);
408
380 if (symbol_conf.show_ref_callgraph && 409 if (symbol_conf.show_ref_callgraph &&
381 strstr(evname, "call-graph=no")) { 410 strstr(evname, "call-graph=no")) {
382 ret += fprintf(fp, ", show reference callgraph"); 411 ret += fprintf(fp, ", show reference callgraph");
@@ -567,6 +596,174 @@ static void report__output_resort(struct report *rep)
567 ui_progress__finish(); 596 ui_progress__finish();
568} 597}
569 598
599static void stats_setup(struct report *rep)
600{
601 memset(&rep->tool, 0, sizeof(rep->tool));
602 rep->tool.no_warn = true;
603}
604
605static int stats_print(struct report *rep)
606{
607 struct perf_session *session = rep->session;
608
609 perf_session__fprintf_nr_events(session, stdout);
610 return 0;
611}
612
613static void tasks_setup(struct report *rep)
614{
615 memset(&rep->tool, 0, sizeof(rep->tool));
616 if (rep->mmaps_mode) {
617 rep->tool.mmap = perf_event__process_mmap;
618 rep->tool.mmap2 = perf_event__process_mmap2;
619 }
620 rep->tool.comm = perf_event__process_comm;
621 rep->tool.exit = perf_event__process_exit;
622 rep->tool.fork = perf_event__process_fork;
623 rep->tool.no_warn = true;
624}
625
626struct task {
627 struct thread *thread;
628 struct list_head list;
629 struct list_head children;
630};
631
632static struct task *tasks_list(struct task *task, struct machine *machine)
633{
634 struct thread *parent_thread, *thread = task->thread;
635 struct task *parent_task;
636
637 /* Already listed. */
638 if (!list_empty(&task->list))
639 return NULL;
640
641 /* Last one in the chain. */
642 if (thread->ppid == -1)
643 return task;
644
645 parent_thread = machine__find_thread(machine, -1, thread->ppid);
646 if (!parent_thread)
647 return ERR_PTR(-ENOENT);
648
649 parent_task = thread__priv(parent_thread);
650 list_add_tail(&task->list, &parent_task->children);
651 return tasks_list(parent_task, machine);
652}
653
654static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp)
655{
656 size_t printed = 0;
657 struct rb_node *nd;
658
659 for (nd = rb_first(&maps->entries); nd; nd = rb_next(nd)) {
660 struct map *map = rb_entry(nd, struct map, rb_node);
661
662 printed += fprintf(fp, "%*s %" PRIx64 "-%" PRIx64 " %c%c%c%c %08" PRIx64 " %" PRIu64 " %s\n",
663 indent, "", map->start, map->end,
664 map->prot & PROT_READ ? 'r' : '-',
665 map->prot & PROT_WRITE ? 'w' : '-',
666 map->prot & PROT_EXEC ? 'x' : '-',
667 map->flags & MAP_SHARED ? 's' : 'p',
668 map->pgoff,
669 map->ino, map->dso->name);
670 }
671
672 return printed;
673}
674
675static int map_groups__fprintf_task(struct map_groups *mg, int indent, FILE *fp)
676{
677 int printed = 0, i;
678 for (i = 0; i < MAP__NR_TYPES; ++i)
679 printed += maps__fprintf_task(&mg->maps[i], indent, fp);
680 return printed;
681}
682
683static void task__print_level(struct task *task, FILE *fp, int level)
684{
685 struct thread *thread = task->thread;
686 struct task *child;
687 int comm_indent = fprintf(fp, " %8d %8d %8d |%*s",
688 thread->pid_, thread->tid, thread->ppid,
689 level, "");
690
691 fprintf(fp, "%s\n", thread__comm_str(thread));
692
693 map_groups__fprintf_task(thread->mg, comm_indent, fp);
694
695 if (!list_empty(&task->children)) {
696 list_for_each_entry(child, &task->children, list)
697 task__print_level(child, fp, level + 1);
698 }
699}
700
701static int tasks_print(struct report *rep, FILE *fp)
702{
703 struct perf_session *session = rep->session;
704 struct machine *machine = &session->machines.host;
705 struct task *tasks, *task;
706 unsigned int nr = 0, itask = 0, i;
707 struct rb_node *nd;
708 LIST_HEAD(list);
709
710 /*
711 * No locking needed while accessing machine->threads,
712 * because --tasks is single threaded command.
713 */
714
715 /* Count all the threads. */
716 for (i = 0; i < THREADS__TABLE_SIZE; i++)
717 nr += machine->threads[i].nr;
718
719 tasks = malloc(sizeof(*tasks) * nr);
720 if (!tasks)
721 return -ENOMEM;
722
723 for (i = 0; i < THREADS__TABLE_SIZE; i++) {
724 struct threads *threads = &machine->threads[i];
725
726 for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) {
727 task = tasks + itask++;
728
729 task->thread = rb_entry(nd, struct thread, rb_node);
730 INIT_LIST_HEAD(&task->children);
731 INIT_LIST_HEAD(&task->list);
732 thread__set_priv(task->thread, task);
733 }
734 }
735
736 /*
737 * Iterate every task down to the unprocessed parent
738 * and link all in task children list. Task with no
739 * parent is added into 'list'.
740 */
741 for (itask = 0; itask < nr; itask++) {
742 task = tasks + itask;
743
744 if (!list_empty(&task->list))
745 continue;
746
747 task = tasks_list(task, machine);
748 if (IS_ERR(task)) {
749 pr_err("Error: failed to process tasks\n");
750 free(tasks);
751 return PTR_ERR(task);
752 }
753
754 if (task)
755 list_add_tail(&task->list, &list);
756 }
757
758 fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
759
760 list_for_each_entry(task, &list, list)
761 task__print_level(task, fp, 0);
762
763 free(tasks);
764 return 0;
765}
766
570static int __cmd_report(struct report *rep) 767static int __cmd_report(struct report *rep)
571{ 768{
572 int ret; 769 int ret;
@@ -598,12 +795,24 @@ static int __cmd_report(struct report *rep)
598 return ret; 795 return ret;
599 } 796 }
600 797
798 if (rep->stats_mode)
799 stats_setup(rep);
800
801 if (rep->tasks_mode)
802 tasks_setup(rep);
803
601 ret = perf_session__process_events(session); 804 ret = perf_session__process_events(session);
602 if (ret) { 805 if (ret) {
603 ui__error("failed to process sample\n"); 806 ui__error("failed to process sample\n");
604 return ret; 807 return ret;
605 } 808 }
606 809
810 if (rep->stats_mode)
811 return stats_print(rep);
812
813 if (rep->tasks_mode)
814 return tasks_print(rep, stdout);
815
607 report__warn_kptr_restrict(rep); 816 report__warn_kptr_restrict(rep);
608 817
609 evlist__for_each_entry(session->evlist, pos) 818 evlist__for_each_entry(session->evlist, pos)
@@ -760,6 +969,9 @@ int cmd_report(int argc, const char **argv)
760 OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"), 969 OPT_BOOLEAN('q', "quiet", &quiet, "Do not show any message"),
761 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 970 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
762 "dump raw trace in ASCII"), 971 "dump raw trace in ASCII"),
972 OPT_BOOLEAN(0, "stats", &report.stats_mode, "Display event stats"),
973 OPT_BOOLEAN(0, "tasks", &report.tasks_mode, "Display recorded tasks"),
974 OPT_BOOLEAN(0, "mmaps", &report.mmaps_mode, "Display recorded tasks memory maps"),
763 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 975 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
764 "file", "vmlinux pathname"), 976 "file", "vmlinux pathname"),
765 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, 977 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
@@ -907,6 +1119,9 @@ int cmd_report(int argc, const char **argv)
907 report.symbol_filter_str = argv[0]; 1119 report.symbol_filter_str = argv[0];
908 } 1120 }
909 1121
1122 if (report.mmaps_mode)
1123 report.tasks_mode = true;
1124
910 if (quiet) 1125 if (quiet)
911 perf_quiet_option(); 1126 perf_quiet_option();
912 1127
@@ -921,13 +1136,6 @@ int cmd_report(int argc, const char **argv)
921 return -EINVAL; 1136 return -EINVAL;
922 } 1137 }
923 1138
924 if (report.use_stdio)
925 use_browser = 0;
926 else if (report.use_tui)
927 use_browser = 1;
928 else if (report.use_gtk)
929 use_browser = 2;
930
931 if (report.inverted_callchain) 1139 if (report.inverted_callchain)
932 callchain_param.order = ORDER_CALLER; 1140 callchain_param.order = ORDER_CALLER;
933 if (symbol_conf.cumulate_callchain && !callchain_param.order_set) 1141 if (symbol_conf.cumulate_callchain && !callchain_param.order_set)
@@ -1014,6 +1222,13 @@ repeat:
1014 perf_hpp_list.need_collapse = true; 1222 perf_hpp_list.need_collapse = true;
1015 } 1223 }
1016 1224
1225 if (report.use_stdio)
1226 use_browser = 0;
1227 else if (report.use_tui)
1228 use_browser = 1;
1229 else if (report.use_gtk)
1230 use_browser = 2;
1231
1017 /* Force tty output for header output and per-thread stat. */ 1232 /* Force tty output for header output and per-thread stat. */
1018 if (report.header || report.header_only || report.show_threads) 1233 if (report.header || report.header_only || report.show_threads)
1019 use_browser = 0; 1234 use_browser = 0;
@@ -1021,6 +1236,12 @@ repeat:
1021 report.tool.show_feat_hdr = SHOW_FEAT_HEADER; 1236 report.tool.show_feat_hdr = SHOW_FEAT_HEADER;
1022 if (report.show_full_info) 1237 if (report.show_full_info)
1023 report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO; 1238 report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO;
1239 if (report.stats_mode || report.tasks_mode)
1240 use_browser = 0;
1241 if (report.stats_mode && report.tasks_mode) {
1242 pr_err("Error: --tasks and --mmaps can't be used together with --stats\n");
1243 goto error;
1244 }
1024 1245
1025 if (strcmp(input_name, "-") != 0) 1246 if (strcmp(input_name, "-") != 0)
1026 setup_browser(true); 1247 setup_browser(true);
@@ -1043,7 +1264,8 @@ repeat:
1043 ret = 0; 1264 ret = 0;
1044 goto error; 1265 goto error;
1045 } 1266 }
1046 } else if (use_browser == 0 && !quiet) { 1267 } else if (use_browser == 0 && !quiet &&
1268 !report.stats_mode && !report.tasks_mode) {
1047 fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", 1269 fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
1048 stdout); 1270 stdout);
1049 } 1271 }
@@ -1077,9 +1299,36 @@ repeat:
1077 if (symbol__init(&session->header.env) < 0) 1299 if (symbol__init(&session->header.env) < 0)
1078 goto error; 1300 goto error;
1079 1301
1080 if (perf_time__parse_str(&report.ptime, report.time_str) != 0) { 1302 report.ptime_range = perf_time__range_alloc(report.time_str,
1081 pr_err("Invalid time string\n"); 1303 &report.range_size);
1082 return -EINVAL; 1304 if (!report.ptime_range) {
1305 ret = -ENOMEM;
1306 goto error;
1307 }
1308
1309 if (perf_time__parse_str(report.ptime_range, report.time_str) != 0) {
1310 if (session->evlist->first_sample_time == 0 &&
1311 session->evlist->last_sample_time == 0) {
1312 pr_err("HINT: no first/last sample time found in perf data.\n"
1313 "Please use latest perf binary to execute 'perf record'\n"
1314 "(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
1315 ret = -EINVAL;
1316 goto error;
1317 }
1318
1319 report.range_num = perf_time__percent_parse_str(
1320 report.ptime_range, report.range_size,
1321 report.time_str,
1322 session->evlist->first_sample_time,
1323 session->evlist->last_sample_time);
1324
1325 if (report.range_num < 0) {
1326 pr_err("Invalid time string\n");
1327 ret = -EINVAL;
1328 goto error;
1329 }
1330 } else {
1331 report.range_num = 1;
1083 } 1332 }
1084 1333
1085 sort__setup_elide(stdout); 1334 sort__setup_elide(stdout);
@@ -1092,6 +1341,8 @@ repeat:
1092 ret = 0; 1341 ret = 0;
1093 1342
1094error: 1343error:
1344 zfree(&report.ptime_range);
1345
1095 perf_session__delete(session); 1346 perf_session__delete(session);
1096 return ret; 1347 return ret;
1097} 1348}