Skip to content

Commit 2f1e20f

Browse files
captain5050namhyung
authored andcommitted
perf report: Sort child tasks by tid
Commit 91e467b ("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 <[email protected]> Acked-by: Namhyung Kim <[email protected]> Cc: Yang Jihong <[email protected]> Cc: Oliver Upton <[email protected]> Signed-off-by: Namhyung Kim <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 498d348 commit 2f1e20f

File tree

3 files changed

+168
-89
lines changed

3 files changed

+168
-89
lines changed

tools/perf/builtin-report.c

Lines changed: 128 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include <linux/ctype.h>
6060
#include <signal.h>
6161
#include <linux/bitmap.h>
62+
#include <linux/list_sort.h>
6263
#include <linux/string.h>
6364
#include <linux/stringify.h>
6465
#include <linux/time64.h>
@@ -828,35 +829,6 @@ static void tasks_setup(struct report *rep)
828829
rep->tool.no_warn = true;
829830
}
830831

831-
struct task {
832-
struct thread *thread;
833-
struct list_head list;
834-
struct list_head children;
835-
};
836-
837-
static struct task *tasks_list(struct task *task, struct machine *machine)
838-
{
839-
struct thread *parent_thread, *thread = task->thread;
840-
struct task *parent_task;
841-
842-
/* Already listed. */
843-
if (!list_empty(&task->list))
844-
return NULL;
845-
846-
/* Last one in the chain. */
847-
if (thread__ppid(thread) == -1)
848-
return task;
849-
850-
parent_thread = machine__find_thread(machine, -1, thread__ppid(thread));
851-
if (!parent_thread)
852-
return ERR_PTR(-ENOENT);
853-
854-
parent_task = thread__priv(parent_thread);
855-
thread__put(parent_thread);
856-
list_add_tail(&task->list, &parent_task->children);
857-
return tasks_list(parent_task, machine);
858-
}
859-
860832
struct maps__fprintf_task_args {
861833
int indent;
862834
FILE *fp;
@@ -900,89 +872,156 @@ static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp)
900872
return args.printed;
901873
}
902874

903-
static void task__print_level(struct task *task, FILE *fp, int level)
875+
static int thread_level(struct machine *machine, const struct thread *thread)
904876
{
905-
struct thread *thread = task->thread;
906-
struct task *child;
907-
int comm_indent = fprintf(fp, " %8d %8d %8d |%*s",
908-
thread__pid(thread), thread__tid(thread),
909-
thread__ppid(thread), level, "");
877+
struct thread *parent_thread;
878+
int res;
910879

911-
fprintf(fp, "%s\n", thread__comm_str(thread));
880+
if (thread__tid(thread) <= 0)
881+
return 0;
912882

913-
maps__fprintf_task(thread__maps(thread), comm_indent, fp);
883+
if (thread__ppid(thread) <= 0)
884+
return 1;
914885

915-
if (!list_empty(&task->children)) {
916-
list_for_each_entry(child, &task->children, list)
917-
task__print_level(child, fp, level + 1);
886+
parent_thread = machine__find_thread(machine, -1, thread__ppid(thread));
887+
if (!parent_thread) {
888+
pr_err("Missing parent thread of %d\n", thread__tid(thread));
889+
return 0;
918890
}
891+
res = 1 + thread_level(machine, parent_thread);
892+
thread__put(parent_thread);
893+
return res;
919894
}
920895

921-
static int tasks_print(struct report *rep, FILE *fp)
896+
static void task__print_level(struct machine *machine, struct thread *thread, FILE *fp)
922897
{
923-
struct perf_session *session = rep->session;
924-
struct machine *machine = &session->machines.host;
925-
struct task *tasks, *task;
926-
unsigned int nr = 0, itask = 0, i;
927-
struct rb_node *nd;
928-
LIST_HEAD(list);
898+
int level = thread_level(machine, thread);
899+
int comm_indent = fprintf(fp, " %8d %8d %8d |%*s",
900+
thread__pid(thread), thread__tid(thread),
901+
thread__ppid(thread), level, "");
929902

930-
/*
931-
* No locking needed while accessing machine->threads,
932-
* because --tasks is single threaded command.
933-
*/
903+
fprintf(fp, "%s\n", thread__comm_str(thread));
934904

935-
/* Count all the threads. */
936-
for (i = 0; i < THREADS__TABLE_SIZE; i++)
937-
nr += machine->threads[i].nr;
905+
maps__fprintf_task(thread__maps(thread), comm_indent, fp);
906+
}
938907

939-
tasks = malloc(sizeof(*tasks) * nr);
940-
if (!tasks)
941-
return -ENOMEM;
908+
/*
909+
* Sort two thread list nodes such that they form a tree. The first node is the
910+
* root of the tree, its children are ordered numerically after it. If a child
911+
* has children itself then they appear immediately after their parent. For
912+
* example, the 4 threads in the order they'd appear in the list:
913+
* - init with a TID 1 and a parent of 0
914+
* - systemd with a TID 3000 and a parent of init/1
915+
* - systemd child thread with TID 4000, the parent is 3000
916+
* - NetworkManager is a child of init with a TID of 3500.
917+
*/
918+
static int task_list_cmp(void *priv, const struct list_head *la, const struct list_head *lb)
919+
{
920+
struct machine *machine = priv;
921+
struct thread_list *task_a = list_entry(la, struct thread_list, list);
922+
struct thread_list *task_b = list_entry(lb, struct thread_list, list);
923+
struct thread *a = task_a->thread;
924+
struct thread *b = task_b->thread;
925+
int level_a, level_b, res;
926+
927+
/* Same thread? */
928+
if (thread__tid(a) == thread__tid(b))
929+
return 0;
942930

943-
for (i = 0; i < THREADS__TABLE_SIZE; i++) {
944-
struct threads *threads = &machine->threads[i];
931+
/* Compare a and b to root. */
932+
if (thread__tid(a) == 0)
933+
return -1;
945934

946-
for (nd = rb_first_cached(&threads->entries); nd;
947-
nd = rb_next(nd)) {
948-
task = tasks + itask++;
935+
if (thread__tid(b) == 0)
936+
return 1;
949937

950-
task->thread = rb_entry(nd, struct thread_rb_node, rb_node)->thread;
951-
INIT_LIST_HEAD(&task->children);
952-
INIT_LIST_HEAD(&task->list);
953-
thread__set_priv(task->thread, task);
954-
}
955-
}
938+
/* If parents match sort by tid. */
939+
if (thread__ppid(a) == thread__ppid(b))
940+
return thread__tid(a) < thread__tid(b) ? -1 : 1;
956941

957942
/*
958-
* Iterate every task down to the unprocessed parent
959-
* and link all in task children list. Task with no
960-
* parent is added into 'list'.
943+
* Find a and b such that if they are a child of each other a and b's
944+
* tid's match, otherwise a and b have a common parent and distinct
945+
* tid's to sort by. First make the depths of the threads match.
961946
*/
962-
for (itask = 0; itask < nr; itask++) {
963-
task = tasks + itask;
964-
965-
if (!list_empty(&task->list))
966-
continue;
967-
968-
task = tasks_list(task, machine);
969-
if (IS_ERR(task)) {
970-
pr_err("Error: failed to process tasks\n");
971-
free(tasks);
972-
return PTR_ERR(task);
947+
level_a = thread_level(machine, a);
948+
level_b = thread_level(machine, b);
949+
a = thread__get(a);
950+
b = thread__get(b);
951+
for (int i = level_a; i > level_b; i--) {
952+
struct thread *parent = machine__find_thread(machine, -1, thread__ppid(a));
953+
954+
thread__put(a);
955+
if (!parent) {
956+
pr_err("Missing parent thread of %d\n", thread__tid(a));
957+
thread__put(b);
958+
return -1;
973959
}
960+
a = parent;
961+
}
962+
for (int i = level_b; i > level_a; i--) {
963+
struct thread *parent = machine__find_thread(machine, -1, thread__ppid(b));
974964

975-
if (task)
976-
list_add_tail(&task->list, &list);
965+
thread__put(b);
966+
if (!parent) {
967+
pr_err("Missing parent thread of %d\n", thread__tid(b));
968+
thread__put(a);
969+
return 1;
970+
}
971+
b = parent;
972+
}
973+
/* Search up to a common parent. */
974+
while (thread__ppid(a) != thread__ppid(b)) {
975+
struct thread *parent;
976+
977+
parent = machine__find_thread(machine, -1, thread__ppid(a));
978+
thread__put(a);
979+
if (!parent)
980+
pr_err("Missing parent thread of %d\n", thread__tid(a));
981+
a = parent;
982+
parent = machine__find_thread(machine, -1, thread__ppid(b));
983+
thread__put(b);
984+
if (!parent)
985+
pr_err("Missing parent thread of %d\n", thread__tid(b));
986+
b = parent;
987+
if (!a || !b) {
988+
/* Handle missing parent (unexpected) with some sanity. */
989+
thread__put(a);
990+
thread__put(b);
991+
return !a && !b ? 0 : (!a ? -1 : 1);
992+
}
993+
}
994+
if (thread__tid(a) == thread__tid(b)) {
995+
/* a is a child of b or vice-versa, deeper levels appear later. */
996+
res = level_a < level_b ? -1 : (level_a > level_b ? 1 : 0);
997+
} else {
998+
/* Sort by tid now the parent is the same. */
999+
res = thread__tid(a) < thread__tid(b) ? -1 : 1;
9771000
}
1001+
thread__put(a);
1002+
thread__put(b);
1003+
return res;
1004+
}
9781005

979-
fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
1006+
static int tasks_print(struct report *rep, FILE *fp)
1007+
{
1008+
struct machine *machine = &rep->session->machines.host;
1009+
LIST_HEAD(tasks);
1010+
int ret;
9801011

981-
list_for_each_entry(task, &list, list)
982-
task__print_level(task, fp, 0);
1012+
ret = machine__thread_list(machine, &tasks);
1013+
if (!ret) {
1014+
struct thread_list *task;
9831015

984-
free(tasks);
985-
return 0;
1016+
list_sort(machine, &tasks, task_list_cmp);
1017+
1018+
fprintf(fp, "# %8s %8s %8s %s\n", "pid", "tid", "ppid", "comm");
1019+
1020+
list_for_each_entry(task, &tasks, list)
1021+
task__print_level(machine, task->thread, fp);
1022+
}
1023+
thread_list__delete(&tasks);
1024+
return ret;
9861025
}
9871026

9881027
static int __cmd_report(struct report *rep)

tools/perf/util/machine.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3261,6 +3261,36 @@ int machines__for_each_thread(struct machines *machines,
32613261
return rc;
32623262
}
32633263

3264+
3265+
static int thread_list_cb(struct thread *thread, void *data)
3266+
{
3267+
struct list_head *list = data;
3268+
struct thread_list *entry = malloc(sizeof(*entry));
3269+
3270+
if (!entry)
3271+
return -ENOMEM;
3272+
3273+
entry->thread = thread__get(thread);
3274+
list_add_tail(&entry->list, list);
3275+
return 0;
3276+
}
3277+
3278+
int machine__thread_list(struct machine *machine, struct list_head *list)
3279+
{
3280+
return machine__for_each_thread(machine, thread_list_cb, list);
3281+
}
3282+
3283+
void thread_list__delete(struct list_head *list)
3284+
{
3285+
struct thread_list *pos, *next;
3286+
3287+
list_for_each_entry_safe(pos, next, list, list) {
3288+
thread__zput(pos->thread);
3289+
list_del(&pos->list);
3290+
free(pos);
3291+
}
3292+
}
3293+
32643294
pid_t machine__get_current_tid(struct machine *machine, int cpu)
32653295
{
32663296
if (cpu < 0 || (size_t)cpu >= machine->current_tid_sz)

tools/perf/util/machine.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,16 @@ int machines__for_each_thread(struct machines *machines,
280280
int (*fn)(struct thread *thread, void *p),
281281
void *priv);
282282

283+
struct thread_list {
284+
struct list_head list;
285+
struct thread *thread;
286+
};
287+
288+
/* Make a list of struct thread_list based on threads in the machine. */
289+
int machine__thread_list(struct machine *machine, struct list_head *list);
290+
/* Free up the nodes within the thread_list list. */
291+
void thread_list__delete(struct list_head *list);
292+
283293
pid_t machine__get_current_tid(struct machine *machine, int cpu);
284294
int machine__set_current_tid(struct machine *machine, int cpu, pid_t pid,
285295
pid_t tid);

0 commit comments

Comments
 (0)