From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754261AbeAGQEh (ORCPT + 1 other); Sun, 7 Jan 2018 11:04:37 -0500 Received: from mx1.redhat.com ([209.132.183.28]:59274 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754243AbeAGQEd (ORCPT ); Sun, 7 Jan 2018 11:04:33 -0500 From: Jiri Olsa To: Arnaldo Carvalho de Melo , Peter Zijlstra Cc: lkml , Ingo Molnar , Namhyung Kim , David Ahern , Andi Kleen , Alexander Shishkin Subject: [PATCH 12/12] perf report: Add --task option to display monitored tasks Date: Sun, 7 Jan 2018 17:03:56 +0100 Message-Id: <20180107160356.28203-13-jolsa@kernel.org> In-Reply-To: <20180107160356.28203-1-jolsa@kernel.org> References: <20180107160356.28203-1-jolsa@kernel.org> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.32]); Sun, 07 Jan 2018 16:04:33 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Return-Path: Adding --task 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 --task # 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 Link: http://lkml.kernel.org/n/tip-ehvadnmg8b3kdhvgsbuesbr7@git.kernel.org Signed-off-by: Jiri Olsa --- tools/perf/Documentation/perf-report.txt | 4 + 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 b0d70bf4ddfe..dcf0d154918f 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -441,6 +441,10 @@ include::itrace.txt[] Display overall events statistics without any further processing. (like the one at the end of the perf report -D command) +--task:: + Display monitored tasks stored in perf data. Displaying pid/tid/ppid + plus the command string aligned to distinguish parent and child tasks. + include::callchain-overhead-calculation.txt[] SEE ALSO diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 11d303494b0c..f2ca2cbe63c1 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -15,6 +15,7 @@ #include "util/color.h" #include #include +#include #include "util/symbol.h" #include "util/callchain.h" #include "util/values.h" @@ -61,6 +62,7 @@ struct report { bool inverted_callchain; bool mem_mode; bool stat_mode; + bool task_mode; bool header; bool header_only; bool nonany_branch_mode; @@ -598,6 +600,124 @@ static int stat_print(struct report *rep) return 0; } +static void task_setup(struct report *rep) +{ + memset(&rep->tool, 0, sizeof(rep->tool)); + rep->tool.comm = perf_event__process_comm; + rep->tool.exit = perf_event__process_exit; + rep->tool.fork = perf_event__process_fork; + rep->tool.no_warn = true; +} + +struct task { + struct thread *thread; + struct list_head list; + struct list_head children; +}; + +static struct task *task_list(struct task *task, struct machine *machine) +{ + struct thread *parent_thread, *thread = task->thread; + struct task *parent_task; + + /* Already listed. */ + if (!list_empty(&task->list)) + return NULL; + + /* Last one in the chain. */ + if (thread->ppid == -1) + return task; + + parent_thread = machine__findnew_thread(machine, -1, thread->ppid); + if (!parent_thread) + return ERR_PTR(-ENOMEM); + + parent_task = thread__priv(parent_thread); + list_add_tail(&task->list, &parent_task->children); + return task_list(parent_task, machine); +} + +static void task__print_level(struct task *task, FILE *fp, int level) +{ + struct thread *thread = task->thread; + struct task *child; + + fprintf(fp, " %8d %8d %8d |%*s%s\n", + thread->pid_, thread->tid, thread->ppid, + level, "", thread__comm_str(thread)); + + if (!list_empty(&task->children)) { + list_for_each_entry(child, &task->children, list) + task__print_level(child, fp, level + 1); + } +} + +static int task_print(struct report *rep, FILE *fp) +{ + struct perf_session *session = rep->session; + struct machine *machine = &session->machines.host; + struct task *tasks, *task; + unsigned int nr = 0, itask = 0, i; + struct rb_node *nd; + LIST_HEAD(list); + + /* + * No locking needed while accessing machine->threads, + * because --task is single threaded command. + */ + + /* Count all the threads. */ + for (i = 0; i < THREADS__TABLE_SIZE; i++) + nr += machine->threads[i].nr; + + tasks = malloc(sizeof(*tasks) * nr); + if (!tasks) + return -ENOMEM; + + for (i = 0; i < THREADS__TABLE_SIZE; i++) { + struct threads *threads = &machine->threads[i]; + + for (nd = rb_first(&threads->entries); nd; nd = rb_next(nd)) { + task = tasks + itask++; + + task->thread = rb_entry(nd, struct thread, rb_node); + INIT_LIST_HEAD(&task->children); + INIT_LIST_HEAD(&task->list); + thread__set_priv(task->thread, task); + } + } + + /* + * Iterate every task down to the unprocessed parent + * and link all in task children list. Task with no + * parent is added into 'list'. + */ + for (itask = 0; itask < nr; itask++) { + task = tasks + itask; + + if (!list_empty(&task->list)) + continue; + + task = task_list(task, machine); + if (IS_ERR(task)) { + pr_err("Error: failed to process tasks\n"); + free(tasks); + return -ENOMEM; + } + + if (task) + list_add_tail(&task->list, &list); + } + + fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm"); + + list_for_each_entry(task, &list, list) + task__print_level(task, fp, 0); + + free(tasks); + return 0; +} + static int __cmd_report(struct report *rep) { int ret; @@ -632,6 +752,9 @@ static int __cmd_report(struct report *rep) if (rep->stat_mode) stat_setup(rep); + if (rep->task_mode) + task_setup(rep); + ret = perf_session__process_events(session); if (ret) { ui__error("failed to process sample\n"); @@ -641,6 +764,9 @@ static int __cmd_report(struct report *rep) if (rep->stat_mode) return stat_print(rep); + if (rep->task_mode) + return task_print(rep, stdout); + report__warn_kptr_restrict(rep); evlist__for_each_entry(session->evlist, pos) @@ -798,6 +924,7 @@ int cmd_report(int argc, const char **argv) OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, "dump raw trace in ASCII"), OPT_BOOLEAN(0, "stat", &report.stat_mode, "Display event stats"), + OPT_BOOLEAN(0, "task", &report.task_mode, "Display recorded tasks"), OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, "file", "vmlinux pathname"), OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, @@ -1059,8 +1186,12 @@ int cmd_report(int argc, const char **argv) report.tool.show_feat_hdr = SHOW_FEAT_HEADER; if (report.show_full_info) report.tool.show_feat_hdr = SHOW_FEAT_HEADER_FULL_INFO; - if (report.stat_mode) + if (report.stat_mode || report.task_mode) use_browser = 0; + if (report.stat_mode && report.task_mode) { + pr_err("Error: --task and --stat options cannot be used together\n"); + goto error; + } if (strcmp(input_name, "-") != 0) setup_browser(true); @@ -1083,7 +1214,8 @@ int cmd_report(int argc, const char **argv) ret = 0; goto error; } - } else if (use_browser == 0 && !quiet && !report.stat_mode) { + } else if (use_browser == 0 && !quiet && + !report.stat_mode && !report.task_mode) { fputs("# To display the perf.data header info, please use --header/--header-only options.\n#\n", stdout); } -- 2.13.6