Skip to content

Commit fc00897

Browse files
committed
perf trace: Add --summary-mode option
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=thread is passed. The other option is to show total summary, which is --summary-mode=total. 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=total 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.529 8.87% futex 1256 89 13331.197 0.000 10.614 733.722 15.49% poll 669 0 6806.618 0.000 10.174 459.316 11.77% ppoll 220 0 3968.797 0.000 18.040 516.775 25.35% clock_nanosleep 1 0 1000.027 1000.027 1000.027 1000.027 0.00% epoll_pwait 21 0 592.783 0.000 28.228 522.293 88.29% nanosleep 16 0 60.515 0.000 3.782 10.123 33.33% ioctl 510 0 4.284 0.001 0.008 0.182 8.84% recvmsg 1434 775 3.497 0.001 0.002 0.174 6.37% write 1393 0 2.854 0.001 0.002 0.017 1.79% read 1063 100 2.236 0.000 0.002 0.083 5.11% ... Reviewed-by: Howard Chu <howardchu95@gmail.com> Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com> Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com> Link: https://lore.kernel.org/r/20250205205443.1986408-5-namhyung@kernel.org Signed-off-by: Namhyung Kim <namhyung@kernel.org>
1 parent bd50a26 commit fc00897

File tree

2 files changed

+114
-19
lines changed

2 files changed

+114
-19
lines changed

tools/perf/Documentation/perf-trace.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
150150
To be used with -s or -S, to show stats for the errnos experienced by
151151
syscalls, using only this option will trigger --summary.
152152

153+
--summary-mode=mode::
154+
To be used with -s or -S, to select how to show summary. By default it'll
155+
show the syscall summary by thread. Possible values are: thread, total.
156+
153157
--tool_stats::
154158
Show tool stats such as number of times fd->pathname was discovered thru
155159
hooking the open syscall return + vfs_getname or via reading /proc/pid/fd, etc.

tools/perf/builtin-trace.c

Lines changed: 110 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ struct syscall_fmt {
139139
bool hexret;
140140
};
141141

142+
enum summary_mode {
143+
SUMMARY__NONE = 0,
144+
SUMMARY__BY_TOTAL,
145+
SUMMARY__BY_THREAD,
146+
};
147+
142148
struct trace {
143149
struct perf_tool tool;
144150
struct syscalltbl *sctbl;
@@ -177,14 +183,17 @@ struct trace {
177183
pid_t *entries;
178184
struct bpf_map *map;
179185
} filter_pids;
186+
struct hashmap *syscall_stats;
180187
double duration_filter;
181188
double runtime_ms;
189+
unsigned long pfmaj, pfmin;
182190
struct {
183191
u64 vfs_getname,
184192
proc_getname;
185193
} stats;
186194
unsigned int max_stack;
187195
unsigned int min_stack;
196+
enum summary_mode summary_mode;
188197
int raw_augmented_syscalls_args_size;
189198
bool raw_augmented_syscalls;
190199
bool fd_path_disabled;
@@ -2496,18 +2505,23 @@ struct syscall_stats {
24962505
};
24972506

24982507
static void thread__update_stats(struct thread *thread, struct thread_trace *ttrace,
2499-
int id, struct perf_sample *sample, long err, bool errno_summary)
2508+
int id, struct perf_sample *sample, long err,
2509+
struct trace *trace)
25002510
{
2511+
struct hashmap *syscall_stats = ttrace->syscall_stats;
25012512
struct syscall_stats *stats = NULL;
25022513
u64 duration = 0;
25032514

2504-
if (!hashmap__find(ttrace->syscall_stats, id, &stats)) {
2515+
if (trace->summary_mode == SUMMARY__BY_TOTAL)
2516+
syscall_stats = trace->syscall_stats;
2517+
2518+
if (!hashmap__find(syscall_stats, id, &stats)) {
25052519
stats = zalloc(sizeof(*stats));
25062520
if (stats == NULL)
25072521
return;
25082522

25092523
init_stats(&stats->stats);
2510-
if (hashmap__add(ttrace->syscall_stats, id, stats) < 0) {
2524+
if (hashmap__add(syscall_stats, id, stats) < 0) {
25112525
free(stats);
25122526
return;
25132527
}
@@ -2521,7 +2535,7 @@ static void thread__update_stats(struct thread *thread, struct thread_trace *ttr
25212535
if (err < 0) {
25222536
++stats->nr_failures;
25232537

2524-
if (!errno_summary)
2538+
if (!trace->errno_summary)
25252539
return;
25262540

25272541
err = -err;
@@ -2813,7 +2827,7 @@ static int trace__sys_exit(struct trace *trace, struct evsel *evsel,
28132827
ret = perf_evsel__sc_tp_uint(evsel, ret, sample);
28142828

28152829
if (trace->summary)
2816-
thread__update_stats(thread, ttrace, id, sample, ret, trace->errno_summary);
2830+
thread__update_stats(thread, ttrace, id, sample, ret, trace);
28172831

28182832
if (!trace->fd_path_disabled && sc->is_open && ret >= 0 && ttrace->filename.pending_open) {
28192833
trace__set_fd_pathname(thread, ret, ttrace->filename.name);
@@ -3251,10 +3265,13 @@ static int trace__pgfault(struct trace *trace,
32513265
if (ttrace == NULL)
32523266
goto out_put;
32533267

3254-
if (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ)
3268+
if (evsel->core.attr.config == PERF_COUNT_SW_PAGE_FAULTS_MAJ) {
32553269
ttrace->pfmaj++;
3256-
else
3270+
trace->pfmaj++;
3271+
} else {
32573272
ttrace->pfmin++;
3273+
trace->pfmin++;
3274+
}
32583275

32593276
if (trace->summary_only)
32603277
goto out;
@@ -3413,6 +3430,7 @@ static int trace__record(struct trace *trace, int argc, const char **argv)
34133430
}
34143431

34153432
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp);
3433+
static size_t trace__fprintf_total_summary(struct trace *trace, FILE *fp);
34163434

34173435
static bool evlist__add_vfs_getname(struct evlist *evlist)
34183436
{
@@ -4325,6 +4343,12 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
43254343
goto out_delete_evlist;
43264344
}
43274345

4346+
if (trace->summary_mode == SUMMARY__BY_TOTAL) {
4347+
trace->syscall_stats = alloc_syscall_stats();
4348+
if (trace->syscall_stats == NULL)
4349+
goto out_delete_evlist;
4350+
}
4351+
43284352
evlist__config(evlist, &trace->opts, &callchain_param);
43294353

43304354
if (forks) {
@@ -4485,8 +4509,12 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
44854509
ordered_events__flush(&trace->oe.data, OE_FLUSH__FINAL);
44864510

44874511
if (!err) {
4488-
if (trace->summary)
4489-
trace__fprintf_thread_summary(trace, trace->output);
4512+
if (trace->summary) {
4513+
if (trace->summary_mode == SUMMARY__BY_TOTAL)
4514+
trace__fprintf_total_summary(trace, trace->output);
4515+
else
4516+
trace__fprintf_thread_summary(trace, trace->output);
4517+
}
44904518

44914519
if (trace->show_tool_stats) {
44924520
fprintf(trace->output, "Stats:\n "
@@ -4498,6 +4526,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
44984526
}
44994527

45004528
out_delete_evlist:
4529+
delete_syscall_stats(trace->syscall_stats);
45014530
trace__symbols__exit(trace);
45024531
evlist__free_syscall_tp_fields(evlist);
45034532
evlist__delete(evlist);
@@ -4625,6 +4654,12 @@ static int trace__replay(struct trace *trace)
46254654
evsel->handler = trace__pgfault;
46264655
}
46274656

4657+
if (trace->summary_mode == SUMMARY__BY_TOTAL) {
4658+
trace->syscall_stats = alloc_syscall_stats();
4659+
if (trace->syscall_stats == NULL)
4660+
goto out;
4661+
}
4662+
46284663
setup_pager();
46294664

46304665
err = perf_session__process_events(session);
@@ -4635,12 +4670,13 @@ static int trace__replay(struct trace *trace)
46354670
trace__fprintf_thread_summary(trace, trace->output);
46364671

46374672
out:
4673+
delete_syscall_stats(trace->syscall_stats);
46384674
perf_session__delete(session);
46394675

46404676
return err;
46414677
}
46424678

4643-
static size_t trace__fprintf_threads_header(FILE *fp)
4679+
static size_t trace__fprintf_summary_header(FILE *fp)
46444680
{
46454681
size_t printed;
46464682

@@ -4663,19 +4699,19 @@ static int entry_cmp(const void *e1, const void *e2)
46634699
return entry1->msecs > entry2->msecs ? -1 : 1;
46644700
}
46654701

4666-
static struct syscall_entry *thread__sort_stats(struct thread_trace *ttrace)
4702+
static struct syscall_entry *syscall__sort_stats(struct hashmap *syscall_stats)
46674703
{
46684704
struct syscall_entry *entry;
46694705
struct hashmap_entry *pos;
46704706
unsigned bkt, i, nr;
46714707

4672-
nr = ttrace->syscall_stats->sz;
4708+
nr = syscall_stats->sz;
46734709
entry = malloc(nr * sizeof(*entry));
46744710
if (entry == NULL)
46754711
return NULL;
46764712

46774713
i = 0;
4678-
hashmap__for_each_entry(ttrace->syscall_stats, pos, bkt) {
4714+
hashmap__for_each_entry(syscall_stats, pos, bkt) {
46794715
struct syscall_stats *ss = pos->pvalue;
46804716
struct stats *st = &ss->stats;
46814717

@@ -4690,14 +4726,14 @@ static struct syscall_entry *thread__sort_stats(struct thread_trace *ttrace)
46904726
return entry;
46914727
}
46924728

4693-
static size_t thread__dump_stats(struct thread_trace *ttrace,
4694-
struct trace *trace, FILE *fp)
4729+
static size_t syscall__dump_stats(struct trace *trace, FILE *fp,
4730+
struct hashmap *syscall_stats)
46954731
{
46964732
size_t printed = 0;
46974733
struct syscall *sc;
46984734
struct syscall_entry *entries;
46994735

4700-
entries = thread__sort_stats(ttrace);
4736+
entries = syscall__sort_stats(syscall_stats);
47014737
if (entries == NULL)
47024738
return 0;
47034739

@@ -4707,7 +4743,7 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
47074743
printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n");
47084744
printed += fprintf(fp, " --------------- -------- ------ -------- --------- --------- --------- ------\n");
47094745

4710-
for (size_t i = 0; i < ttrace->syscall_stats->sz; i++) {
4746+
for (size_t i = 0; i < syscall_stats->sz; i++) {
47114747
struct syscall_entry *entry = &entries[i];
47124748
struct syscall_stats *stats = entry->stats;
47134749

@@ -4744,6 +4780,17 @@ static size_t thread__dump_stats(struct thread_trace *ttrace,
47444780
return printed;
47454781
}
47464782

4783+
static size_t thread__dump_stats(struct thread_trace *ttrace,
4784+
struct trace *trace, FILE *fp)
4785+
{
4786+
return syscall__dump_stats(trace, fp, ttrace->syscall_stats);
4787+
}
4788+
4789+
static size_t system__dump_stats(struct trace *trace, FILE *fp)
4790+
{
4791+
return syscall__dump_stats(trace, fp, trace->syscall_stats);
4792+
}
4793+
47474794
static size_t trace__fprintf_thread(FILE *fp, struct thread *thread, struct trace *trace)
47484795
{
47494796
size_t printed = 0;
@@ -4797,7 +4844,7 @@ static int trace_nr_events_cmp(void *priv __maybe_unused,
47974844

47984845
static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp)
47994846
{
4800-
size_t printed = trace__fprintf_threads_header(fp);
4847+
size_t printed = trace__fprintf_summary_header(fp);
48014848
LIST_HEAD(threads);
48024849

48034850
if (machine__thread_list(trace->host, &threads) == 0) {
@@ -4812,6 +4859,27 @@ static size_t trace__fprintf_thread_summary(struct trace *trace, FILE *fp)
48124859
return printed;
48134860
}
48144861

4862+
static size_t trace__fprintf_total_summary(struct trace *trace, FILE *fp)
4863+
{
4864+
size_t printed = trace__fprintf_summary_header(fp);
4865+
4866+
printed += fprintf(fp, " total, ");
4867+
printed += fprintf(fp, "%lu events", trace->nr_events);
4868+
4869+
if (trace->pfmaj)
4870+
printed += fprintf(fp, ", %lu majfaults", trace->pfmaj);
4871+
if (trace->pfmin)
4872+
printed += fprintf(fp, ", %lu minfaults", trace->pfmin);
4873+
if (trace->sched)
4874+
printed += fprintf(fp, ", %.3f msec\n", trace->runtime_ms);
4875+
else if (fputc('\n', fp) != EOF)
4876+
++printed;
4877+
4878+
printed += system__dump_stats(trace, fp);
4879+
4880+
return printed;
4881+
}
4882+
48154883
static int trace__set_duration(const struct option *opt, const char *str,
48164884
int unset __maybe_unused)
48174885
{
@@ -5083,6 +5151,23 @@ static int trace__parse_cgroups(const struct option *opt, const char *str, int u
50835151
return 0;
50845152
}
50855153

5154+
static int trace__parse_summary_mode(const struct option *opt, const char *str,
5155+
int unset __maybe_unused)
5156+
{
5157+
struct trace *trace = opt->value;
5158+
5159+
if (!strcmp(str, "thread")) {
5160+
trace->summary_mode = SUMMARY__BY_THREAD;
5161+
} else if (!strcmp(str, "total")) {
5162+
trace->summary_mode = SUMMARY__BY_TOTAL;
5163+
} else {
5164+
pr_err("Unknown summary mode: %s\n", str);
5165+
return -1;
5166+
}
5167+
5168+
return 0;
5169+
}
5170+
50865171
static int trace__config(const char *var, const char *value, void *arg)
50875172
{
50885173
struct trace *trace = arg;
@@ -5230,6 +5315,9 @@ int cmd_trace(int argc, const char **argv)
52305315
"Show all syscalls and summary with statistics"),
52315316
OPT_BOOLEAN(0, "errno-summary", &trace.errno_summary,
52325317
"Show errno stats per syscall, use with -s or -S"),
5318+
OPT_CALLBACK(0, "summary-mode", &trace, "mode",
5319+
"How to show summary: select thread (default) or total",
5320+
trace__parse_summary_mode),
52335321
OPT_CALLBACK_DEFAULT('F', "pf", &trace.trace_pgfaults, "all|maj|min",
52345322
"Trace pagefaults", parse_pagefaults, "maj"),
52355323
OPT_BOOLEAN(0, "syscalls", &trace.trace_syscalls, "Trace syscalls"),
@@ -5526,8 +5614,11 @@ int cmd_trace(int argc, const char **argv)
55265614
trace.summary = trace.summary_only;
55275615

55285616
/* Keep exited threads, otherwise information might be lost for summary */
5529-
if (trace.summary)
5617+
if (trace.summary) {
55305618
symbol_conf.keep_exited_threads = true;
5619+
if (trace.summary_mode == SUMMARY__NONE)
5620+
trace.summary_mode = SUMMARY__BY_THREAD;
5621+
}
55315622

55325623
if (output_name != NULL) {
55335624
err = trace__open_output(&trace, output_name);

0 commit comments

Comments
 (0)