Received: by 2002:a05:7412:251c:b0:e2:908c:2ebd with SMTP id w28csp2186669rda; Tue, 24 Oct 2023 15:29:06 -0700 (PDT) X-Google-Smtp-Source: AGHT+IE2uHalgI4ILPdmRiOQEJc4NZABXpRV0X5GT0pAM4cvvoxzZBAw3kGZaw4jDIrbiYeP0yUU X-Received: by 2002:a05:6870:9e90:b0:1e9:da9a:f2d6 with SMTP id pu16-20020a0568709e9000b001e9da9af2d6mr17477577oab.40.1698186546019; Tue, 24 Oct 2023 15:29:06 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698186545; cv=none; d=google.com; s=arc-20160816; b=xY9Y8ldtGPs8vN1htkZ4qLW9QuZzSylsPF16zPZCbXodeyN03jN1cMaCQDHXQiky31 suYXs2+XPEm7ZwdyLKbgUYJemYeBnQCoZW4Wxx/2EiXVAl7H2HcLRCr4h4n5CMmWshOE a7SqdZblQjVtW5Yarz/61Vrzuqt/72en6HoFixGSwieGxb6KUHILXCNiyu49reVDRxw5 FQLgbwAMZt77uvzmNishf9xHYNWjUZuM0Kwyc9e8n96/uOc+a409iDBQAys3ScsQYcpn bu8U2hU5P08U0ogpwss85HNruAmuFQ8OcVB824ldXP1caUaA9nHoK6iCDJzTau3wfe26 5n5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:to:from:subject:references:mime-version :message-id:in-reply-to:date:dkim-signature; bh=fIRcu/3WTvtsQSdzqjdau/PJdT4wd5hzv4KiweKRc9Q=; fh=NAro5oTsDEB2Or99ABwb774QJkwQSkr6EoLVqn/Yde0=; b=dPA5m/qW7Bbz4kQUUrkgpfrzyM9uIiwzquyrfOazzACVbSfPz4qi4ClIRUXPZI3aKV BQuFo8KvHH5PGtxXWa83QaYxCmzqyXFnlsZYCeFkjACFhhcnSVu5UYksUptd0CfMRVJb UWRnZUddkSrq03CAS42dVTdTIincz5O+hoO5+9XkjuuCg3pGjus9FHOUqdH7xnif2jzQ dLcZlIDlYINJYZEbditVmBOcNKZTCyGHY5gnDz71Q4s+9tPVWT1vzfnUKYrHyHIbnOai Ey8f6fsVACjC+GY4C47+kdce3vIv5xiOpwbwSZeaccTkp2UEiGWHfTd5JoAJHL0z7IR0 Upkw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=KEHxNGDm; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from pete.vger.email (pete.vger.email. [2620:137:e000::3:6]) by mx.google.com with ESMTPS id l62-20020a639141000000b005aa9a561867si9175431pge.621.2023.10.24.15.29.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 24 Oct 2023 15:29:05 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) client-ip=2620:137:e000::3:6; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20230601 header.b=KEHxNGDm; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:6 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by pete.vger.email (Postfix) with ESMTP id 76FEC808BE7B; Tue, 24 Oct 2023 15:29:01 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at pete.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344480AbjJXW2g (ORCPT + 99 others); Tue, 24 Oct 2023 18:28:36 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60076 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1344610AbjJXW2P (ORCPT ); Tue, 24 Oct 2023 18:28:15 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1E2111FF7 for ; Tue, 24 Oct 2023 15:25:55 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-d9c4ae201e0so252759276.1 for ; Tue, 24 Oct 2023 15:25:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1698186354; x=1698791154; 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=fIRcu/3WTvtsQSdzqjdau/PJdT4wd5hzv4KiweKRc9Q=; b=KEHxNGDmceKF1GzjwfJ/hvIZ5MVUiNH7xP5GbJs7S4/R6dZBesKAuLRTZ8FcMj0uoU 4ZIBJciWaJqpkCkEsmIXQKKBvEUkU1X4XJhytBpd8HeFnyAX57Vo3sCSE+Q+rqhyF4Jj MBp/5lEGVeg4cwdyS5wtgU3kc8OnO4E3fnCjv29GtgMA9uycuIvgQvnKkQacWdwmKWQF dkK03Prpw6AsmJqOALF12/rWBJwo5LfeCJ03Cbobh886ZrO5V/YxjISJEbIkkRd2LDW9 H/1j4QY4Ak3qYN1LsQmCb6Fa6S1cwfFQILbnfyVeNVV2FoI51nDOALjNPzR1UrzaOtpi /e8Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698186354; x=1698791154; 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=fIRcu/3WTvtsQSdzqjdau/PJdT4wd5hzv4KiweKRc9Q=; b=IcLDNq7Fzv8Y+wD+BtFV5Y6U/tf76w0LvZuetjBFwGx8kHXPSVPAdfh9F8XK3sgKNR rAKD269F1X/OjkkXCZ1WWbY5i+NZ29iKPlvypSYrDwFJwSFzLiv18GjsFlQJgJ3YPzIX dfS92f5fIKDZR1p+7eWBrBDJQWdKtrtKV/u7EzpWfTgjd1H7LF3LMVRQm79gRg9ZeqfD NQdIvwlIji6FhOMwHq0LAfYNcscGcc70cUUtB6Gs/sZvmkPEAEs9w7gX1ftZ2TcKpet+ liT3GXtlECtr/tJtzDcqhy8qHkWKZwJM6olePS0Sea8qBxipjUDAADbkjV7dH8r7mc6v yQTA== X-Gm-Message-State: AOJu0Yxui5IMjAu2vtjPZPH+LosIEfjefbNl3/yZR/whEgX0SGcw3iAQ SW1cDa6eXhiVkCNkNtJdmLbov6Es7zdj X-Received: from irogers.svl.corp.google.com ([2620:15c:2a3:200:93d2:18cc:4d63:45ba]) (user=irogers job=sendgmr) by 2002:a25:d1ca:0:b0:d9c:f811:eb9e with SMTP id i193-20020a25d1ca000000b00d9cf811eb9emr321690ybg.4.1698186354204; Tue, 24 Oct 2023 15:25:54 -0700 (PDT) Date: Tue, 24 Oct 2023 15:23:48 -0700 In-Reply-To: <20231024222353.3024098-1-irogers@google.com> Message-Id: <20231024222353.3024098-46-irogers@google.com> Mime-Version: 1.0 References: <20231024222353.3024098-1-irogers@google.com> X-Mailer: git-send-email 2.42.0.758.gaed0368e0e-goog Subject: [PATCH v3 45/50] 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 , Leo Yan , Song Liu , Sandipan Das , James Clark , Anshuman Khandual , Miguel Ojeda , Liam Howlett , Yang Jihong , Athira Rajeev , Kajol Jain , K Prateek Nayak , Sean Christopherson , Yanteng Si , Ravi Bangoria , German Gomez , Changbin Du , Paolo Bonzini , Masami Hiramatsu , liuwenyu , linux-kernel@vger.kernel.org, linux-perf-users@vger.kernel.org Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-8.4 required=5.0 tests=DKIMWL_WL_MED,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,USER_IN_DEF_DKIM_WL autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on pete.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (pete.vger.email [0.0.0.0]); Tue, 24 Oct 2023 15:29:01 -0700 (PDT) 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 d7f1f3c8cb7d..3731e9bf7000 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -815,35 +816,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; @@ -887,89 +859,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 f9c77119af22..6d7a505850c8 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -3258,6 +3258,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.42.0.758.gaed0368e0e-goog