Received: by 2002:a05:7412:b995:b0:f9:9502:5bb8 with SMTP id it21csp6935534rdb; Tue, 2 Jan 2024 21:09:30 -0800 (PST) X-Google-Smtp-Source: AGHT+IFaTojqqLg5daicwf9jAdGRyde8fyufQLTuID4VW1viTaOKX+/pdjijBEto4J/5iwhfka9X X-Received: by 2002:a05:6512:1255:b0:50e:76b8:e754 with SMTP id fb21-20020a056512125500b0050e76b8e754mr3663676lfb.141.1704258570706; Tue, 02 Jan 2024 21:09:30 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1704258570; cv=none; d=google.com; s=arc-20160816; b=q7Nnm9xacyFKY44BdX+BWINjZG0PF6+wYQ/MAE4k9kaS3jdwiYMQphHL9CRAIOcCk5 xp/oejuRVG1Bnwz+4muRZP7Ia/CYFtklwYPGUJtyH1cgqHHRO2vFQYRZ5FuKvV4cTKze I+Cz8slNL3tYy40MoTMzmBJOPBd6y2OgzbUm6NGtw4s4EFEUUSrUZugyp4jvZagRXpkq xQ43scND3cXzkxPud9Uk07rG74fEg3UeNKSYGWuff5rxVdPvDmW8By2FVfiltc78sYgG 77YdlZFa9bOcr0PkqlA9s4xDmABTHfM6tX0T3C8ifEpq40tY0uf4/PkMji5p128VMM7H WsPA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=to:from:subject:references:mime-version:list-unsubscribe :list-subscribe:list-id:precedence:message-id:in-reply-to:date :dkim-signature; bh=bmovtK15ZM7dzfTkpd2uZSIVYTFPvsabIQ6qIB9DpAE=; fh=Io7x3OacpefNRRNKoYAzVE0aWTDwD7FRzyCdD1e3AN4=; b=jYqyKfxUW4WdaGrqIOyUeY8LgvZbbPCnXxyN5I6IKVTKswW4u3vrcNB+vI0igv+ObV DPKa2uQV7c+mR4WCM+0qdSSb+g9xFSmkBprU/6WqB9/9vxb7x86tusN8HeZMnuoce+fj XJ4eqYZJVXB+7qfobAVEYTDM6KYw+dTknfKukZxGhBqJKRWaKpdanaV1vj6MlzXmAP+c kOH3gujDGfWYr4XeLShH/MsmRCi4c0OveBep+TEnwfvySIxHKisBLGnNuZfk3XYALgDY 5Q7y0Od+j+dwZBnTsSo3XmA8NNlIt8QssqCLvlkx+JMH5VRI+gxqr1H3tmS9dEWmVR9w hAaQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=I0y1JmVU; spf=pass (google.com: domain of linux-kernel+bounces-15167-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) smtp.mailfrom="linux-kernel+bounces-15167-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from am.mirrors.kernel.org (am.mirrors.kernel.org. [2604:1380:4601:e00::3]) by mx.google.com with ESMTPS id r18-20020a056402235200b005540fd5f1a8si11372653eda.628.2024.01.02.21.09.30 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 02 Jan 2024 21:09:30 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel+bounces-15167-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) client-ip=2604:1380:4601:e00::3; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=I0y1JmVU; spf=pass (google.com: domain of linux-kernel+bounces-15167-linux.lists.archive=gmail.com@vger.kernel.org designates 2604:1380:4601:e00::3 as permitted sender) smtp.mailfrom="linux-kernel+bounces-15167-linux.lists.archive=gmail.com@vger.kernel.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from smtp.subspace.kernel.org (wormhole.subspace.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by am.mirrors.kernel.org (Postfix) with ESMTPS id 486431F23A88 for ; Wed, 3 Jan 2024 05:09:30 +0000 (UTC) Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by smtp.subspace.kernel.org (Postfix) with ESMTP id C41FC18C28; Wed, 3 Jan 2024 05:07:09 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="I0y1JmVU" X-Original-To: linux-kernel@vger.kernel.org Received: from mail-yw1-f202.google.com (mail-yw1-f202.google.com [209.85.128.202]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0F2D518AE1 for ; Wed, 3 Jan 2024 05:07:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--irogers.bounces.google.com Received: by mail-yw1-f202.google.com with SMTP id 00721157ae682-5e9a02c6d49so150489867b3.2 for ; Tue, 02 Jan 2024 21:07:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1704258425; x=1704863225; darn=vger.kernel.org; h=to:from:subject:references:mime-version:message-id:in-reply-to:date :from:to:cc:subject:date:message-id:reply-to; bh=bmovtK15ZM7dzfTkpd2uZSIVYTFPvsabIQ6qIB9DpAE=; b=I0y1JmVURYW7xoJBJBQfC8rY1Be49qctSTIkvq2ub/Sct9OUxGXhxwJfwQP1I0kuae MY2HjamJmuGlrr0WsFd+z+NGjIq/jNqNIcEDkydOPZXbXcIN629R/qfEG3jz+KifDh5J IUKEVtWC5nBZVS8tDgbJryTzSTxNz8K/6NURMLjoNYwJ3Tptb64+klV8Sm8WUHUUw9K2 YwHViekdlEqLMMkfIh+wYYKJXXHvDeEeSgTNOgsZxRP57aRSRFqPC7PYXGX9qKt8L2Dc bkjLu4feHnaaWsrnzKnTJE+mt0KX1kC3jnKiNWAMQMl60IkD7WUrNB1BK2UqDwx1qOw/ vKAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1704258425; x=1704863225; h=to:from:subject:references:mime-version:message-id:in-reply-to:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=bmovtK15ZM7dzfTkpd2uZSIVYTFPvsabIQ6qIB9DpAE=; b=MhzFpA5MggqXb+aDwZu6dCusvK06Z8lzTHzXLObRQH791/oVpZCLGWE2YShcWKttXa eCetWKXHcZzvdANHG0X8MZkXICxeLgaeMBIkC/HNotAkbojX3mrQuy7BoRg/E8O5Ondl WHaKfvQaWMDbBpyGHVDCws/09dfQqLSOOBRikx84nhxBbRtGat2w1yFA5t913FNpUxw5 otWUOuCNBjS08ZusLsjoplnst1I+4p2gi8ZiMAtorYrWstDfSBXsP85BY1Yqo7/debPu FyT1UIfNuFqViJzonW5dB94vw/lE4TZ1I1hC8Mb5SUCBX1H6LsLP97EuVpLadY6yAiWX YR5w== X-Gm-Message-State: AOJu0YwhYNFx2NmnHo8ChlRodnyjTCdOzpeFlJmvCQMF9ek6AxSpA2d4 lxsuJprTnpjZl7ZtQOfmjKgwMS931IxkEtgMS/k= X-Received: from irogers.svl.corp.google.com ([2620:15c:2a3:200:e2bd:f1f6:8ea6:8d6c]) (user=irogers job=sendgmr) by 2002:a05:6902:1814:b0:dbe:23c0:baaf with SMTP id cf20-20020a056902181400b00dbe23c0baafmr4500762ybb.6.1704258425107; Tue, 02 Jan 2024 21:07:05 -0800 (PST) Date: Tue, 2 Jan 2024 21:06:18 -0800 In-Reply-To: <20240103050635.391888-1-irogers@google.com> Message-Id: <20240103050635.391888-9-irogers@google.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20240103050635.391888-1-irogers@google.com> X-Mailer: git-send-email 2.43.0.472.g3155946c3a-goog Subject: [PATCH v7 08/25] perf report: Sort child tasks by tid From: Ian Rogers To: Peter Zijlstra , Ingo Molnar , Arnaldo Carvalho de Melo , Mark Rutland , Alexander Shishkin , Jiri Olsa , Namhyung Kim , Ian Rogers , Adrian Hunter , Nick Terrell , Kan Liang , Andi Kleen , Kajol Jain , Athira Rajeev , Huacai Chen , Masami Hiramatsu , Vincent Whitchurch , "Steinar H. Gunderson" , Liam Howlett , Miguel Ojeda , Colin Ian King , Dmitrii Dolgov <9erthalion6@gmail.com>, Yang Jihong , Ming Wang , James Clark , K Prateek Nayak , Sean Christopherson , Leo Yan , Ravi Bangoria , German Gomez , Changbin Du , Paolo Bonzini , Li Dong , Sandipan Das , liuwenyu , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org, Guilherme Amadio Content-Type: text/plain; charset="UTF-8" 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 --- 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 f2ed2b7e80a3..ed0cc813cebb 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -828,35 +829,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; @@ -900,89 +872,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.472.g3155946c3a-goog