From: Ian Rogers <irogers@google.com>
To: Peter Zijlstra <peterz@infradead.org>,
Ingo Molnar <mingo@redhat.com>,
Arnaldo Carvalho de Melo <acme@kernel.org>,
Mark Rutland <mark.rutland@arm.com>,
Alexander Shishkin <alexander.shishkin@linux.intel.com>,
Jiri Olsa <jolsa@kernel.org>, Namhyung Kim <namhyung@kernel.org>,
Ian Rogers <irogers@google.com>,
Adrian Hunter <adrian.hunter@intel.com>,
Nick Terrell <terrelln@fb.com>,
Kan Liang <kan.liang@linux.intel.com>,
Andi Kleen <ak@linux.intel.com>, Kajol Jain <kjain@linux.ibm.com>,
Athira Rajeev <atrajeev@linux.vnet.ibm.com>,
Huacai Chen <chenhuacai@kernel.org>,
Masami Hiramatsu <mhiramat@kernel.org>,
Vincent Whitchurch <vincent.whitchurch@axis.com>,
"Steinar H. Gunderson" <sesse@google.com>,
Liam Howlett <liam.howlett@oracle.com>,
Miguel Ojeda <ojeda@kernel.org>,
Colin Ian King <colin.i.king@gmail.com>,
Dmitrii Dolgov <9erthalion6@gmail.com>,
Yang Jihong <yangjihong1@huawei.com>,
Ming Wang <wangming01@loongson.cn>,
James Clark <james.clark@arm.com>,
K Prateek Nayak <kprateek.nayak@amd.com>,
Sean Christopherson <seanjc@google.com>,
Leo Yan <leo.yan@linaro.org>,
Ravi Bangoria <ravi.bangoria@amd.com>,
German Gomez <german.gomez@arm.com>,
Changbin Du <changbin.du@huawei.com>,
Paolo Bonzini <pbonzini@redhat.com>, Li Dong <lidong@vivo.com>,
Sandipan Das <sandipan.das@amd.com>,
liuwenyu <liuwenyu7@huawei.com>,
linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org,
Guilherme Amadio <amadio@gentoo.org>
Subject: [PATCH v6 30/47] perf report: Sort child tasks by tid
Date: Wed, 6 Dec 2023 17:17:04 -0800 [thread overview]
Message-ID: <20231207011722.1220634-31-irogers@google.com> (raw)
In-Reply-To: <20231207011722.1220634-1-irogers@google.com>
Commit 91e467bc568f ("perf machine: Use hashtable for machine
threads") made the iteration of thread tids unordered. The perf report
--tasks output now shows child threads in an order determined by the
hashing. For example, in this snippet tid 3 appears after tid 256 even
though they have the same ppid 2:
```
$ perf report --tasks
% pid tid ppid comm
0 0 -1 |swapper
2 2 0 | kthreadd
256 256 2 | kworker/12:1H-k
693761 693761 2 | kworker/10:1-mm
1301762 1301762 2 | kworker/1:1-mm_
1302530 1302530 2 | kworker/u32:0-k
3 3 2 | rcu_gp
...
```
The output is easier to read if threads appear numerically
increasing. To allow for this, read all threads into a list then sort
with a comparator that orders by the child task's of the first common
parent. The list creation and deletion are created as utilities on
machine. The indentation is possible by counting the number of
parents a child has.
With this change the output for the same data file is now like:
```
$ perf report --tasks
% pid tid ppid comm
0 0 -1 |swapper
1 1 0 | systemd
823 823 1 | systemd-journal
853 853 1 | systemd-udevd
3230 3230 1 | systemd-timesyn
3236 3236 1 | auditd
3239 3239 3236 | audisp-syslog
3321 3321 1 | accounts-daemon
...
```
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-report.c | 203 ++++++++++++++++++++----------------
tools/perf/util/machine.c | 30 ++++++
tools/perf/util/machine.h | 10 ++
3 files changed, 155 insertions(+), 88 deletions(-)
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index a5d7bc5b843f..f5b95d45f6da 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -59,6 +59,7 @@
#include <linux/ctype.h>
#include <signal.h>
#include <linux/bitmap.h>
+#include <linux/list_sort.h>
#include <linux/string.h>
#include <linux/stringify.h>
#include <linux/time64.h>
@@ -830,35 +831,6 @@ static void tasks_setup(struct report *rep)
rep->tool.no_warn = true;
}
-struct task {
- struct thread *thread;
- struct list_head list;
- struct list_head children;
-};
-
-static struct task *tasks_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(thread) == -1)
- return task;
-
- parent_thread = machine__find_thread(machine, -1, thread__ppid(thread));
- if (!parent_thread)
- return ERR_PTR(-ENOENT);
-
- parent_task = thread__priv(parent_thread);
- thread__put(parent_thread);
- list_add_tail(&task->list, &parent_task->children);
- return tasks_list(parent_task, machine);
-}
-
struct maps__fprintf_task_args {
int indent;
FILE *fp;
@@ -902,89 +874,144 @@ static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp)
return args.printed;
}
-static void task__print_level(struct task *task, FILE *fp, int level)
+static int thread_level(struct machine *machine, const struct thread *thread)
{
- struct thread *thread = task->thread;
- struct task *child;
- int comm_indent = fprintf(fp, " %8d %8d %8d |%*s",
- thread__pid(thread), thread__tid(thread),
- thread__ppid(thread), level, "");
+ struct thread *parent_thread;
+ int res;
- fprintf(fp, "%s\n", thread__comm_str(thread));
+ if (thread__tid(thread) <= 0)
+ return 0;
- maps__fprintf_task(thread__maps(thread), comm_indent, fp);
+ if (thread__ppid(thread) <= 0)
+ return 1;
- if (!list_empty(&task->children)) {
- list_for_each_entry(child, &task->children, list)
- task__print_level(child, fp, level + 1);
+ parent_thread = machine__find_thread(machine, -1, thread__ppid(thread));
+ if (!parent_thread) {
+ pr_err("Missing parent thread of %d\n", thread__tid(thread));
+ return 0;
}
+ res = 1 + thread_level(machine, parent_thread);
+ thread__put(parent_thread);
+ return res;
}
-static int tasks_print(struct report *rep, FILE *fp)
+static void task__print_level(struct machine *machine, struct thread *thread, 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);
+ int level = thread_level(machine, thread);
+ int comm_indent = fprintf(fp, " %8d %8d %8d |%*s",
+ thread__pid(thread), thread__tid(thread),
+ thread__ppid(thread), level, "");
- /*
- * No locking needed while accessing machine->threads,
- * because --tasks is single threaded command.
- */
+ fprintf(fp, "%s\n", thread__comm_str(thread));
- /* Count all the threads. */
- for (i = 0; i < THREADS__TABLE_SIZE; i++)
- nr += machine->threads[i].nr;
+ maps__fprintf_task(thread__maps(thread), comm_indent, fp);
+}
- tasks = malloc(sizeof(*tasks) * nr);
- if (!tasks)
- return -ENOMEM;
+static int task_list_cmp(void *priv, const struct list_head *la, const struct list_head *lb)
+{
+ struct machine *machine = priv;
+ struct thread_list *task_a = list_entry(la, struct thread_list, list);
+ struct thread_list *task_b = list_entry(lb, struct thread_list, list);
+ struct thread *a = task_a->thread;
+ struct thread *b = task_b->thread;
+ int level_a, level_b, res;
+
+ /* Compare a and b to root. */
+ if (thread__tid(a) == thread__tid(b))
+ return 0;
- for (i = 0; i < THREADS__TABLE_SIZE; i++) {
- struct threads *threads = &machine->threads[i];
+ if (thread__tid(a) == 0)
+ return -1;
- for (nd = rb_first_cached(&threads->entries); nd;
- nd = rb_next(nd)) {
- task = tasks + itask++;
+ if (thread__tid(b) == 0)
+ return 1;
- task->thread = rb_entry(nd, struct thread_rb_node, rb_node)->thread;
- INIT_LIST_HEAD(&task->children);
- INIT_LIST_HEAD(&task->list);
- thread__set_priv(task->thread, task);
- }
+ /* If parents match sort by tid. */
+ if (thread__ppid(a) == thread__ppid(b)) {
+ return thread__tid(a) < thread__tid(b)
+ ? -1
+ : (thread__tid(a) > thread__tid(b) ? 1 : 0);
}
/*
- * Iterate every task down to the unprocessed parent
- * and link all in task children list. Task with no
- * parent is added into 'list'.
+ * Find a and b such that if they are a child of each other a and b's
+ * tid's match, otherwise a and b have a common parent and distinct
+ * tid's to sort by. First make the depths of the threads match.
*/
- for (itask = 0; itask < nr; itask++) {
- task = tasks + itask;
-
- if (!list_empty(&task->list))
- continue;
-
- task = tasks_list(task, machine);
- if (IS_ERR(task)) {
- pr_err("Error: failed to process tasks\n");
- free(tasks);
- return PTR_ERR(task);
+ level_a = thread_level(machine, a);
+ level_b = thread_level(machine, b);
+ a = thread__get(a);
+ b = thread__get(b);
+ for (int i = level_a; i > level_b; i--) {
+ struct thread *parent = machine__find_thread(machine, -1, thread__ppid(a));
+
+ thread__put(a);
+ if (!parent) {
+ pr_err("Missing parent thread of %d\n", thread__tid(a));
+ thread__put(b);
+ return -1;
}
+ a = parent;
+ }
+ for (int i = level_b; i > level_a; i--) {
+ struct thread *parent = machine__find_thread(machine, -1, thread__ppid(b));
- if (task)
- list_add_tail(&task->list, &list);
+ thread__put(b);
+ if (!parent) {
+ pr_err("Missing parent thread of %d\n", thread__tid(b));
+ thread__put(a);
+ return 1;
+ }
+ b = parent;
+ }
+ /* Search up to a common parent. */
+ while (thread__ppid(a) != thread__ppid(b)) {
+ struct thread *parent;
+
+ parent = machine__find_thread(machine, -1, thread__ppid(a));
+ thread__put(a);
+ if (!parent)
+ pr_err("Missing parent thread of %d\n", thread__tid(a));
+ a = parent;
+ parent = machine__find_thread(machine, -1, thread__ppid(b));
+ thread__put(b);
+ if (!parent)
+ pr_err("Missing parent thread of %d\n", thread__tid(b));
+ b = parent;
+ if (!a || !b)
+ return !a && !b ? 0 : (!a ? -1 : 1);
+ }
+ if (thread__tid(a) == thread__tid(b)) {
+ /* a is a child of b or vice-versa, deeper levels appear later. */
+ res = level_a < level_b ? -1 : (level_a > level_b ? 1 : 0);
+ } else {
+ /* Sort by tid now the parent is the same. */
+ res = thread__tid(a) < thread__tid(b) ? -1 : 1;
}
+ thread__put(a);
+ thread__put(b);
+ return res;
+}
+
+static int tasks_print(struct report *rep, FILE *fp)
+{
+ struct machine *machine = &rep->session->machines.host;
+ LIST_HEAD(tasks);
+ int ret;
- fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
+ ret = machine__thread_list(machine, &tasks);
+ if (!ret) {
+ struct thread_list *task;
- list_for_each_entry(task, &list, list)
- task__print_level(task, fp, 0);
+ list_sort(machine, &tasks, task_list_cmp);
- free(tasks);
- return 0;
+ fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
+
+ list_for_each_entry(task, &tasks, list)
+ task__print_level(machine, task->thread, fp);
+ }
+ thread_list__delete(&tasks);
+ return ret;
}
static int __cmd_report(struct report *rep)
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 3da92f18814a..7872ce92c9fc 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -3261,6 +3261,36 @@ int machines__for_each_thread(struct machines *machines,
return rc;
}
+
+static int thread_list_cb(struct thread *thread, void *data)
+{
+ struct list_head *list = data;
+ struct thread_list *entry = malloc(sizeof(*entry));
+
+ if (!entry)
+ return -ENOMEM;
+
+ entry->thread = thread__get(thread);
+ list_add_tail(&entry->list, list);
+ return 0;
+}
+
+int machine__thread_list(struct machine *machine, struct list_head *list)
+{
+ return machine__for_each_thread(machine, thread_list_cb, list);
+}
+
+void thread_list__delete(struct list_head *list)
+{
+ struct thread_list *pos, *next;
+
+ list_for_each_entry_safe(pos, next, list, list) {
+ thread__zput(pos->thread);
+ list_del(&pos->list);
+ free(pos);
+ }
+}
+
pid_t machine__get_current_tid(struct machine *machine, int cpu)
{
if (cpu < 0 || (size_t)cpu >= machine->current_tid_sz)
diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h
index 1279acda6a8a..b738ce84817b 100644
--- a/tools/perf/util/machine.h
+++ b/tools/perf/util/machine.h
@@ -280,6 +280,16 @@ int machines__for_each_thread(struct machines *machines,
int (*fn)(struct thread *thread, void *p),
void *priv);
+struct thread_list {
+ struct list_head list;
+ struct thread *thread;
+};
+
+/* Make a list of struct thread_list based on threads in the machine. */
+int machine__thread_list(struct machine *machine, struct list_head *list);
+/* Free up the nodes within the thread_list list. */
+void thread_list__delete(struct list_head *list);
+
pid_t machine__get_current_tid(struct machine *machine, int cpu);
int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
pid_t tid);
--
2.43.0.rc2.451.g8631bc7472-goog
next prev parent reply other threads:[~2023-12-07 1:19 UTC|newest]
Thread overview: 56+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-12-07 1:16 [PATCH v6 00/47] maps/threads/dsos memory improvements and fixes Ian Rogers
2023-12-07 1:16 ` [PATCH v6 01/47] perf map: Improve map/unmap parameter names Ian Rogers
2023-12-11 23:38 ` Namhyung Kim
2023-12-11 23:39 ` Namhyung Kim
2023-12-11 23:52 ` Ian Rogers
2023-12-19 6:06 ` Namhyung Kim
2023-12-07 1:16 ` [PATCH v6 02/47] perf maps: Add maps__for_each_map to iterate maps holding the lock Ian Rogers
2023-12-07 1:16 ` [PATCH v6 03/47] perf events x86: Use function to add missing lock Ian Rogers
2023-12-07 1:16 ` [PATCH v6 04/47] perf report: Use function to add missing maps lock Ian Rogers
2023-12-07 1:16 ` [PATCH v6 05/47] perf tests: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 06/47] perf machine: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 07/47] perf probe-event: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 08/47] perf symbol: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 09/47] perf synthetic-events: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 10/47] perf thread: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 11/47] perf unwind: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 12/47] perf vdso: " Ian Rogers
2023-12-07 1:16 ` [PATCH v6 13/47] perf maps: Reduce scope of maps__for_each_entry Ian Rogers
2023-12-07 1:16 ` [PATCH v6 14/47] perf maps: Add remove maps function to remove a map based on callback Ian Rogers
2023-12-07 1:16 ` [PATCH v6 15/47] perf debug: Expose debug file Ian Rogers
2023-12-07 1:16 ` [PATCH v6 16/47] perf maps: Refactor maps__fixup_overlappings Ian Rogers
2023-12-07 1:16 ` [PATCH v6 17/47] perf maps: Do simple merge if given map doesn't overlap Ian Rogers
2023-12-07 1:16 ` [PATCH v6 18/47] perf maps: Rename clone to copy from Ian Rogers
2023-12-07 1:16 ` [PATCH v6 19/47] perf maps: Add maps__load_first Ian Rogers
2023-12-07 1:16 ` [PATCH v6 20/47] perf maps: Add find next entry to give entry after the given map Ian Rogers
2023-12-07 1:16 ` [PATCH v6 21/47] perf maps: Reduce scope of map_rb_node and maps internals Ian Rogers
2023-12-07 1:16 ` [PATCH v6 22/47] perf maps: Fix up overlaps during fixup_end Ian Rogers
2023-12-07 1:16 ` [PATCH v6 23/47] perf maps: Switch from rbtree to lazily sorted array for addresses Ian Rogers
2023-12-07 1:16 ` [PATCH v6 24/47] perf maps: Get map before returning in maps__find Ian Rogers
2023-12-07 1:16 ` [PATCH v6 25/47] perf maps: Get map before returning in maps__find_by_name Ian Rogers
2023-12-07 1:17 ` [PATCH v6 26/47] perf maps: Get map before returning in maps__find_next_entry Ian Rogers
2023-12-07 1:17 ` [PATCH v6 27/47] perf maps: Hide maps internals Ian Rogers
2023-12-07 1:17 ` [PATCH v6 28/47] perf maps: Locking tidy up of nr_maps Ian Rogers
2023-12-07 1:17 ` [PATCH v6 29/47] perf dso: Reorder variables to save space in struct dso Ian Rogers
2023-12-07 1:17 ` Ian Rogers [this message]
2023-12-07 1:17 ` [PATCH v6 31/47] perf trace: Ignore thread hashing in summary Ian Rogers
2023-12-07 1:17 ` [PATCH v6 32/47] perf machine: Move fprintf to for_each loop and a callback Ian Rogers
2023-12-07 1:17 ` [PATCH v6 33/47] perf threads: Move threads to its own files Ian Rogers
2023-12-07 1:17 ` [PATCH v6 34/47] perf threads: Switch from rbtree to hashmap Ian Rogers
2023-12-07 1:17 ` [PATCH v6 35/47] perf threads: Reduce table size from 256 to 8 Ian Rogers
2023-12-07 1:17 ` [PATCH v6 36/47] perf dsos: Attempt to better abstract dsos internals Ian Rogers
2023-12-07 1:17 ` [PATCH v6 37/47] perf dsos: Tidy reference counting and locking Ian Rogers
2023-12-07 1:17 ` [PATCH v6 38/47] perf dsos: Add dsos__for_each_dso Ian Rogers
2023-12-07 1:17 ` [PATCH v6 39/47] perf dso: Move dso functions out of dsos Ian Rogers
2023-12-07 1:17 ` [PATCH v6 40/47] perf dsos: Switch more loops to dsos__for_each_dso Ian Rogers
2023-12-07 1:17 ` [PATCH v6 41/47] perf dsos: Switch backing storage to array from rbtree/list Ian Rogers
2023-12-07 1:17 ` [PATCH v6 42/47] perf dsos: Remove __dsos__addnew Ian Rogers
2023-12-07 1:17 ` [PATCH v6 43/47] perf dsos: Remove __dsos__findnew_link_by_longname_id Ian Rogers
2023-12-07 1:17 ` [PATCH v6 44/47] perf dsos: Switch hand code to bsearch Ian Rogers
2023-12-07 1:17 ` [PATCH v6 45/47] perf dso: Add reference count checking and accessor functions Ian Rogers
2023-12-07 1:17 ` [PATCH v6 46/47] perf dso: Reference counting related fixes Ian Rogers
2023-12-07 1:17 ` [PATCH v6 47/47] perf dso: Use container_of to avoid a pointer in dso_data Ian Rogers
2023-12-18 20:53 ` [PATCH v6 00/47] maps/threads/dsos memory improvements and fixes Arnaldo Carvalho de Melo
2023-12-19 1:27 ` Arnaldo Carvalho de Melo
2023-12-20 17:46 ` Arnaldo Carvalho de Melo
2023-12-20 18:07 ` Arnaldo Carvalho de Melo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20231207011722.1220634-31-irogers@google.com \
--to=irogers@google.com \
--cc=9erthalion6@gmail.com \
--cc=acme@kernel.org \
--cc=adrian.hunter@intel.com \
--cc=ak@linux.intel.com \
--cc=alexander.shishkin@linux.intel.com \
--cc=amadio@gentoo.org \
--cc=atrajeev@linux.vnet.ibm.com \
--cc=changbin.du@huawei.com \
--cc=chenhuacai@kernel.org \
--cc=colin.i.king@gmail.com \
--cc=german.gomez@arm.com \
--cc=james.clark@arm.com \
--cc=jolsa@kernel.org \
--cc=kan.liang@linux.intel.com \
--cc=kjain@linux.ibm.com \
--cc=kprateek.nayak@amd.com \
--cc=leo.yan@linaro.org \
--cc=liam.howlett@oracle.com \
--cc=lidong@vivo.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-perf-users@vger.kernel.org \
--cc=liuwenyu7@huawei.com \
--cc=mark.rutland@arm.com \
--cc=mhiramat@kernel.org \
--cc=mingo@redhat.com \
--cc=namhyung@kernel.org \
--cc=ojeda@kernel.org \
--cc=pbonzini@redhat.com \
--cc=peterz@infradead.org \
--cc=ravi.bangoria@amd.com \
--cc=sandipan.das@amd.com \
--cc=seanjc@google.com \
--cc=sesse@google.com \
--cc=terrelln@fb.com \
--cc=vincent.whitchurch@axis.com \
--cc=wangming01@loongson.cn \
--cc=yangjihong1@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®