|
59 | 59 | #include <linux/ctype.h>
|
60 | 60 | #include <signal.h>
|
61 | 61 | #include <linux/bitmap.h>
|
| 62 | +#include <linux/list_sort.h> |
62 | 63 | #include <linux/string.h>
|
63 | 64 | #include <linux/stringify.h>
|
64 | 65 | #include <linux/time64.h>
|
@@ -828,35 +829,6 @@ static void tasks_setup(struct report *rep)
|
828 | 829 | rep->tool.no_warn = true;
|
829 | 830 | }
|
830 | 831 |
|
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 |
| - |
860 | 832 | struct maps__fprintf_task_args {
|
861 | 833 | int indent;
|
862 | 834 | FILE *fp;
|
@@ -900,89 +872,156 @@ static size_t maps__fprintf_task(struct maps *maps, int indent, FILE *fp)
|
900 | 872 | return args.printed;
|
901 | 873 | }
|
902 | 874 |
|
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) |
904 | 876 | {
|
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; |
910 | 879 |
|
911 |
| - fprintf(fp, "%s\n", thread__comm_str(thread)); |
| 880 | + if (thread__tid(thread) <= 0) |
| 881 | + return 0; |
912 | 882 |
|
913 |
| - maps__fprintf_task(thread__maps(thread), comm_indent, fp); |
| 883 | + if (thread__ppid(thread) <= 0) |
| 884 | + return 1; |
914 | 885 |
|
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; |
918 | 890 | }
|
| 891 | + res = 1 + thread_level(machine, parent_thread); |
| 892 | + thread__put(parent_thread); |
| 893 | + return res; |
919 | 894 | }
|
920 | 895 |
|
921 |
| -static int tasks_print(struct report *rep, FILE *fp) |
| 896 | +static void task__print_level(struct machine *machine, struct thread *thread, FILE *fp) |
922 | 897 | {
|
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, ""); |
929 | 902 |
|
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)); |
934 | 904 |
|
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 | +} |
938 | 907 |
|
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; |
942 | 930 |
|
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; |
945 | 934 |
|
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; |
949 | 937 |
|
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; |
956 | 941 |
|
957 | 942 | /*
|
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. |
961 | 946 | */
|
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; |
973 | 959 | }
|
| 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)); |
974 | 964 |
|
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; |
977 | 1000 | }
|
| 1001 | + thread__put(a); |
| 1002 | + thread__put(b); |
| 1003 | + return res; |
| 1004 | +} |
978 | 1005 |
|
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; |
980 | 1011 |
|
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; |
983 | 1015 |
|
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; |
986 | 1025 | }
|
987 | 1026 |
|
988 | 1027 | static int __cmd_report(struct report *rep)
|
|
0 commit comments