From nobody Tue Dec 16 05:55: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 --- 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