From nobody Mon Dec 15 21:46:46 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D96A52144A3; Wed, 5 Feb 2025 20:54:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788885; cv=none; b=nKpHwf1XUxLtinTro1yZo0x35fWvLO0DUpDAINAQc/Nsr5YA7YuzI+sqC4dpp92av+eU2hbMsPOh+VA+wroK7ce6UyseW47011noLgMZ6oUBX0Exk3yeVxVHE7SCBMc5MbrQLUbHyxaMIE+cc8+2A3DnVT4yKknq/PxUFz/CHQo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788885; c=relaxed/simple; bh=/CbTKhO1vIKko2EDqbn2o86kZt9yDBeYWk+0kvM0ou0=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Wl5LsIE+ifpgFmKnK35FpMr75as0neDzFBBZ2Zyt4idoG9lL1ZAVizhIvDE0Dl6q0ZUgNx5MIdOeF7g+coDZ8kOgzM+uGSLrzpDFEIktY4k0Vgi8zrR/AKJpG8HTe3jNxKawl7K9XX9slVuFsIf33DEwRbCzzIVegS0smhDwmHU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=bGS7P5OO; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="bGS7P5OO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E6E12C4CEE5; Wed, 5 Feb 2025 20:54:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738788885; bh=/CbTKhO1vIKko2EDqbn2o86kZt9yDBeYWk+0kvM0ou0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=bGS7P5OOhbiHvh9GgjuxaScq7m1GoZ25+scPmRfYgtwDNbUklSMYu7MV/wiQvVGnw L+6kmp7OJA3vWqOgYl1YwDn8FmEYkLG72RPcKT9uYOeqbSZgwjljOQVL9qNd3CcbSL 3ojlgPhYTR5RuYEpf6Z8QkComP05vYx9pxLipnH/hBJZPZUR7QuD9tERFmhYSaVyzu QNBQfCNVsVsdiDM3iM5cJ4GGLx9YfaJHxYE28QNUmBbctqG3nbcNYMXsiPyLridN1M 9vNo7qV6Iei3+z755GNiMTEHBMdY+S5l9w/7tv/G51CzIbqCYvhI+bk0VxOEOPrcrq fO+jgnnVv829Q== From: Namhyung Kim To: Arnaldo Carvalho de Melo , Ian Rogers , Kan Liang Cc: Jiri Olsa , Adrian Hunter , Peter Zijlstra , Ingo Molnar , LKML , linux-perf-users@vger.kernel.org, Howard Chu Subject: [PATCH v3 1/4] perf trace: Allocate syscall stats only if summary is on Date: Wed, 5 Feb 2025 12:54:40 -0800 Message-ID: <20250205205443.1986408-2-namhyung@kernel.org> X-Mailer: git-send-email 2.48.1.502.g6dc24dfdaf-goog In-Reply-To: <20250205205443.1986408-1-namhyung@kernel.org> References: <20250205205443.1986408-1-namhyung@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The syscall stats are used only when summary is requested. Let's avoid unnecessary operations. While at it, let's pass 'trace' pointer directly instead of passing 'output' file pointer and 'summary' option in the 'trace' separately. Acked-by: Howard Chu Signed-off-by: Namhyung Kim Acked-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ac97632f13dc8f7c..7e0324a2e9182088 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1522,13 +1522,14 @@ struct thread_trace { struct intlist *syscall_stats; }; =20 -static struct thread_trace *thread_trace__new(void) +static struct thread_trace *thread_trace__new(struct trace *trace) { struct thread_trace *ttrace =3D zalloc(sizeof(struct thread_trace)); =20 if (ttrace) { ttrace->files.max =3D -1; - ttrace->syscall_stats =3D intlist__new(NULL); + if (trace->summary) + ttrace->syscall_stats =3D intlist__new(NULL); } =20 return ttrace; @@ -1550,7 +1551,7 @@ static void thread_trace__delete(void *pttrace) free(ttrace); } =20 -static struct thread_trace *thread__trace(struct thread *thread, FILE *fp) +static struct thread_trace *thread__trace(struct thread *thread, struct tr= ace *trace) { struct thread_trace *ttrace; =20 @@ -1558,7 +1559,7 @@ static struct thread_trace *thread__trace(struct thre= ad *thread, FILE *fp) goto fail; =20 if (thread__priv(thread) =3D=3D NULL) - thread__set_priv(thread, thread_trace__new()); + thread__set_priv(thread, thread_trace__new(trace)); =20 if (thread__priv(thread) =3D=3D NULL) goto fail; @@ -1568,7 +1569,7 @@ static struct thread_trace *thread__trace(struct thre= ad *thread, FILE *fp) =20 return ttrace; fail: - color_fprintf(fp, PERF_COLOR_RED, + color_fprintf(trace->output, PERF_COLOR_RED, "WARNING: not enough memory, dropping samples!\n"); return NULL; } @@ -2622,7 +2623,7 @@ static int trace__sys_enter(struct trace *trace, stru= ct evsel *evsel, return -1; =20 thread =3D machine__findnew_thread(trace->host, sample->pid, sample->tid); - ttrace =3D thread__trace(thread, trace->output); + ttrace =3D thread__trace(thread, trace); if (ttrace =3D=3D NULL) goto out_put; =20 @@ -2699,7 +2700,7 @@ static int trace__fprintf_sys_enter(struct trace *tra= ce, struct evsel *evsel, return -1; =20 thread =3D machine__findnew_thread(trace->host, sample->pid, sample->tid); - ttrace =3D thread__trace(thread, trace->output); + ttrace =3D thread__trace(thread, trace); /* * We need to get ttrace just to make sure it is there when syscall__scnp= rintf_args() * and the rest of the beautifiers accessing it via struct syscall_arg to= uches it. @@ -2771,7 +2772,7 @@ static int trace__sys_exit(struct trace *trace, struc= t evsel *evsel, return -1; =20 thread =3D machine__findnew_thread(trace->host, sample->pid, sample->tid); - ttrace =3D thread__trace(thread, trace->output); + ttrace =3D thread__trace(thread, trace); if (ttrace =3D=3D NULL) goto out_put; =20 @@ -2960,7 +2961,7 @@ static int trace__sched_stat_runtime(struct trace *tr= ace, struct evsel *evsel, struct thread *thread =3D machine__findnew_thread(trace->host, sample->pid, sample->tid); - struct thread_trace *ttrace =3D thread__trace(thread, trace->output); + struct thread_trace *ttrace =3D thread__trace(thread, trace); =20 if (ttrace =3D=3D NULL) goto out_dump; @@ -3214,7 +3215,7 @@ static int trace__pgfault(struct trace *trace, } } =20 - ttrace =3D thread__trace(thread, trace->output); + ttrace =3D thread__trace(thread, trace); if (ttrace =3D=3D NULL) goto out_put; =20 --=20 2.48.1.502.g6dc24dfdaf-goog From nobody Mon Dec 15 21:46:46 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 649B8214A68; Wed, 5 Feb 2025 20:54:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788886; cv=none; b=pwQDOWoTRZvPRUbxmR8BZc1n3Rlgr7TpWNKybntYpAYQjtLnWrJ3y+QZpnzcoTOUGOxWDEjF7zdYOlKsPNjH/wFqVe1DRkOVNGs+gZnROaqzPtxuijTlrxg+DP86BbzAn3Z/DD33RPSumyabMWBGvvgm6Lm2vE1LhRMntLbPVK8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788886; c=relaxed/simple; bh=/WTAgk9sQpEsxfpN55ZnoqFPeaI1ULSneciI0bQqFs8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=pbyHyaU+KYGvBjRAORBCohSfoUo5rb6oyy629g/yGrDkXfsV/Qg4zlE82iJs28fOKKXGcHLX2Fk8uqVmxGK+FcxG7YrCdy0GOLr60B8jz+45vlgC+7rx/TPwAxcyDAHi2pWyJJ+qYp9acOa7Sa1c63hUUD4vanYchap9R4T9E4Q= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CrpcE3qm; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CrpcE3qm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 83B56C4CEE2; Wed, 5 Feb 2025 20:54:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738788885; bh=/WTAgk9sQpEsxfpN55ZnoqFPeaI1ULSneciI0bQqFs8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=CrpcE3qmgBL2kUjv+pgn/w2QGlwq4FJEr+oNRUFiNS3ZCSRtTJ5xjsELyxqbzs4UH cne4yoP02b8jFsl6U3HDYnYPegeDNWiATtIsBvZZUZBip/FLfe0wRIb1Gq/d8o2A/a SShksRmVdxvc+8Vzw2MBW8gwPj34FhCUAN7ojYaMqeLLEAso3VruOAz4AiHoFRxYbu Q2Q7pDMTZNg1XAxdQnxBqZI1S37kjh6CSQRyLQF+qQ7BqZADbukzAP29ii91sqgR3j 9FFB3R/mP6uA2AnmHXzMyVnAyR9rjvTMZMyt29lybyKtdyC3csbSqyYbfsZpEVZQho gQDk0vxje/lxg== From: Namhyung Kim To: Arnaldo Carvalho de Melo , Ian Rogers , Kan Liang Cc: Jiri Olsa , Adrian Hunter , Peter Zijlstra , Ingo Molnar , LKML , linux-perf-users@vger.kernel.org, Howard Chu Subject: [PATCH v3 2/4] perf trace: Convert syscall_stats to hashmap Date: Wed, 5 Feb 2025 12:54:41 -0800 Message-ID: <20250205205443.1986408-3-namhyung@kernel.org> X-Mailer: git-send-email 2.48.1.502.g6dc24dfdaf-goog In-Reply-To: <20250205205443.1986408-1-namhyung@kernel.org> References: <20250205205443.1986408-1-namhyung@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" It was using a RBtree-based int-list as a hash and a custom resort logic for that. As we have hashmap, let's convert to it and add a custom sort function for the hashmap entries using an array. It should be faster and more light-weighted. It's also to prepare supporting system-wide syscall stats. No functional changes intended. Acked-by: Howard Chu Signed-off-by: Namhyung Kim Acked-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 117 ++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 29 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 7e0324a2e9182088..5e37f05737b75a14 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -39,6 +39,7 @@ #include "util/synthetic-events.h" #include "util/evlist.h" #include "util/evswitch.h" +#include "util/hashmap.h" #include "util/mmap.h" #include #include @@ -63,7 +64,6 @@ #include "print_binary.h" #include "string2.h" #include "syscalltbl.h" -#include "rb_resort.h" #include "../perf.h" #include "trace_augment.h" =20 @@ -1519,17 +1519,50 @@ struct thread_trace { struct file *table; } files; =20 - struct intlist *syscall_stats; + struct hashmap *syscall_stats; }; =20 +static size_t syscall_id_hash(long key, void *ctx __maybe_unused) +{ + return key; +} + +static bool syscall_id_equal(long key1, long key2, void *ctx __maybe_unuse= d) +{ + return key1 =3D=3D key2; +} + +static struct hashmap *alloc_syscall_stats(void) +{ + return hashmap__new(syscall_id_hash, syscall_id_equal, NULL); +} + +static void delete_syscall_stats(struct hashmap *syscall_stats) +{ + struct hashmap_entry *pos; + size_t bkt; + + if (syscall_stats =3D=3D NULL) + return; + + hashmap__for_each_entry(syscall_stats, pos, bkt) + free(pos->pvalue); + hashmap__free(syscall_stats); +} + static struct thread_trace *thread_trace__new(struct trace *trace) { struct thread_trace *ttrace =3D zalloc(sizeof(struct thread_trace)); =20 if (ttrace) { ttrace->files.max =3D -1; - if (trace->summary) - ttrace->syscall_stats =3D intlist__new(NULL); + if (trace->summary) { + ttrace->syscall_stats =3D alloc_syscall_stats(); + if (IS_ERR(ttrace->syscall_stats)) { + free(ttrace); + ttrace =3D NULL; + } + } } =20 return ttrace; @@ -1544,7 +1577,7 @@ static void thread_trace__delete(void *pttrace) if (!ttrace) return; =20 - intlist__delete(ttrace->syscall_stats); + delete_syscall_stats(ttrace->syscall_stats); ttrace->syscall_stats =3D NULL; thread_trace__free_files(ttrace); zfree(&ttrace->entry_str); @@ -2463,22 +2496,19 @@ struct syscall_stats { static void thread__update_stats(struct thread *thread, struct thread_trac= e *ttrace, int id, struct perf_sample *sample, long err, bool errno_summary) { - struct int_node *inode; - struct syscall_stats *stats; + struct syscall_stats *stats =3D NULL; u64 duration =3D 0; =20 - inode =3D intlist__findnew(ttrace->syscall_stats, id); - if (inode =3D=3D NULL) - return; - - stats =3D inode->priv; - if (stats =3D=3D NULL) { + if (!hashmap__find(ttrace->syscall_stats, id, &stats)) { stats =3D zalloc(sizeof(*stats)); if (stats =3D=3D NULL) return; =20 init_stats(&stats->stats); - inode->priv =3D stats; + if (hashmap__add(ttrace->syscall_stats, id, stats) < 0) { + free(stats); + return; + } } =20 if (ttrace->entry_time && sample->time > ttrace->entry_time) @@ -4617,18 +4647,45 @@ static size_t trace__fprintf_threads_header(FILE *f= p) return printed; } =20 -DEFINE_RESORT_RB(syscall_stats, a->msecs > b->msecs, +struct syscall_entry { struct syscall_stats *stats; double msecs; int syscall; -) +}; + +static int entry_cmp(const void *e1, const void *e2) +{ + const struct syscall_entry *entry1 =3D e1; + const struct syscall_entry *entry2 =3D e2; + + return entry1->msecs > entry2->msecs ? -1 : 1; +} + +static struct syscall_entry *thread__sort_stats(struct thread_trace *ttrac= e) { - struct int_node *source =3D rb_entry(nd, struct int_node, rb_node); - struct syscall_stats *stats =3D source->priv; + struct syscall_entry *entry; + struct hashmap_entry *pos; + unsigned bkt, i, nr; + + nr =3D ttrace->syscall_stats->sz; + entry =3D malloc(nr * sizeof(*entry)); + if (entry =3D=3D NULL) + return NULL; + + i =3D 0; + hashmap__for_each_entry(ttrace->syscall_stats, pos, bkt) { + struct syscall_stats *ss =3D pos->pvalue; + struct stats *st =3D &ss->stats; =20 - entry->syscall =3D source->i; - entry->stats =3D stats; - entry->msecs =3D stats ? (u64)stats->stats.n * (avg_stats(&stats->stats= ) / NSEC_PER_MSEC) : 0; + entry[i].stats =3D ss; + entry[i].msecs =3D (u64)st->n * (avg_stats(st) / NSEC_PER_MSEC); + entry[i].syscall =3D pos->key; + i++; + } + assert(i =3D=3D nr); + + qsort(entry, nr, sizeof(*entry), entry_cmp); + return entry; } =20 static size_t thread__dump_stats(struct thread_trace *ttrace, @@ -4636,10 +4693,10 @@ static size_t thread__dump_stats(struct thread_trac= e *ttrace, { size_t printed =3D 0; struct syscall *sc; - struct rb_node *nd; - DECLARE_RESORT_RB_INTLIST(syscall_stats, ttrace->syscall_stats); + struct syscall_entry *entries; =20 - if (syscall_stats =3D=3D NULL) + entries =3D thread__sort_stats(ttrace); + if (entries =3D=3D NULL) return 0; =20 printed +=3D fprintf(fp, "\n"); @@ -4648,8 +4705,10 @@ static size_t thread__dump_stats(struct thread_trace= *ttrace, printed +=3D fprintf(fp, " (msec) = (msec) (msec) (msec) (%%)\n"); printed +=3D fprintf(fp, " --------------- -------- ------ -------- --= ------- --------- --------- ------\n"); =20 - resort_rb__for_each_entry(nd, syscall_stats) { - struct syscall_stats *stats =3D syscall_stats_entry->stats; + for (size_t i =3D 0; i < ttrace->syscall_stats->sz; i++) { + struct syscall_entry *entry =3D &entries[i]; + struct syscall_stats *stats =3D entry->stats; + if (stats) { double min =3D (double)(stats->stats.min) / NSEC_PER_MSEC; double max =3D (double)(stats->stats.max) / NSEC_PER_MSEC; @@ -4660,10 +4719,10 @@ static size_t thread__dump_stats(struct thread_trac= e *ttrace, pct =3D avg ? 100.0 * stddev_stats(&stats->stats) / avg : 0.0; avg /=3D NSEC_PER_MSEC; =20 - sc =3D &trace->syscalls.table[syscall_stats_entry->syscall]; + sc =3D &trace->syscalls.table[entry->syscall]; printed +=3D fprintf(fp, " %-15s", sc->name); printed +=3D fprintf(fp, " %8" PRIu64 " %6" PRIu64 " %9.3f %9.3f %9.3f", - n, stats->nr_failures, syscall_stats_entry->msecs, min, avg); + n, stats->nr_failures, entry->msecs, min, avg); printed +=3D fprintf(fp, " %9.3f %9.2f%%\n", max, pct); =20 if (trace->errno_summary && stats->nr_failures) { @@ -4677,7 +4736,7 @@ static size_t thread__dump_stats(struct thread_trace = *ttrace, } } =20 - resort_rb__delete(syscall_stats); + free(entries); printed +=3D fprintf(fp, "\n\n"); =20 return printed; --=20 2.48.1.502.g6dc24dfdaf-goog From nobody Mon Dec 15 21:46:46 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id CD02E214A78; Wed, 5 Feb 2025 20:54:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788886; cv=none; b=beLChxuj2hW2AiRtMi60xJG4RJk6XgvqFbIw1dAYeTK9ojWWsb01Of1qZ9Cc1S2lTUpIRKTa/voFhgg6vDpkJwPvdgsq3mRgSh6zFGemQ3XCjHy2pW3i8nkqtsJj0pgy+yIeTL5bXjYq6dWhD9iRnSMthDAOpYqLH9OpmzOUxs4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788886; c=relaxed/simple; bh=FDbNvgt9pIwWRO1IKjBTH36aZ5Jxdu/UOAkSa8LWK+4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YmtImr+kAMjPwxLtN1CrbD3DaQk3SEbJMJU5XKSGwYEK8cgEHv/lMzw/QZ+/eMJu3wST+gmHHv6GP0Q8gNmpYIlwMy/qA7gDllSGcShPAGcc1FRg5VfO1+23tVUxT9WvqCprlpSZMVtRIC3fMivm+wCJ5TvSTUwI6eVgAnk3O0E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Q8/cDDhm; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="Q8/cDDhm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 05774C4CEDD; Wed, 5 Feb 2025 20:54:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738788886; bh=FDbNvgt9pIwWRO1IKjBTH36aZ5Jxdu/UOAkSa8LWK+4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Q8/cDDhmvWm3lU0gOzgnglZB2NmTR9lDnFCQsLuQ6k++dgvSFlzaJs1mGLXFO93VE I8lOjF+0wcMzwat1bxkOZslSs/VLAt8sGXZlD1NYtgAJ1mf5tAJxJDxA6NM39si+Zp i6cJIpxJ53HXKxoLsVPGUqpSVOwNVe4C3WiK7gWeeEMGSoIzA0ogDQ6xz4zmyW0Rac zj/foFQcWO/OsLWPmy75XI8hV7aWPq+irg/jqWgXkDaaHSaVhk8wurmllUqKbbKq7S D0qTum5zE2RHXNcx7vpQ1Rg5wDvObHfbW6w1UDf9lJRD3JTqaTBOGZPq2BhfpXSW6P 5MaTShpQ/UCsw== From: Namhyung Kim To: Arnaldo Carvalho de Melo , Ian Rogers , Kan Liang Cc: Jiri Olsa , Adrian Hunter , Peter Zijlstra , Ingo Molnar , LKML , linux-perf-users@vger.kernel.org, Howard Chu Subject: [PATCH v3 3/4] perf tools: Get rid of now-unused rb_resort.h Date: Wed, 5 Feb 2025 12:54:42 -0800 Message-ID: <20250205205443.1986408-4-namhyung@kernel.org> X-Mailer: git-send-email 2.48.1.502.g6dc24dfdaf-goog In-Reply-To: <20250205205443.1986408-1-namhyung@kernel.org> References: <20250205205443.1986408-1-namhyung@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" It was only used in perf trace and it switched to use hashmap instead. Let's delete the code. Acked-by: Howard Chu Signed-off-by: Namhyung Kim Acked-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo --- tools/perf/util/rb_resort.h | 146 ------------------------------------ 1 file changed, 146 deletions(-) delete mode 100644 tools/perf/util/rb_resort.h diff --git a/tools/perf/util/rb_resort.h b/tools/perf/util/rb_resort.h deleted file mode 100644 index d927a0d250528505..0000000000000000 --- a/tools/perf/util/rb_resort.h +++ /dev/null @@ -1,146 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _PERF_RESORT_RB_H_ -#define _PERF_RESORT_RB_H_ -/* - * Template for creating a class to resort an existing rb_tree according to - * a new sort criteria, that must be present in the entries of the source - * rb_tree. - * - * (c) 2016 Arnaldo Carvalho de Melo - * - * Quick example, resorting threads by its shortname: - * - * First define the prefix (threads) to be used for the functions and data - * structures created, and provide an expression for the sorting, then the - * fields to be present in each of the entries in the new, sorted, rb_tree. - * - * The body of the init function should collect the fields, maybe - * pre-calculating them from multiple entries in the original 'entry' from - * the rb_tree used as a source for the entries to be sorted: - -DEFINE_RB_RESORT_RB(threads, strcmp(a->thread->shortname, - b->thread->shortname) < 0, - struct thread *thread; -) -{ - entry->thread =3D rb_entry(nd, struct thread, rb_node); -} - - * After this it is just a matter of instantiating it and iterating it, - * for a few data structures with existing rb_trees, such as 'struct machi= ne', - * helpers are available to get the rb_root and the nr_entries: - - DECLARE_RESORT_RB_MACHINE_THREADS(threads, machine_ptr); - - * This will instantiate the new rb_tree and a cursor for it, that can be = used as: - - struct rb_node *nd; - - resort_rb__for_each_entry(nd, threads) { - struct thread *t =3D threads_entry; - printf("%s: %d\n", t->shortname, t->tid); - } - - * Then delete it: - - resort_rb__delete(threads); - - * The name of the data structures and functions will have a _sorted suffix - * right before the method names, i.e. will look like: - * - * struct threads_sorted_entry {} - * threads_sorted__insert() - */ - -#define DEFINE_RESORT_RB(__name, __comp, ...) \ -struct __name##_sorted_entry { \ - struct rb_node rb_node; \ - __VA_ARGS__ \ -}; \ -static void __name##_sorted__init_entry(struct rb_node *nd, \ - struct __name##_sorted_entry *entry); \ - \ -static int __name##_sorted__cmp(struct rb_node *nda, struct rb_node *ndb) \ -{ \ - struct __name##_sorted_entry *a, *b; \ - a =3D rb_entry(nda, struct __name##_sorted_entry, rb_node); \ - b =3D rb_entry(ndb, struct __name##_sorted_entry, rb_node); \ - return __comp; \ -} \ - \ -struct __name##_sorted { \ - struct rb_root entries; \ - struct __name##_sorted_entry nd[0]; \ -}; \ - \ -static void __name##_sorted__insert(struct __name##_sorted *sorted, \ - struct rb_node *sorted_nd) \ -{ \ - struct rb_node **p =3D &sorted->entries.rb_node, *parent =3D NULL; \ - while (*p !=3D NULL) { \ - parent =3D *p; \ - if (__name##_sorted__cmp(sorted_nd, parent)) \ - p =3D &(*p)->rb_left; \ - else \ - p =3D &(*p)->rb_right; \ - } \ - rb_link_node(sorted_nd, parent, p); \ - rb_insert_color(sorted_nd, &sorted->entries); \ -} \ - \ -static void __name##_sorted__sort(struct __name##_sorted *sorted, \ - struct rb_root *entries) \ -{ \ - struct rb_node *nd; \ - unsigned int i =3D 0; \ - for (nd =3D rb_first(entries); nd; nd =3D rb_next(nd)) { \ - struct __name##_sorted_entry *snd =3D &sorted->nd[i++]; \ - __name##_sorted__init_entry(nd, snd); \ - __name##_sorted__insert(sorted, &snd->rb_node); \ - } \ -} \ - \ -static struct __name##_sorted *__name##_sorted__new(struct rb_root *entrie= s, \ - int nr_entries) \ -{ \ - struct __name##_sorted *sorted; \ - sorted =3D malloc(sizeof(*sorted) + sizeof(sorted->nd[0]) * nr_entries); \ - if (sorted) { \ - sorted->entries =3D RB_ROOT; \ - __name##_sorted__sort(sorted, entries); \ - } \ - return sorted; \ -} \ - \ -static void __name##_sorted__delete(struct __name##_sorted *sorted) \ -{ \ - free(sorted); \ -} \ - \ -static void __name##_sorted__init_entry(struct rb_node *nd, \ - struct __name##_sorted_entry *entry) - -#define DECLARE_RESORT_RB(__name) \ -struct __name##_sorted_entry *__name##_entry; \ -struct __name##_sorted *__name =3D __name##_sorted__new - -#define resort_rb__for_each_entry(__nd, __name) \ - for (__nd =3D rb_first(&__name->entries); \ - __name##_entry =3D rb_entry(__nd, struct __name##_sorted_entry, \ - rb_node), __nd; \ - __nd =3D rb_next(__nd)) - -#define resort_rb__delete(__name) \ - __name##_sorted__delete(__name), __name =3D NULL - -/* - * Helpers for other classes that contains both an rbtree and the - * number of entries in it: - */ - -/* For 'struct intlist' */ -#define DECLARE_RESORT_RB_INTLIST(__name, __ilist) \ - DECLARE_RESORT_RB(__name)(&__ilist->rblist.entries.rb_root, \ - __ilist->rblist.nr_entries) - -#endif /* _PERF_RESORT_RB_H_ */ --=20 2.48.1.502.g6dc24dfdaf-goog From nobody Mon Dec 15 21:46:46 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B1AC214A9B; Wed, 5 Feb 2025 20:54:46 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788887; cv=none; b=JB8KB5vF74dG9rZDoP7xK3h/nY4fnkMR9ILwS52QXe/AttuOeIvPqEkMvxm9b652+7NjJVKiXy6BjmdoJXKaxokfPImq90Kg6NnGP4dQDaJxo4T5XPxMRdVU9kS6rMNONQo7Zt0aHBlZUQFfRdDCEgnTO/K5ID8578ouvfOkEm8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1738788887; c=relaxed/simple; bh=XE6Bvd50XlsQeab9XMbWM3cs5Q4mASteJ33+ZYVfOu8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=hcSDBGMpgQx9Fmdf5LpaLliWUHrOMMsMS23rKRHtYDZ2KF+TxWE3iFAo+Fk4+GJSX4ieVPGytryA2VHdksgQM4D6Cf57e/AOctCJKWutKkoYsnOx4DDdZLf3hKzi5zjtsexQgSTAfPYGAt8C9DnAIdoYECE6ujFV+tu2QirilHQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=jSm14BcC; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="jSm14BcC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 79B44C4CEEC; Wed, 5 Feb 2025 20:54:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1738788886; bh=XE6Bvd50XlsQeab9XMbWM3cs5Q4mASteJ33+ZYVfOu8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jSm14BcCcKnsAMdHSg9sOcnJHVmX42taIJyR8wwmwrHFDbog34F1Oq4Wh+TQee8l0 Si43gyWncNja8kpN0lEhQeKITEEQRkPzn52GALBBSlS+8mOAF9u1LeAqgT90BD/dKX DxXNahKFOuJ5+H3j1X71cDcnwxAAvOMGiD05nU5eWUri+aLBg0BG2D2BGxf4Nc+WeZ vg0J674DJO1Gp/sxZTgNBNykyc6hdEuDNX+fHeXViH1z5+NC2YNxHFaVcTFzR/pEol 0LK1SnFfCQmNnkRGXOqrlKEPJvkunK788iOhMkBibtQlxd6nzCedc0PiUjvcmhGuuk 78x6aiFVYOcNQ== From: Namhyung Kim To: Arnaldo Carvalho de Melo , Ian Rogers , Kan Liang Cc: Jiri Olsa , Adrian Hunter , Peter Zijlstra , Ingo Molnar , LKML , linux-perf-users@vger.kernel.org, Howard Chu Subject: [PATCH v3 4/4] perf trace: Add --summary-mode option Date: Wed, 5 Feb 2025 12:54:43 -0800 Message-ID: <20250205205443.1986408-5-namhyung@kernel.org> X-Mailer: git-send-email 2.48.1.502.g6dc24dfdaf-goog In-Reply-To: <20250205205443.1986408-1-namhyung@kernel.org> References: <20250205205443.1986408-1-namhyung@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The --summary-mode option will select how to show the syscall summary at the end. By default, it'll show the summary for each thread and it's the same as if --summary-mode=3Dthread is passed. The other option is to show total summary, which is --summary-mode=3Dtotal. I'd like to have this instead of a separate option like --total-summary because we may want to add a new summary mode (by cgroup) later. $ sudo ./perf trace -as --summary-mode=3Dtotal sleep 1 Summary of events: total, 21580 events syscall calls errors total min avg max = stddev (msec) (msec) (msec) (msec)= (%) --------------- -------- ------ -------- --------- --------- --------= - ------ epoll_wait 1305 0 14716.712 0.000 11.277 551.52= 9 8.87% futex 1256 89 13331.197 0.000 10.614 733.72= 2 15.49% poll 669 0 6806.618 0.000 10.174 459.31= 6 11.77% ppoll 220 0 3968.797 0.000 18.040 516.77= 5 25.35% clock_nanosleep 1 0 1000.027 1000.027 1000.027 1000.02= 7 0.00% epoll_pwait 21 0 592.783 0.000 28.228 522.29= 3 88.29% nanosleep 16 0 60.515 0.000 3.782 10.12= 3 33.33% ioctl 510 0 4.284 0.001 0.008 0.18= 2 8.84% recvmsg 1434 775 3.497 0.001 0.002 0.17= 4 6.37% write 1393 0 2.854 0.001 0.002 0.01= 7 1.79% read 1063 100 2.236 0.000 0.002 0.08= 3 5.11% ... Acked-by: Howard Chu Signed-off-by: Namhyung Kim Acked-by: Arnaldo Carvalho de Melo Reviewed-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-trace.txt | 4 + tools/perf/builtin-trace.c | 129 ++++++++++++++++++++---- 2 files changed, 114 insertions(+), 19 deletions(-) diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documenta= tion/perf-trace.txt index fb3d2af33844c06b..887dc37773d0f4d6 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -150,6 +150,10 @@ the thread executes on the designated CPUs. Default is= to monitor all CPUs. To be used with -s or -S, to show stats for the errnos experienced by syscalls, using only this option will trigger --summary. =20 +--summary-mode=3Dmode:: + To be used with -s or -S, to select how to show summary. By default it'll + show the syscall summary by thread. Possible values are: thread, total. + --tool_stats:: Show tool stats such as number of times fd->pathname was discovered thru hooking the open syscall return + vfs_getname or via reading /proc/pid/fd= , etc. diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5e37f05737b75a14..8411dc5e67198303 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -139,6 +139,12 @@ struct syscall_fmt { bool hexret; }; =20 +enum summary_mode { + SUMMARY__NONE =3D 0, + SUMMARY__BY_TOTAL, + SUMMARY__BY_THREAD, +}; + struct trace { struct perf_tool tool; struct syscalltbl *sctbl; @@ -177,14 +183,17 @@ struct trace { pid_t *entries; struct bpf_map *map; } filter_pids; + struct hashmap *syscall_stats; double duration_filter; double runtime_ms; + unsigned long pfmaj, pfmin; struct { u64 vfs_getname, proc_getname; } stats; unsigned int max_stack; unsigned int min_stack; + enum summary_mode summary_mode; int raw_augmented_syscalls_args_size; bool raw_augmented_syscalls; bool fd_path_disabled; @@ -2494,18 +2503,23 @@ struct syscall_stats { }; =20 static void thread__update_stats(struct thread *thread, struct thread_trac= e *ttrace, - int id, struct perf_sample *sample, long err, bool errno_summary) + int id, struct perf_sample *sample, long err, + struct trace *trace) { + struct hashmap *syscall_stats =3D ttrace->syscall_stats; struct syscall_stats *stats =3D NULL; u64 duration =3D 0; =20 - if (!hashmap__find(ttrace->syscall_stats, id, &stats)) { + if (trace->summary_mode =3D=3D SUMMARY__BY_TOTAL) + syscall_stats =3D trace->syscall_stats; + + if (!hashmap__find(syscall_stats, id, &stats)) { stats =3D zalloc(sizeof(*stats)); if (stats =3D=3D NULL) return; =20 init_stats(&stats->stats); - if (hashmap__add(ttrace->syscall_stats, id, stats) < 0) { + if (hashmap__add(syscall_stats, id, stats) < 0) { free(stats); return; } @@ -2519,7 +2533,7 @@ static void thread__update_stats(struct thread *threa= d, struct thread_trace *ttr if (err < 0) { ++stats->nr_failures; =20 - if (!errno_summary) + if (!trace->errno_summary) return; =20 err =3D -err; @@ -2811,7 +2825,7 @@ static int trace__sys_exit(struct trace *trace, struc= t evsel *evsel, ret =3D perf_evsel__sc_tp_uint(evsel, ret, sample); =20 if (trace->summary) - thread__update_stats(thread, ttrace, id, sample, ret, trace->errno_summa= ry); + thread__update_stats(thread, ttrace, id, sample, ret, trace); =20 if (!trace->fd_path_disabled && sc->is_open && ret >=3D 0 && ttrace->file= name.pending_open) { trace__set_fd_pathname(thread, ret, ttrace->filename.name); @@ -3249,10 +3263,13 @@ static int trace__pgfault(struct trace *trace, if (ttrace =3D=3D NULL) goto out_put; =20 - if (evsel->core.attr.config =3D=3D PERF_COUNT_SW_PAGE_FAULTS_MAJ) + if (evsel->core.attr.config =3D=3D PERF_COUNT_SW_PAGE_FAULTS_MAJ) { ttrace->pfmaj++; - else + trace->pfmaj++; + } else { ttrace->pfmin++; + trace->pfmin++; + } =20 if (trace->summary_only) goto out; @@ -3411,6 +3428,7 @@ static int trace__record(struct trace *trace, int arg= c, const char **argv) } =20 static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp); +static size_t trace__fprintf_total_summary(struct trace *trace, FILE *fp); =20 static bool evlist__add_vfs_getname(struct evlist *evlist) { @@ -4323,6 +4341,12 @@ static int trace__run(struct trace *trace, int argc,= const char **argv) goto out_delete_evlist; } =20 + if (trace->summary_mode =3D=3D SUMMARY__BY_TOTAL) { + trace->syscall_stats =3D alloc_syscall_stats(); + if (trace->syscall_stats =3D=3D NULL) + goto out_delete_evlist; + } + evlist__config(evlist, &trace->opts, &callchain_param); =20 if (forks) { @@ -4483,8 +4507,12 @@ static int trace__run(struct trace *trace, int argc,= const char **argv) ordered_events__flush(&trace->oe.data, OE_FLUSH__FINAL); =20 if (!err) { - if (trace->summary) - trace__fprintf_thread_summary(trace, trace->output); + if (trace->summary) { + if (trace->summary_mode =3D=3D SUMMARY__BY_TOTAL) + trace__fprintf_total_summary(trace, trace->output); + else + trace__fprintf_thread_summary(trace, trace->output); + } =20 if (trace->show_tool_stats) { fprintf(trace->output, "Stats:\n " @@ -4496,6 +4524,7 @@ static int trace__run(struct trace *trace, int argc, = const char **argv) } =20 out_delete_evlist: + delete_syscall_stats(trace->syscall_stats); trace__symbols__exit(trace); evlist__free_syscall_tp_fields(evlist); evlist__delete(evlist); @@ -4623,6 +4652,12 @@ static int trace__replay(struct trace *trace) evsel->handler =3D trace__pgfault; } =20 + if (trace->summary_mode =3D=3D SUMMARY__BY_TOTAL) { + trace->syscall_stats =3D alloc_syscall_stats(); + if (trace->syscall_stats =3D=3D NULL) + goto out; + } + setup_pager(); =20 err =3D perf_session__process_events(session); @@ -4633,12 +4668,13 @@ static int trace__replay(struct trace *trace) trace__fprintf_thread_summary(trace, trace->output); =20 out: + delete_syscall_stats(trace->syscall_stats); perf_session__delete(session); =20 return err; } =20 -static size_t trace__fprintf_threads_header(FILE *fp) +static size_t trace__fprintf_summary_header(FILE *fp) { size_t printed; =20 @@ -4661,19 +4697,19 @@ static int entry_cmp(const void *e1, const void *e2) return entry1->msecs > entry2->msecs ? -1 : 1; } =20 -static struct syscall_entry *thread__sort_stats(struct thread_trace *ttrac= e) +static struct syscall_entry *syscall__sort_stats(struct hashmap *syscall_s= tats) { struct syscall_entry *entry; struct hashmap_entry *pos; unsigned bkt, i, nr; =20 - nr =3D ttrace->syscall_stats->sz; + nr =3D syscall_stats->sz; entry =3D malloc(nr * sizeof(*entry)); if (entry =3D=3D NULL) return NULL; =20 i =3D 0; - hashmap__for_each_entry(ttrace->syscall_stats, pos, bkt) { + hashmap__for_each_entry(syscall_stats, pos, bkt) { struct syscall_stats *ss =3D pos->pvalue; struct stats *st =3D &ss->stats; =20 @@ -4688,14 +4724,14 @@ static struct syscall_entry *thread__sort_stats(str= uct thread_trace *ttrace) return entry; } =20 -static size_t thread__dump_stats(struct thread_trace *ttrace, - struct trace *trace, FILE *fp) +static size_t syscall__dump_stats(struct trace *trace, FILE *fp, + struct hashmap *syscall_stats) { size_t printed =3D 0; struct syscall *sc; struct syscall_entry *entries; =20 - entries =3D thread__sort_stats(ttrace); + entries =3D syscall__sort_stats(syscall_stats); if (entries =3D=3D NULL) return 0; =20 @@ -4705,7 +4741,7 @@ static size_t thread__dump_stats(struct thread_trace = *ttrace, printed +=3D fprintf(fp, " (msec) = (msec) (msec) (msec) (%%)\n"); printed +=3D fprintf(fp, " --------------- -------- ------ -------- --= ------- --------- --------- ------\n"); =20 - for (size_t i =3D 0; i < ttrace->syscall_stats->sz; i++) { + for (size_t i =3D 0; i < syscall_stats->sz; i++) { struct syscall_entry *entry =3D &entries[i]; struct syscall_stats *stats =3D entry->stats; =20 @@ -4742,6 +4778,17 @@ static size_t thread__dump_stats(struct thread_trace= *ttrace, return printed; } =20 +static size_t thread__dump_stats(struct thread_trace *ttrace, + struct trace *trace, FILE *fp) +{ + return syscall__dump_stats(trace, fp, ttrace->syscall_stats); +} + +static size_t system__dump_stats(struct trace *trace, FILE *fp) +{ + return syscall__dump_stats(trace, fp, trace->syscall_stats); +} + static size_t trace__fprintf_thread(FILE *fp, struct thread *thread, struc= t trace *trace) { size_t printed =3D 0; @@ -4795,7 +4842,7 @@ static int trace_nr_events_cmp(void *priv __maybe_unu= sed, =20 static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp) { - size_t printed =3D trace__fprintf_threads_header(fp); + size_t printed =3D trace__fprintf_summary_header(fp); LIST_HEAD(threads); =20 if (machine__thread_list(trace->host, &threads) =3D=3D 0) { @@ -4810,6 +4857,27 @@ static size_t trace__fprintf_thread_summary(struct t= race *trace, FILE *fp) return printed; } =20 +static size_t trace__fprintf_total_summary(struct trace *trace, FILE *fp) +{ + size_t printed =3D trace__fprintf_summary_header(fp); + + printed +=3D fprintf(fp, " total, "); + printed +=3D fprintf(fp, "%lu events", trace->nr_events); + + if (trace->pfmaj) + printed +=3D fprintf(fp, ", %lu majfaults", trace->pfmaj); + if (trace->pfmin) + printed +=3D fprintf(fp, ", %lu minfaults", trace->pfmin); + if (trace->sched) + printed +=3D fprintf(fp, ", %.3f msec\n", trace->runtime_ms); + else if (fputc('\n', fp) !=3D EOF) + ++printed; + + printed +=3D system__dump_stats(trace, fp); + + return printed; +} + static int trace__set_duration(const struct option *opt, const char *str, int unset __maybe_unused) { @@ -5081,6 +5149,23 @@ static int trace__parse_cgroups(const struct option = *opt, const char *str, int u return 0; } =20 +static int trace__parse_summary_mode(const struct option *opt, const char = *str, + int unset __maybe_unused) +{ + struct trace *trace =3D opt->value; + + if (!strcmp(str, "thread")) { + trace->summary_mode =3D SUMMARY__BY_THREAD; + } else if (!strcmp(str, "total")) { + trace->summary_mode =3D SUMMARY__BY_TOTAL; + } else { + pr_err("Unknown summary mode: %s\n", str); + return -1; + } + + return 0; +} + static int trace__config(const char *var, const char *value, void *arg) { struct trace *trace =3D arg; @@ -5228,6 +5313,9 @@ int cmd_trace(int argc, const char **argv) "Show all syscalls and summary with statistics"), OPT_BOOLEAN(0, "errno-summary", &trace.errno_summary, "Show errno stats per syscall, use with -s or -S"), + OPT_CALLBACK(0, "summary-mode", &trace, "mode", + "How to show summary: select thread (default) or total", + trace__parse_summary_mode), OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min", "Trace pagefaults", parse_pagefaults, "maj"), OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"), @@ -5524,8 +5612,11 @@ int cmd_trace(int argc, const char **argv) trace.summary =3D trace.summary_only; =20 /* Keep exited threads, otherwise information might be lost for summary */ - if (trace.summary) + if (trace.summary) { symbol_conf.keep_exited_threads =3D true; + if (trace.summary_mode =3D=3D SUMMARY__NONE) + trace.summary_mode =3D SUMMARY__BY_THREAD; + } =20 if (output_name !=3D NULL) { err =3D trace__open_output(&trace, output_name); --=20 2.48.1.502.g6dc24dfdaf-goog