@@ -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+
142148struct 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
24982507static 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
34153432static 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
34173435static 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
45004528out_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
46374672out :
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+
47474794static 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
47984845static 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+
48154883static 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+
50865171static 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