aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorJiri Olsa <jolsa@kernel.org>2018-01-07 11:03:56 -0500
committerArnaldo Carvalho de Melo <acme@redhat.com>2018-01-10 10:00:56 -0500
commit930f8b3479444d264aa33e008c4b00b86e8c62cc (patch)
tree5f3e29f022dbd04d636c619f642cdd3e41ae962f /tools/perf
parent2d1073def3cb69aa44f99be7ef42da7cc561be1f (diff)
perf report: Add --tasks option to display monitored tasks
Add --tasks option to display monitored tasks stored in perf.data. Displaying pid/tid/ppid plus the command string aligned to distinguish parent and child tasks. $ perf record -a ... $ perf report --tasks # pid tid ppid comm 0 0 -1 |swapper 2 2 0 | kthreadd 14080 14080 2 | kworker/u17:1 4 4 2 | kworker/0:0H 6 6 2 | mm_percpu_wq ... 1 1 0 | systemd 23242 23242 1 | firefox 23242 23298 23242 | Cache2 I/O 23242 23304 23242 | GMPThread ... 1195 1195 1 | login 1611 1611 1195 | bash 1639 1639 1611 | startx 1663 1663 1639 | xinit 1673 1673 1663 | xmonad-x86_64-l 23939 23939 1673 | xterm 23941 23941 23939 | bash 23963 23963 23941 | mutt 24954 24954 23963 | offlineimap Signed-off-by: Jiri Olsa <jolsa@kernel.org> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: David Ahern <dsahern@gmail.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: http://lkml.kernel.org/r/20180107160356.28203-13-jolsa@kernel.org [ Make it --tasks, plural, --task works as well, as its unambiguous ] [ Use machine__find_thread(), not findnew(), as pointed out by Namhyung ] Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/Documentation/perf-report.txt4
-rw-r--r--tools/perf/builtin-report.c136
2 files changed, 138 insertions, 2 deletions
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index a7d11ef2fe25..856c3c7e94fa 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -461,6 +461,10 @@ include::itrace.txt[]
461 Display overall events statistics without any further processing. 461 Display overall events statistics without any further processing.
462 (like the one at the end of the perf report -D command) 462 (like the one at the end of the perf report -D command)
463 463
464--tasks::
465 Display monitored tasks stored in perf data. Displaying pid/tid/ppid
466 plus the command string aligned to distinguish parent and child tasks.
467
464include::callchain-overhead-calculation.txt[] 468include::callchain-overhead-calculation.txt[]
465 469
466SEE ALSO 470SEE ALSO
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 8e67a8c25ab1..2c7bd85651dc 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"
@@ -63,6 +64,7 @@ struct report {
63 bool inverted_callchain; 64 bool inverted_callchain;
64 bool mem_mode; 65 bool mem_mode;
65 bool stats_mode; 66 bool stats_mode;
67 bool tasks_mode;
66 bool header; 68 bool header;
67 bool header_only; 69 bool header_only;
68 bool nonany_branch_mode; 70 bool nonany_branch_mode;
@@ -603,6 +605,124 @@ static int stats_print(struct report *rep)
603 return 0; 605 return 0;
604} 606}
605 607
608static void tasks_setup(struct report *rep)
609{
610 memset(&rep->tool, 0, sizeof(rep->tool));
611 rep->tool.comm = perf_event__process_comm;
612 rep->tool.exit = perf_event__process_exit;
613 rep->tool.fork = perf_event__process_fork;
614 rep->tool.no_warn = true;
615}
616
617struct task {
618 struct thread *thread;
619 struct list_head list;
620 struct list_head children;
621};
622
623static struct task *tasks_list(struct task *task, struct machine *machine)
624{
625 struct thread *parent_thread, *thread = task->thread;
626 struct task *parent_task;
627
628 /* Already listed. */
629 if (!list_empty(&task->list))
630 return NULL;
631
632 /* Last one in the chain. */
633 if (thread->ppid == -1)
634 return task;
635
636 parent_thread = machine__find_thread(machine, -1, thread->ppid);
637 if (!parent_thread)
638 return ERR_PTR(-ENOENT);
639
640 parent_task = thread__priv(parent_thread);
641 list_add_tail(&task->list, &parent_task->children);
642 return tasks_list(parent_task, machine);
643}
644
645static void task__print_level(struct task *task, FILE *fp, int level)
646{
647 struct thread *thread = task->thread;
648 struct task *child;
649
650 fprintf(fp, " %8d %8d %8d |%*s%s\n",
651 thread->pid_, thread->tid, thread->ppid,
652 level, "", thread__comm_str(thread));
653
654 if (!list_empty(&task->children)) {
655 list_for_each_entry(child, &task->children, list)
656 task__print_level(child, fp, level + 1);
657 }
658}
659
660static int tasks_print(struct report *rep, FILE *fp)
661{
662 struct perf_session *session = rep->session;
663 struct machine *machine = &session->machines.host;
664 struct task *tasks, *task;
665 unsigned int nr = 0, itask = 0, i;
666 struct rb_node *nd;
667 LIST_HEAD(list);
668
669 /*
670 * No locking needed while accessing machine->threads,
671 * because --tasks is single threaded command.
672 */
673
674 /* Count all the threads. */
675 for (i = 0; i < THREADS__TABLE_SIZE; i++)
676 nr += machine->threads[i].nr;
677
678 tasks = malloc(sizeof(*tasks) * nr);
679 if (!tasks)
680 return -ENOMEM;
681
682 for (i = 0; i < THREADS__TABLE_SIZE; i++) {
683 struct threads *threads = &machine->threads[i];
684
685 for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) {
686 task = tasks + itask++;
687
688 task->thread = rb_entry(nd, struct thread, rb_node);
689 INIT_LIST_HEAD(&task->children);
690 INIT_LIST_HEAD(&task->list);
691 thread__set_priv(task->thread, task);
692 }
693 }
694
695 /*
696 * Iterate every task down to the unprocessed parent
697 * and link all in task children list. Task with no
698 * parent is added into 'list'.
699 */
700 for (itask = 0; itask < nr; itask++) {
701 task = tasks + itask;
702
703 if (!list_empty(&task->list))
704 continue;
705
706 task = tasks_list(task, machine);
707 if (IS_ERR(task)) {
708 pr_err("Error: failed to process tasks\n");
709 free(tasks);
710 return PTR_ERR(task);
711 }
712
713 if (task)
714 list_add_tail(&task->list, &list);
715 }
716
717 fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
718
719 list_for_each_entry(task, &list, list)
720 task__print_level(task, fp, 0);
721
722 free(tasks);
723 return 0;
724}
725
606static int __cmd_report(struct report *rep) 726static int __cmd_report(struct report *rep)
607{ 727{
608 int ret; 728 int ret;
@@ -637,6 +757,9 @@ static int __cmd_report(struct report *rep)
637 if (rep->stats_mode) 757 if (rep->stats_mode)
638 stats_setup(rep); 758 stats_setup(rep);
639 759
760 if (rep->tasks_mode)
761 tasks_setup(rep);
762
640 ret = perf_session__process_events(session); 763 ret = perf_session__process_events(session);
641 if (ret) { 764 if (ret) {
642 ui__error("failed to process sample\n"); 765 ui__error("failed to process sample\n");
@@ -646,6 +769,9 @@ static int __cmd_report(struct report *rep)
646 if (rep->stats_mode) 769 if (rep->stats_mode)
647 return stats_print(rep); 770 return stats_print(rep);
648 771
772 if (rep->tasks_mode)
773 return tasks_print(rep, stdout);
774
649 report__warn_kptr_restrict(rep); 775 report__warn_kptr_restrict(rep);
650 776
651 evlist__for_each_entry(session->evlist, pos) 777 evlist__for_each_entry(session->evlist, pos)
@@ -803,6 +929,7 @@ int cmd_report(int argc, const char **argv)
803 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, 929 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
804 "dump raw trace in ASCII"), 930 "dump raw trace in ASCII"),
805 OPT_BOOLEAN(0, "stats", &report.stats_mode, "Display event stats"), 931 OPT_BOOLEAN(0, "stats", &report.stats_mode, "Display event stats"),
932 OPT_BOOLEAN(0, "tasks", &report.tasks_mode, "Display recorded tasks"),
806 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 933 OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
807 "file", "vmlinux pathname"), 934 "file", "vmlinux pathname"),
808 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, 935 OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
@@ -1064,8 +1191,12 @@ repeat:
1064 report.tool.show_feat_hdr = SHOW_FEAT_HEADER; 1191 report.tool.show_feat_hdr = SHOW_FEAT_HEADER;
1065 if (report.show_full_info) 1192 if (report.show_full_info)
1066 report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO; 1193 report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO;
1067 if (report.stats_mode) 1194 if (report.stats_mode || report.tasks_mode)
1068 use_browser = 0; 1195 use_browser = 0;
1196 if (report.stats_mode && report.tasks_mode) {
1197 pr_err("Error: --tasks and --stats options cannot be used together\n");
1198 goto error;
1199 }
1069 1200
1070 if (strcmp(input_name, "-") != 0) 1201 if (strcmp(input_name, "-") != 0)
1071 setup_browser(true); 1202 setup_browser(true);
@@ -1088,7 +1219,8 @@ repeat:
1088 ret = 0; 1219 ret = 0;
1089 goto error; 1220 goto error;
1090 } 1221 }
1091 } else if (use_browser == 0 && !quiet && !report.stats_mode) { 1222 } else if (use_browser == 0 && !quiet &&
1223 !report.stats_mode && !report.tasks_mode) {
1092 fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", 1224 fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n",
1093 stdout); 1225 stdout);
1094 } 1226 }