diff options
author | Jiri Olsa <jolsa@kernel.org> | 2018-01-07 11:03:56 -0500 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2018-01-10 10:00:56 -0500 |
commit | 930f8b3479444d264aa33e008c4b00b86e8c62cc (patch) | |
tree | 5f3e29f022dbd04d636c619f642cdd3e41ae962f /tools/perf | |
parent | 2d1073def3cb69aa44f99be7ef42da7cc561be1f (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.txt | 4 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 136 |
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 | |||
464 | include::callchain-overhead-calculation.txt[] | 468 | include::callchain-overhead-calculation.txt[] |
465 | 469 | ||
466 | SEE ALSO | 470 | SEE 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 | ||
608 | static 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 | |||
617 | struct task { | ||
618 | struct thread *thread; | ||
619 | struct list_head list; | ||
620 | struct list_head children; | ||
621 | }; | ||
622 | |||
623 | static 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 | |||
645 | static 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 | |||
660 | static 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 | |||
606 | static int __cmd_report(struct report *rep) | 726 | static 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 | } |