aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorIngo Molnar <mingo@kernel.org>2013-02-01 05:17:09 -0500
committerIngo Molnar <mingo@kernel.org>2013-02-01 05:17:09 -0500
commit9c4c5fd9e6207f04dbf59c5a9699fded144542e6 (patch)
tree7b08598257898727183d6bb8b2c4824b1b0d52a5 /tools
parent152fefa921535665f95840c08062844ab2f5593e (diff)
parent2ac3634a7e1c8eedc961030c87c5c36ebd5bbf8e (diff)
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core
Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: . Make some POWER7 events available in sysfs, equivalent to what was done on x86, from Sukadev Bhattiprolu. . Add event group view, from Namyung Kim: To use it, 'perf record' should group events when recording. And then perf report parses the saved group relation from file header and prints them together if --group option is provided. You can use 'perf evlist' command to see event group information: $ perf record -e '{ref-cycles,cycles}' noploop 1 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.385 MB perf.data (~16807 samples) ] $ perf evlist --group {ref-cycles,cycles} With this example, default perf report will show you each event separately like this: $ perf report ... # group: {ref-cycles,cycles} # ======== # Samples: 3K of event 'ref-cycles' # Event count (approx.): 3153797218 # # Overhead Command Shared Object Symbol # ........ ....... ................. .......................... 99.84% noploop noploop [.] main 0.07% noploop ld-2.15.so [.] strcmp 0.03% noploop [kernel.kallsyms] [k] timerqueue_del 0.03% noploop [kernel.kallsyms] [k] sched_clock_cpu 0.02% noploop [kernel.kallsyms] [k] account_user_time 0.01% noploop [kernel.kallsyms] [k] __alloc_pages_nodemask 0.00% noploop [kernel.kallsyms] [k] native_write_msr_safe # Samples: 3K of event 'cycles' # Event count (approx.): 3722310525 # # Overhead Command Shared Object Symbol # ........ ....... ................. ......................... 99.76% noploop noploop [.] main 0.11% noploop [kernel.kallsyms] [k] _raw_spin_lock 0.06% noploop [kernel.kallsyms] [k] find_get_page 0.03% noploop [kernel.kallsyms] [k] sched_clock_cpu 0.02% noploop [kernel.kallsyms] [k] rcu_check_callbacks 0.02% noploop [kernel.kallsyms] [k] __current_kernel_time 0.00% noploop [kernel.kallsyms] [k] native_write_msr_safe In this case the event group information will be shown in the end of header area. So you can use --group option to enable event group view. $ perf report --group ... # group: {ref-cycles,cycles} # ======== # Samples: 7K of event 'anon group { ref-cycles, cycles }' # Event count (approx.): 6876107743 # # Overhead Command Shared Object Symbol # ................ ....... ................. .......................... 99.84% 99.76% noploop noploop [.] main 0.07% 0.00% noploop ld-2.15.so [.] strcmp 0.03% 0.00% noploop [kernel.kallsyms] [k] timerqueue_del 0.03% 0.03% noploop [kernel.kallsyms] [k] sched_clock_cpu 0.02% 0.00% noploop [kernel.kallsyms] [k] account_user_time 0.01% 0.00% noploop [kernel.kallsyms] [k] __alloc_pages_nodemask 0.00% 0.00% noploop [kernel.kallsyms] [k] native_write_msr_safe 0.00% 0.11% noploop [kernel.kallsyms] [k] _raw_spin_lock 0.00% 0.06% noploop [kernel.kallsyms] [k] find_get_page 0.00% 0.02% noploop [kernel.kallsyms] [k] rcu_check_callbacks 0.00% 0.02% noploop [kernel.kallsyms] [k] __current_kernel_time As you can see the Overhead column now contains both of ref-cycles and cycles and header line shows group information also - 'anon group { ref-cycles, cycles }'. The output is sorted by period of group leader first. If perf.data file doesn't contain group information, this --group option does nothing. So if you want enable event group view by default you can set it in ~/.perfconfig file: $ cat ~/.perfconfig [report] group = true It can be overridden with command line if you want: $ perf report --no-group Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/Documentation/perf-evlist.txt4
-rw-r--r--tools/perf/Documentation/perf-report.txt3
-rw-r--r--tools/perf/builtin-evlist.c7
-rw-r--r--tools/perf/builtin-record.c3
-rw-r--r--tools/perf/builtin-report.c47
-rw-r--r--tools/perf/builtin-top.c62
-rw-r--r--tools/perf/tests/parse-events.c28
-rw-r--r--tools/perf/ui/browsers/hists.c217
-rw-r--r--tools/perf/ui/gtk/hists.c130
-rw-r--r--tools/perf/ui/hist.c306
-rw-r--r--tools/perf/ui/stdio/hist.c2
-rw-r--r--tools/perf/util/evlist.c7
-rw-r--r--tools/perf/util/evlist.h1
-rw-r--r--tools/perf/util/evsel.c49
-rw-r--r--tools/perf/util/evsel.h16
-rw-r--r--tools/perf/util/header.c164
-rw-r--r--tools/perf/util/header.h2
-rw-r--r--tools/perf/util/hist.c59
-rw-r--r--tools/perf/util/parse-events.c1
-rw-r--r--tools/perf/util/parse-events.h1
-rw-r--r--tools/perf/util/parse-events.y10
-rw-r--r--tools/perf/util/symbol.h3
22 files changed, 873 insertions, 249 deletions
diff --git a/tools/perf/Documentation/perf-evlist.txt b/tools/perf/Documentation/perf-evlist.txt
index 15217345c2fa..1ceb3700ffbb 100644
--- a/tools/perf/Documentation/perf-evlist.txt
+++ b/tools/perf/Documentation/perf-evlist.txt
@@ -28,6 +28,10 @@ OPTIONS
28--verbose=:: 28--verbose=::
29 Show all fields. 29 Show all fields.
30 30
31-g::
32--group::
33 Show event group information.
34
31SEE ALSO 35SEE ALSO
32-------- 36--------
33linkperf:perf-record[1], linkperf:perf-list[1], 37linkperf:perf-record[1], linkperf:perf-list[1],
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 848a0dcb6dfd..02284a0067f0 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -203,6 +203,9 @@ OPTIONS
203--objdump=<path>:: 203--objdump=<path>::
204 Path to objdump binary. 204 Path to objdump binary.
205 205
206--group::
207 Show event group information together.
208
206SEE ALSO 209SEE ALSO
207-------- 210--------
208linkperf:perf-stat[1], linkperf:perf-annotate[1] 211linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index 1312a5e03ec7..85a5e35dd147 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -39,6 +39,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
39 OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), 39 OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
40 OPT_BOOLEAN('v', "verbose", &details.verbose, 40 OPT_BOOLEAN('v', "verbose", &details.verbose,
41 "Show all event attr details"), 41 "Show all event attr details"),
42 OPT_BOOLEAN('g', "group", &symbol_conf.event_group,
43 "Show event group information"),
42 OPT_END() 44 OPT_END()
43 }; 45 };
44 const char * const evlist_usage[] = { 46 const char * const evlist_usage[] = {
@@ -50,5 +52,10 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
50 if (argc) 52 if (argc)
51 usage_with_options(evlist_usage, options); 53 usage_with_options(evlist_usage, options);
52 54
55 if (symbol_conf.event_group && (details.verbose || details.freq)) {
56 pr_err("--group option is not compatible with other options\n");
57 usage_with_options(evlist_usage, options);
58 }
59
53 return __cmd_evlist(input_name, &details); 60 return __cmd_evlist(input_name, &details);
54} 61}
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 2ac690cad411..774c90713a53 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -486,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
486 goto out_delete_session; 486 goto out_delete_session;
487 } 487 }
488 488
489 if (!evsel_list->nr_groups)
490 perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
491
489 /* 492 /*
490 * perf_session__delete(session) will be called at perf_record__exit() 493 * perf_session__delete(session) will be called at perf_record__exit()
491 */ 494 */
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 47a864478543..0d221870561a 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -8,6 +8,7 @@
8#include "builtin.h" 8#include "builtin.h"
9 9
10#include "util/util.h" 10#include "util/util.h"
11#include "util/cache.h"
11 12
12#include "util/annotate.h" 13#include "util/annotate.h"
13#include "util/color.h" 14#include "util/color.h"
@@ -54,6 +55,16 @@ struct perf_report {
54 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); 55 DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
55}; 56};
56 57
58static int perf_report_config(const char *var, const char *value, void *cb)
59{
60 if (!strcmp(var, "report.group")) {
61 symbol_conf.event_group = perf_config_bool(var, value);
62 return 0;
63 }
64
65 return perf_default_config(var, value, cb);
66}
67
57static int perf_report__add_branch_hist_entry(struct perf_tool *tool, 68static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
58 struct addr_location *al, 69 struct addr_location *al,
59 struct perf_sample *sample, 70 struct perf_sample *sample,
@@ -299,6 +310,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
299 char unit; 310 char unit;
300 unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; 311 unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
301 u64 nr_events = self->stats.total_period; 312 u64 nr_events = self->stats.total_period;
313 struct perf_evsel *evsel = hists_to_evsel(self);
314 char buf[512];
315 size_t size = sizeof(buf);
316
317 if (symbol_conf.event_group && evsel->nr_members > 1) {
318 struct perf_evsel *pos;
319
320 perf_evsel__group_desc(evsel, buf, size);
321 evname = buf;
322
323 for_each_group_member(pos, evsel) {
324 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
325 nr_events += pos->hists.stats.total_period;
326 }
327 }
302 328
303 nr_samples = convert_unit(nr_samples, &unit); 329 nr_samples = convert_unit(nr_samples, &unit);
304 ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); 330 ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit);
@@ -319,6 +345,10 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
319 struct hists *hists = &pos->hists; 345 struct hists *hists = &pos->hists;
320 const char *evname = perf_evsel__name(pos); 346 const char *evname = perf_evsel__name(pos);
321 347
348 if (symbol_conf.event_group &&
349 !perf_evsel__is_group_leader(pos))
350 continue;
351
322 hists__fprintf_nr_sample_events(hists, evname, stdout); 352 hists__fprintf_nr_sample_events(hists, evname, stdout);
323 hists__fprintf(hists, true, 0, 0, stdout); 353 hists__fprintf(hists, true, 0, 0, stdout);
324 fprintf(stdout, "\n\n"); 354 fprintf(stdout, "\n\n");
@@ -416,8 +446,16 @@ static int __cmd_report(struct perf_report *rep)
416 hists->symbol_filter_str = rep->symbol_filter_str; 446 hists->symbol_filter_str = rep->symbol_filter_str;
417 447
418 hists__collapse_resort(hists); 448 hists__collapse_resort(hists);
419 hists__output_resort(hists);
420 nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; 449 nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
450
451 /* Non-group events are considered as leader */
452 if (symbol_conf.event_group &&
453 !perf_evsel__is_group_leader(pos)) {
454 struct hists *leader_hists = &pos->leader->hists;
455
456 hists__match(leader_hists, hists);
457 hists__link(leader_hists, hists);
458 }
421 } 459 }
422 460
423 if (nr_samples == 0) { 461 if (nr_samples == 0) {
@@ -425,6 +463,9 @@ static int __cmd_report(struct perf_report *rep)
425 goto out_delete; 463 goto out_delete;
426 } 464 }
427 465
466 list_for_each_entry(pos, &session->evlist->entries, node)
467 hists__output_resort(&pos->hists);
468
428 if (use_browser > 0) { 469 if (use_browser > 0) {
429 if (use_browser == 1) { 470 if (use_browser == 1) {
430 perf_evlist__tui_browse_hists(session->evlist, help, 471 perf_evlist__tui_browse_hists(session->evlist, help,
@@ -638,6 +679,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
638 "Specify disassembler style (e.g. -M intel for intel syntax)"), 679 "Specify disassembler style (e.g. -M intel for intel syntax)"),
639 OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, 680 OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
640 "Show a column with the sum of periods"), 681 "Show a column with the sum of periods"),
682 OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
683 "Show event group information together"),
641 OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "", 684 OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
642 "use branch records for histogram filling", parse_branch_mode), 685 "use branch records for histogram filling", parse_branch_mode),
643 OPT_STRING(0, "objdump", &objdump_path, "path", 686 OPT_STRING(0, "objdump", &objdump_path, "path",
@@ -645,6 +688,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
645 OPT_END() 688 OPT_END()
646 }; 689 };
647 690
691 perf_config(perf_report_config, NULL);
692
648 argc = parse_options(argc, argv, options, report_usage, 0); 693 argc = parse_options(argc, argv, options, report_usage, 0);
649 694
650 if (report.use_stdio) 695 if (report.use_stdio)
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 7978c8117b7f..f561757b1bfa 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -68,6 +68,8 @@
68#include <linux/unistd.h> 68#include <linux/unistd.h>
69#include <linux/types.h> 69#include <linux/types.h>
70 70
71static volatile int done;
72
71static void perf_top__update_print_entries(struct perf_top *top) 73static void perf_top__update_print_entries(struct perf_top *top)
72{ 74{
73 if (top->print_entries > 9) 75 if (top->print_entries > 9)
@@ -431,8 +433,10 @@ static int perf_top__key_mapped(struct perf_top *top, int c)
431 return 0; 433 return 0;
432} 434}
433 435
434static void perf_top__handle_keypress(struct perf_top *top, int c) 436static bool perf_top__handle_keypress(struct perf_top *top, int c)
435{ 437{
438 bool ret = true;
439
436 if (!perf_top__key_mapped(top, c)) { 440 if (!perf_top__key_mapped(top, c)) {
437 struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; 441 struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
438 struct termios tc, save; 442 struct termios tc, save;
@@ -453,7 +457,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
453 457
454 tcsetattr(0, TCSAFLUSH, &save); 458 tcsetattr(0, TCSAFLUSH, &save);
455 if (!perf_top__key_mapped(top, c)) 459 if (!perf_top__key_mapped(top, c))
456 return; 460 return ret;
457 } 461 }
458 462
459 switch (c) { 463 switch (c) {
@@ -515,7 +519,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
515 printf("exiting.\n"); 519 printf("exiting.\n");
516 if (top->dump_symtab) 520 if (top->dump_symtab)
517 perf_session__fprintf_dsos(top->session, stderr); 521 perf_session__fprintf_dsos(top->session, stderr);
518 exit(0); 522 ret = false;
523 break;
519 case 's': 524 case 's':
520 perf_top__prompt_symbol(top, "Enter details symbol"); 525 perf_top__prompt_symbol(top, "Enter details symbol");
521 break; 526 break;
@@ -538,6 +543,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
538 default: 543 default:
539 break; 544 break;
540 } 545 }
546
547 return ret;
541} 548}
542 549
543static void perf_top__sort_new_samples(void *arg) 550static void perf_top__sort_new_samples(void *arg)
@@ -579,8 +586,7 @@ static void *display_thread_tui(void *arg)
579 perf_evlist__tui_browse_hists(top->evlist, help, &hbt, 586 perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
580 &top->session->header.env); 587 &top->session->header.env);
581 588
582 exit_browser(0); 589 done = 1;
583 exit(0);
584 return NULL; 590 return NULL;
585} 591}
586 592
@@ -604,7 +610,7 @@ repeat:
604 /* trash return*/ 610 /* trash return*/
605 getc(stdin); 611 getc(stdin);
606 612
607 while (1) { 613 while (!done) {
608 perf_top__print_sym_table(top); 614 perf_top__print_sym_table(top);
609 /* 615 /*
610 * Either timeout expired or we got an EINTR due to SIGWINCH, 616 * Either timeout expired or we got an EINTR due to SIGWINCH,
@@ -618,15 +624,14 @@ repeat:
618 continue; 624 continue;
619 /* Fall trhu */ 625 /* Fall trhu */
620 default: 626 default:
621 goto process_hotkey; 627 c = getc(stdin);
628 tcsetattr(0, TCSAFLUSH, &save);
629
630 if (perf_top__handle_keypress(top, c))
631 goto repeat;
632 done = 1;
622 } 633 }
623 } 634 }
624process_hotkey:
625 c = getc(stdin);
626 tcsetattr(0, TCSAFLUSH, &save);
627
628 perf_top__handle_keypress(top, c);
629 goto repeat;
630 635
631 return NULL; 636 return NULL;
632} 637}
@@ -705,7 +710,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
705 } 710 }
706 711
707 if (!machine) { 712 if (!machine) {
708 pr_err("%u unprocessable samples recorded.\n", 713 pr_err("%u unprocessable samples recorded.\r",
709 top->session->stats.nr_unprocessable_samples++); 714 top->session->stats.nr_unprocessable_samples++);
710 return; 715 return;
711 } 716 }
@@ -868,7 +873,7 @@ static void perf_top__mmap_read(struct perf_top *top)
868 perf_top__mmap_read_idx(top, i); 873 perf_top__mmap_read_idx(top, i);
869} 874}
870 875
871static void perf_top__start_counters(struct perf_top *top) 876static int perf_top__start_counters(struct perf_top *top)
872{ 877{
873 char msg[512]; 878 char msg[512];
874 struct perf_evsel *counter; 879 struct perf_evsel *counter;
@@ -900,11 +905,10 @@ try_again:
900 goto out_err; 905 goto out_err;
901 } 906 }
902 907
903 return; 908 return 0;
904 909
905out_err: 910out_err:
906 exit_browser(0); 911 return -1;
907 exit(0);
908} 912}
909 913
910static int perf_top__setup_sample_type(struct perf_top *top) 914static int perf_top__setup_sample_type(struct perf_top *top)
@@ -948,7 +952,11 @@ static int __cmd_top(struct perf_top *top)
948 else 952 else
949 perf_event__synthesize_threads(&top->tool, perf_event__process, 953 perf_event__synthesize_threads(&top->tool, perf_event__process,
950 &top->session->machines.host); 954 &top->session->machines.host);
951 perf_top__start_counters(top); 955
956 ret = perf_top__start_counters(top);
957 if (ret)
958 goto out_delete;
959
952 top->session->evlist = top->evlist; 960 top->session->evlist = top->evlist;
953 perf_session__set_id_hdr_size(top->session); 961 perf_session__set_id_hdr_size(top->session);
954 962
@@ -968,10 +976,11 @@ static int __cmd_top(struct perf_top *top)
968 976
969 perf_top__mmap_read(top); 977 perf_top__mmap_read(top);
970 978
979 ret = -1;
971 if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : 980 if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :
972 display_thread), top)) { 981 display_thread), top)) {
973 ui__error("Could not create display thread.\n"); 982 ui__error("Could not create display thread.\n");
974 exit(-1); 983 goto out_delete;
975 } 984 }
976 985
977 if (top->realtime_prio) { 986 if (top->realtime_prio) {
@@ -980,11 +989,11 @@ static int __cmd_top(struct perf_top *top)
980 param.sched_priority = top->realtime_prio; 989 param.sched_priority = top->realtime_prio;
981 if (sched_setscheduler(0, SCHED_FIFO, &param)) { 990 if (sched_setscheduler(0, SCHED_FIFO, &param)) {
982 ui__error("Could not set realtime priority.\n"); 991 ui__error("Could not set realtime priority.\n");
983 exit(-1); 992 goto out_delete;
984 } 993 }
985 } 994 }
986 995
987 while (1) { 996 while (!done) {
988 u64 hits = top->samples; 997 u64 hits = top->samples;
989 998
990 perf_top__mmap_read(top); 999 perf_top__mmap_read(top);
@@ -993,11 +1002,12 @@ static int __cmd_top(struct perf_top *top)
993 ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); 1002 ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
994 } 1003 }
995 1004
1005 ret = 0;
996out_delete: 1006out_delete:
997 perf_session__delete(top->session); 1007 perf_session__delete(top->session);
998 top->session = NULL; 1008 top->session = NULL;
999 1009
1000 return 0; 1010 return ret;
1001} 1011}
1002 1012
1003static int 1013static int
@@ -1154,7 +1164,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1154 if (!top.evlist->nr_entries && 1164 if (!top.evlist->nr_entries &&
1155 perf_evlist__add_default(top.evlist) < 0) { 1165 perf_evlist__add_default(top.evlist) < 0) {
1156 ui__error("Not enough memory for event selector list\n"); 1166 ui__error("Not enough memory for event selector list\n");
1157 return -ENOMEM; 1167 goto out_delete_maps;
1158 } 1168 }
1159 1169
1160 symbol_conf.nr_events = top.evlist->nr_entries; 1170 symbol_conf.nr_events = top.evlist->nr_entries;
@@ -1177,7 +1187,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1177 } else { 1187 } else {
1178 ui__error("frequency and count are zero, aborting\n"); 1188 ui__error("frequency and count are zero, aborting\n");
1179 status = -EINVAL; 1189 status = -EINVAL;
1180 goto out_delete_evlist; 1190 goto out_delete_maps;
1181 } 1191 }
1182 1192
1183 top.sym_evsel = perf_evlist__first(top.evlist); 1193 top.sym_evsel = perf_evlist__first(top.evlist);
@@ -1210,6 +1220,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
1210 1220
1211 status = __cmd_top(&top); 1221 status = __cmd_top(&top);
1212 1222
1223out_delete_maps:
1224 perf_evlist__delete_maps(top.evlist);
1213out_delete_evlist: 1225out_delete_evlist:
1214 perf_evlist__delete(top.evlist); 1226 perf_evlist__delete(top.evlist);
1215 1227
diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c
index 20acaff295d2..80a8daf54a63 100644
--- a/tools/perf/tests/parse-events.c
+++ b/tools/perf/tests/parse-events.c
@@ -23,6 +23,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist)
23 struct perf_evsel *evsel = perf_evlist__first(evlist); 23 struct perf_evsel *evsel = perf_evlist__first(evlist);
24 24
25 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); 25 TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
26 TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
26 TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); 27 TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
27 TEST_ASSERT_VAL("wrong sample_type", 28 TEST_ASSERT_VAL("wrong sample_type",
28 PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); 29 PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
@@ -35,6 +36,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
35 struct perf_evsel *evsel; 36 struct perf_evsel *evsel;
36 37
37 TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); 38 TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
39 TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
38 40
39 list_for_each_entry(evsel, &evlist->entries, node) { 41 list_for_each_entry(evsel, &evlist->entries, node) {
40 TEST_ASSERT_VAL("wrong type", 42 TEST_ASSERT_VAL("wrong type",
@@ -510,6 +512,7 @@ static int test__group1(struct perf_evlist *evlist)
510 struct perf_evsel *evsel, *leader; 512 struct perf_evsel *evsel, *leader;
511 513
512 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); 514 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
515 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
513 516
514 /* instructions:k */ 517 /* instructions:k */
515 evsel = leader = perf_evlist__first(evlist); 518 evsel = leader = perf_evlist__first(evlist);
@@ -523,6 +526,8 @@ static int test__group1(struct perf_evlist *evlist)
523 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); 526 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
524 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 527 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
525 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 528 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
529 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
530 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
526 531
527 /* cycles:upp */ 532 /* cycles:upp */
528 evsel = perf_evsel__next(evsel); 533 evsel = perf_evsel__next(evsel);
@@ -537,6 +542,7 @@ static int test__group1(struct perf_evlist *evlist)
537 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); 542 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
538 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); 543 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
539 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 544 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
545 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
540 546
541 return 0; 547 return 0;
542} 548}
@@ -546,6 +552,7 @@ static int test__group2(struct perf_evlist *evlist)
546 struct perf_evsel *evsel, *leader; 552 struct perf_evsel *evsel, *leader;
547 553
548 TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); 554 TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
555 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
549 556
550 /* faults + :ku modifier */ 557 /* faults + :ku modifier */
551 evsel = leader = perf_evlist__first(evlist); 558 evsel = leader = perf_evlist__first(evlist);
@@ -559,6 +566,8 @@ static int test__group2(struct perf_evlist *evlist)
559 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); 566 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
560 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 567 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
561 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 568 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
569 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
570 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
562 571
563 /* cache-references + :u modifier */ 572 /* cache-references + :u modifier */
564 evsel = perf_evsel__next(evsel); 573 evsel = perf_evsel__next(evsel);
@@ -572,6 +581,7 @@ static int test__group2(struct perf_evlist *evlist)
572 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); 581 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
573 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 582 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
574 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 583 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
584 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
575 585
576 /* cycles:k */ 586 /* cycles:k */
577 evsel = perf_evsel__next(evsel); 587 evsel = perf_evsel__next(evsel);
@@ -594,6 +604,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
594 struct perf_evsel *evsel, *leader; 604 struct perf_evsel *evsel, *leader;
595 605
596 TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); 606 TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
607 TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
597 608
598 /* group1 syscalls:sys_enter_open:H */ 609 /* group1 syscalls:sys_enter_open:H */
599 evsel = leader = perf_evlist__first(evlist); 610 evsel = leader = perf_evlist__first(evlist);
@@ -610,6 +621,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
610 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 621 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
611 TEST_ASSERT_VAL("wrong group name", 622 TEST_ASSERT_VAL("wrong group name",
612 !strcmp(leader->group_name, "group1")); 623 !strcmp(leader->group_name, "group1"));
624 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
625 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
613 626
614 /* group1 cycles:kppp */ 627 /* group1 cycles:kppp */
615 evsel = perf_evsel__next(evsel); 628 evsel = perf_evsel__next(evsel);
@@ -625,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
625 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); 638 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3);
626 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 639 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
627 TEST_ASSERT_VAL("wrong group name", !evsel->group_name); 640 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
641 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
628 642
629 /* group2 cycles + G modifier */ 643 /* group2 cycles + G modifier */
630 evsel = leader = perf_evsel__next(evsel); 644 evsel = leader = perf_evsel__next(evsel);
@@ -640,6 +654,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
640 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 654 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
641 TEST_ASSERT_VAL("wrong group name", 655 TEST_ASSERT_VAL("wrong group name",
642 !strcmp(leader->group_name, "group2")); 656 !strcmp(leader->group_name, "group2"));
657 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
658 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
643 659
644 /* group2 1:3 + G modifier */ 660 /* group2 1:3 + G modifier */
645 evsel = perf_evsel__next(evsel); 661 evsel = perf_evsel__next(evsel);
@@ -652,6 +668,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
652 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); 668 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
653 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 669 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
654 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 670 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
671 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
655 672
656 /* instructions:u */ 673 /* instructions:u */
657 evsel = perf_evsel__next(evsel); 674 evsel = perf_evsel__next(evsel);
@@ -674,6 +691,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
674 struct perf_evsel *evsel, *leader; 691 struct perf_evsel *evsel, *leader;
675 692
676 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); 693 TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
694 TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
677 695
678 /* cycles:u + p */ 696 /* cycles:u + p */
679 evsel = leader = perf_evlist__first(evlist); 697 evsel = leader = perf_evlist__first(evlist);
@@ -689,6 +707,8 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
689 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); 707 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1);
690 TEST_ASSERT_VAL("wrong group name", !evsel->group_name); 708 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
691 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 709 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
710 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
711 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
692 712
693 /* instructions:kp + p */ 713 /* instructions:kp + p */
694 evsel = perf_evsel__next(evsel); 714 evsel = perf_evsel__next(evsel);
@@ -703,6 +723,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
703 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); 723 TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
704 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); 724 TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
705 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 725 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
726 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
706 727
707 return 0; 728 return 0;
708} 729}
@@ -712,6 +733,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
712 struct perf_evsel *evsel, *leader; 733 struct perf_evsel *evsel, *leader;
713 734
714 TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); 735 TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
736 TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
715 737
716 /* cycles + G */ 738 /* cycles + G */
717 evsel = leader = perf_evlist__first(evlist); 739 evsel = leader = perf_evlist__first(evlist);
@@ -726,6 +748,8 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
726 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 748 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
727 TEST_ASSERT_VAL("wrong group name", !evsel->group_name); 749 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
728 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 750 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
751 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
752 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
729 753
730 /* instructions + G */ 754 /* instructions + G */
731 evsel = perf_evsel__next(evsel); 755 evsel = perf_evsel__next(evsel);
@@ -739,6 +763,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
739 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); 763 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
740 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 764 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
741 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 765 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
766 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
742 767
743 /* cycles:G */ 768 /* cycles:G */
744 evsel = leader = perf_evsel__next(evsel); 769 evsel = leader = perf_evsel__next(evsel);
@@ -753,6 +778,8 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
753 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 778 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
754 TEST_ASSERT_VAL("wrong group name", !evsel->group_name); 779 TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
755 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); 780 TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
781 TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
782 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
756 783
757 /* instructions:G */ 784 /* instructions:G */
758 evsel = perf_evsel__next(evsel); 785 evsel = perf_evsel__next(evsel);
@@ -766,6 +793,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
766 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); 793 TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
767 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); 794 TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
768 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); 795 TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
796 TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
769 797
770 /* cycles */ 798 /* cycles */
771 evsel = perf_evsel__next(evsel); 799 evsel = perf_evsel__next(evsel);
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 57b82c26cd05..20ccd57753f7 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -567,23 +567,123 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
567 return row - first_row; 567 return row - first_row;
568} 568}
569 569
570#define HPP__COLOR_FN(_name, _field) \ 570struct hpp_arg {
571static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \ 571 struct ui_browser *b;
572 struct hist_entry *he) \ 572 char folded_sign;
573 bool current_entry;
574};
575
576static int __hpp__color_callchain(struct hpp_arg *arg)
577{
578 if (!symbol_conf.use_callchain)
579 return 0;
580
581 slsmg_printf("%c ", arg->folded_sign);
582 return 2;
583}
584
585static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
586 u64 (*get_field)(struct hist_entry *),
587 int (*callchain_cb)(struct hpp_arg *))
588{
589 int ret = 0;
590 double percent = 0.0;
591 struct hists *hists = he->hists;
592 struct hpp_arg *arg = hpp->ptr;
593
594 if (hists->stats.total_period)
595 percent = 100.0 * get_field(he) / hists->stats.total_period;
596
597 ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
598
599 if (callchain_cb)
600 ret += callchain_cb(arg);
601
602 ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
603 slsmg_printf("%s", hpp->buf);
604
605 if (symbol_conf.event_group) {
606 int prev_idx, idx_delta;
607 struct perf_evsel *evsel = hists_to_evsel(hists);
608 struct hist_entry *pair;
609 int nr_members = evsel->nr_members;
610
611 if (nr_members <= 1)
612 goto out;
613
614 prev_idx = perf_evsel__group_idx(evsel);
615
616 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
617 u64 period = get_field(pair);
618 u64 total = pair->hists->stats.total_period;
619
620 if (!total)
621 continue;
622
623 evsel = hists_to_evsel(pair->hists);
624 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
625
626 while (idx_delta--) {
627 /*
628 * zero-fill group members in the middle which
629 * have no sample
630 */
631 ui_browser__set_percent_color(arg->b, 0.0,
632 arg->current_entry);
633 ret += scnprintf(hpp->buf, hpp->size,
634 " %6.2f%%", 0.0);
635 slsmg_printf("%s", hpp->buf);
636 }
637
638 percent = 100.0 * period / total;
639 ui_browser__set_percent_color(arg->b, percent,
640 arg->current_entry);
641 ret += scnprintf(hpp->buf, hpp->size,
642 " %6.2f%%", percent);
643 slsmg_printf("%s", hpp->buf);
644
645 prev_idx = perf_evsel__group_idx(evsel);
646 }
647
648 idx_delta = nr_members - prev_idx - 1;
649
650 while (idx_delta--) {
651 /*
652 * zero-fill group members at last which have no sample
653 */
654 ui_browser__set_percent_color(arg->b, 0.0,
655 arg->current_entry);
656 ret += scnprintf(hpp->buf, hpp->size,
657 " %6.2f%%", 0.0);
658 slsmg_printf("%s", hpp->buf);
659 }
660 }
661out:
662 if (!arg->current_entry || !arg->b->navkeypressed)
663 ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
664
665 return ret;
666}
667
668#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
669static u64 __hpp_get_##_field(struct hist_entry *he) \
573{ \ 670{ \
574 struct hists *hists = he->hists; \ 671 return he->stat._field; \
575 double percent = 100.0 * he->stat._field / hists->stats.total_period; \ 672} \
576 *(double *)hpp->ptr = percent; \ 673 \
577 return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \ 674static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
675 struct hist_entry *he) \
676{ \
677 return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
578} 678}
579 679
580HPP__COLOR_FN(overhead, period) 680__HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
581HPP__COLOR_FN(overhead_sys, period_sys) 681__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
582HPP__COLOR_FN(overhead_us, period_us) 682__HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
583HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) 683__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
584HPP__COLOR_FN(overhead_guest_us, period_guest_us) 684__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
585 685
586#undef HPP__COLOR_FN 686#undef __HPP_COLOR_PERCENT_FN
587 687
588void hist_browser__init_hpp(void) 688void hist_browser__init_hpp(void)
589{ 689{
@@ -608,7 +708,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
608 unsigned short row) 708 unsigned short row)
609{ 709{
610 char s[256]; 710 char s[256];
611 double percent;
612 int printed = 0; 711 int printed = 0;
613 int width = browser->b.width; 712 int width = browser->b.width;
614 char folded_sign = ' '; 713 char folded_sign = ' ';
@@ -628,16 +727,20 @@ static int hist_browser__show_entry(struct hist_browser *browser,
628 } 727 }
629 728
630 if (row_offset == 0) { 729 if (row_offset == 0) {
730 struct hpp_arg arg = {
731 .b = &browser->b,
732 .folded_sign = folded_sign,
733 .current_entry = current_entry,
734 };
631 struct perf_hpp hpp = { 735 struct perf_hpp hpp = {
632 .buf = s, 736 .buf = s,
633 .size = sizeof(s), 737 .size = sizeof(s),
738 .ptr = &arg,
634 }; 739 };
635 int i = 0;
636 740
637 ui_browser__gotorc(&browser->b, row, 0); 741 ui_browser__gotorc(&browser->b, row, 0);
638 742
639 perf_hpp__for_each_format(fmt) { 743 perf_hpp__for_each_format(fmt) {
640
641 if (!first) { 744 if (!first) {
642 slsmg_printf(" "); 745 slsmg_printf(" ");
643 width -= 2; 746 width -= 2;
@@ -645,27 +748,11 @@ static int hist_browser__show_entry(struct hist_browser *browser,
645 first = false; 748 first = false;
646 749
647 if (fmt->color) { 750 if (fmt->color) {
648 hpp.ptr = &percent;
649 /* It will set percent for us. See HPP__COLOR_FN above. */
650 width -= fmt->color(&hpp, entry); 751 width -= fmt->color(&hpp, entry);
651
652 ui_browser__set_percent_color(&browser->b, percent, current_entry);
653
654 if (!i && symbol_conf.use_callchain) {
655 slsmg_printf("%c ", folded_sign);
656 width -= 2;
657 }
658
659 slsmg_printf("%s", s);
660
661 if (!current_entry || !browser->b.navkeypressed)
662 ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
663 } else { 752 } else {
664 width -= fmt->entry(&hpp, entry); 753 width -= fmt->entry(&hpp, entry);
665 slsmg_printf("%s", s); 754 slsmg_printf("%s", s);
666 } 755 }
667
668 i++;
669 } 756 }
670 757
671 /* The scroll bar isn't being used */ 758 /* The scroll bar isn't being used */
@@ -1102,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
1102 const struct thread *thread = hists->thread_filter; 1189 const struct thread *thread = hists->thread_filter;
1103 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; 1190 unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
1104 u64 nr_events = hists->stats.total_period; 1191 u64 nr_events = hists->stats.total_period;
1192 struct perf_evsel *evsel = hists_to_evsel(hists);
1193 char buf[512];
1194 size_t buflen = sizeof(buf);
1195
1196 if (symbol_conf.event_group && evsel->nr_members > 1) {
1197 struct perf_evsel *pos;
1198
1199 perf_evsel__group_desc(evsel, buf, buflen);
1200 ev_name = buf;
1201
1202 for_each_group_member(pos, evsel) {
1203 nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1204 nr_events += pos->hists.stats.total_period;
1205 }
1206 }
1105 1207
1106 nr_samples = convert_unit(nr_samples, &unit); 1208 nr_samples = convert_unit(nr_samples, &unit);
1107 printed = scnprintf(bf, size, 1209 printed = scnprintf(bf, size,
@@ -1498,6 +1600,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
1498 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : 1600 ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
1499 HE_COLORSET_NORMAL); 1601 HE_COLORSET_NORMAL);
1500 1602
1603 if (symbol_conf.event_group && evsel->nr_members > 1) {
1604 struct perf_evsel *pos;
1605
1606 ev_name = perf_evsel__group_name(evsel);
1607
1608 for_each_group_member(pos, evsel) {
1609 nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
1610 }
1611 }
1612
1501 nr_events = convert_unit(nr_events, &unit); 1613 nr_events = convert_unit(nr_events, &unit);
1502 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, 1614 printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
1503 unit, unit == ' ' ? "" : " ", ev_name); 1615 unit, unit == ' ' ? "" : " ", ev_name);
@@ -1608,8 +1720,19 @@ out:
1608 return key; 1720 return key;
1609} 1721}
1610 1722
1723static bool filter_group_entries(struct ui_browser *self __maybe_unused,
1724 void *entry)
1725{
1726 struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
1727
1728 if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
1729 return true;
1730
1731 return false;
1732}
1733
1611static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, 1734static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1612 const char *help, 1735 int nr_entries, const char *help,
1613 struct hist_browser_timer *hbt, 1736 struct hist_browser_timer *hbt,
1614 struct perf_session_env *env) 1737 struct perf_session_env *env)
1615{ 1738{
@@ -1620,7 +1743,8 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1620 .refresh = ui_browser__list_head_refresh, 1743 .refresh = ui_browser__list_head_refresh,
1621 .seek = ui_browser__list_head_seek, 1744 .seek = ui_browser__list_head_seek,
1622 .write = perf_evsel_menu__write, 1745 .write = perf_evsel_menu__write,
1623 .nr_entries = evlist->nr_entries, 1746 .filter = filter_group_entries,
1747 .nr_entries = nr_entries,
1624 .priv = evlist, 1748 .priv = evlist,
1625 }, 1749 },
1626 .env = env, 1750 .env = env,
@@ -1636,20 +1760,37 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
1636 menu.b.width = line_len; 1760 menu.b.width = line_len;
1637 } 1761 }
1638 1762
1639 return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt); 1763 return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
1640} 1764}
1641 1765
1642int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, 1766int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
1643 struct hist_browser_timer *hbt, 1767 struct hist_browser_timer *hbt,
1644 struct perf_session_env *env) 1768 struct perf_session_env *env)
1645{ 1769{
1646 if (evlist->nr_entries == 1) { 1770 int nr_entries = evlist->nr_entries;
1771
1772single_entry:
1773 if (nr_entries == 1) {
1647 struct perf_evsel *first = list_entry(evlist->entries.next, 1774 struct perf_evsel *first = list_entry(evlist->entries.next,
1648 struct perf_evsel, node); 1775 struct perf_evsel, node);
1649 const char *ev_name = perf_evsel__name(first); 1776 const char *ev_name = perf_evsel__name(first);
1650 return perf_evsel__hists_browse(first, evlist->nr_entries, help, 1777
1778 return perf_evsel__hists_browse(first, nr_entries, help,
1651 ev_name, false, hbt, env); 1779 ev_name, false, hbt, env);
1652 } 1780 }
1653 1781
1654 return __perf_evlist__tui_browse_hists(evlist, help, hbt, env); 1782 if (symbol_conf.event_group) {
1783 struct perf_evsel *pos;
1784
1785 nr_entries = 0;
1786 list_for_each_entry(pos, &evlist->entries, node)
1787 if (perf_evsel__is_group_leader(pos))
1788 nr_entries++;
1789
1790 if (nr_entries == 1)
1791 goto single_entry;
1792 }
1793
1794 return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
1795 hbt, env);
1655} 1796}
diff --git a/tools/perf/ui/gtk/hists.c b/tools/perf/ui/gtk/hists.c
index c03da79d524f..1e764a8ad259 100644
--- a/tools/perf/ui/gtk/hists.c
+++ b/tools/perf/ui/gtk/hists.c
@@ -8,32 +8,105 @@
8 8
9#define MAX_COLUMNS 32 9#define MAX_COLUMNS 32
10 10
11#define HPP__COLOR_FN(_name, _field) \ 11static int __percent_color_snprintf(char *buf, size_t size, double percent)
12static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \ 12{
13 struct hist_entry *he) \ 13 int ret = 0;
14 const char *markup;
15
16 markup = perf_gtk__get_percent_color(percent);
17 if (markup)
18 ret += scnprintf(buf, size, markup);
19
20 ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent);
21
22 if (markup)
23 ret += scnprintf(buf + ret, size - ret, "</span>");
24
25 return ret;
26}
27
28
29static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
30 u64 (*get_field)(struct hist_entry *))
31{
32 int ret;
33 double percent = 0.0;
34 struct hists *hists = he->hists;
35
36 if (hists->stats.total_period)
37 percent = 100.0 * get_field(he) / hists->stats.total_period;
38
39 ret = __percent_color_snprintf(hpp->buf, hpp->size, percent);
40
41 if (symbol_conf.event_group) {
42 int prev_idx, idx_delta;
43 struct perf_evsel *evsel = hists_to_evsel(hists);
44 struct hist_entry *pair;
45 int nr_members = evsel->nr_members;
46
47 if (nr_members <= 1)
48 return ret;
49
50 prev_idx = perf_evsel__group_idx(evsel);
51
52 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
53 u64 period = get_field(pair);
54 u64 total = pair->hists->stats.total_period;
55
56 evsel = hists_to_evsel(pair->hists);
57 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
58
59 while (idx_delta--) {
60 /*
61 * zero-fill group members in the middle which
62 * have no sample
63 */
64 ret += __percent_color_snprintf(hpp->buf + ret,
65 hpp->size - ret,
66 0.0);
67 }
68
69 percent = 100.0 * period / total;
70 ret += __percent_color_snprintf(hpp->buf + ret,
71 hpp->size - ret,
72 percent);
73
74 prev_idx = perf_evsel__group_idx(evsel);
75 }
76
77 idx_delta = nr_members - prev_idx - 1;
78
79 while (idx_delta--) {
80 /*
81 * zero-fill group members at last which have no sample
82 */
83 ret += __percent_color_snprintf(hpp->buf + ret,
84 hpp->size - ret,
85 0.0);
86 }
87 }
88 return ret;
89}
90
91#define __HPP_COLOR_PERCENT_FN(_type, _field) \
92static u64 he_get_##_field(struct hist_entry *he) \
14{ \ 93{ \
15 struct hists *hists = he->hists; \ 94 return he->stat._field; \
16 double percent = 100.0 * he->stat._field / hists->stats.total_period; \ 95} \
17 const char *markup; \
18 int ret = 0; \
19 \ 96 \
20 markup = perf_gtk__get_percent_color(percent); \ 97static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \
21 if (markup) \ 98 struct hist_entry *he) \
22 ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \ 99{ \
23 ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \ 100 return __hpp__color_fmt(hpp, he, he_get_##_field); \
24 if (markup) \
25 ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \
26 \
27 return ret; \
28} 101}
29 102
30HPP__COLOR_FN(overhead, period) 103__HPP_COLOR_PERCENT_FN(overhead, period)
31HPP__COLOR_FN(overhead_sys, period_sys) 104__HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
32HPP__COLOR_FN(overhead_us, period_us) 105__HPP_COLOR_PERCENT_FN(overhead_us, period_us)
33HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) 106__HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
34HPP__COLOR_FN(overhead_guest_us, period_guest_us) 107__HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
35 108
36#undef HPP__COLOR_FN 109#undef __HPP_COLOR_PERCENT_FN
37 110
38 111
39void perf_gtk__init_hpp(void) 112void perf_gtk__init_hpp(void)
@@ -70,6 +143,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
70 struct perf_hpp hpp = { 143 struct perf_hpp hpp = {
71 .buf = s, 144 .buf = s,
72 .size = sizeof(s), 145 .size = sizeof(s),
146 .ptr = hists_to_evsel(hists),
73 }; 147 };
74 148
75 nr_cols = 0; 149 nr_cols = 0;
@@ -96,7 +170,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
96 fmt->header(&hpp); 170 fmt->header(&hpp);
97 171
98 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), 172 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
99 -1, s, 173 -1, ltrim(s),
100 renderer, "markup", 174 renderer, "markup",
101 col_idx++, NULL); 175 col_idx++, NULL);
102 } 176 }
@@ -196,6 +270,18 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
196 const char *evname = perf_evsel__name(pos); 270 const char *evname = perf_evsel__name(pos);
197 GtkWidget *scrolled_window; 271 GtkWidget *scrolled_window;
198 GtkWidget *tab_label; 272 GtkWidget *tab_label;
273 char buf[512];
274 size_t size = sizeof(buf);
275
276 if (symbol_conf.event_group) {
277 if (!perf_evsel__is_group_leader(pos))
278 continue;
279
280 if (pos->nr_members > 1) {
281 perf_evsel__group_desc(pos, buf, size);
282 evname = buf;
283 }
284 }
199 285
200 scrolled_window = gtk_scrolled_window_new(NULL, NULL); 286 scrolled_window = gtk_scrolled_window_new(NULL, NULL);
201 287
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
index 1889c12ca81f..a47ce98c2cb1 100644
--- a/tools/perf/ui/hist.c
+++ b/tools/perf/ui/hist.c
@@ -3,151 +3,164 @@
3#include "../util/hist.h" 3#include "../util/hist.h"
4#include "../util/util.h" 4#include "../util/util.h"
5#include "../util/sort.h" 5#include "../util/sort.h"
6 6#include "../util/evsel.h"
7 7
8/* hist period print (hpp) functions */ 8/* hist period print (hpp) functions */
9static int hpp__header_overhead(struct perf_hpp *hpp)
10{
11 return scnprintf(hpp->buf, hpp->size, "Overhead");
12}
13
14static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused)
15{
16 return 8;
17}
18
19static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he)
20{
21 struct hists *hists = he->hists;
22 double percent = 100.0 * he->stat.period / hists->stats.total_period;
23
24 return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
25}
26
27static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he)
28{
29 struct hists *hists = he->hists;
30 double percent = 100.0 * he->stat.period / hists->stats.total_period;
31 const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
32 9
33 return scnprintf(hpp->buf, hpp->size, fmt, percent); 10typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...);
34}
35
36static int hpp__header_overhead_sys(struct perf_hpp *hpp)
37{
38 const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
39
40 return scnprintf(hpp->buf, hpp->size, fmt, "sys");
41}
42
43static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused)
44{
45 return 7;
46}
47 11
48static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) 12static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
13 u64 (*get_field)(struct hist_entry *),
14 const char *fmt, hpp_snprint_fn print_fn)
49{ 15{
16 int ret;
17 double percent = 0.0;
50 struct hists *hists = he->hists; 18 struct hists *hists = he->hists;
51 double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
52 19
53 return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); 20 if (hists->stats.total_period)
54} 21 percent = 100.0 * get_field(he) / hists->stats.total_period;
55 22
56static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) 23 ret = print_fn(hpp->buf, hpp->size, fmt, percent);
57{
58 struct hists *hists = he->hists;
59 double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
60 const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
61 24
62 return scnprintf(hpp->buf, hpp->size, fmt, percent); 25 if (symbol_conf.event_group) {
63} 26 int prev_idx, idx_delta;
27 struct perf_evsel *evsel = hists_to_evsel(hists);
28 struct hist_entry *pair;
29 int nr_members = evsel->nr_members;
64 30
65static int hpp__header_overhead_us(struct perf_hpp *hpp) 31 if (nr_members <= 1)
66{ 32 return ret;
67 const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
68 33
69 return scnprintf(hpp->buf, hpp->size, fmt, "user"); 34 prev_idx = perf_evsel__group_idx(evsel);
70}
71 35
72static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused) 36 list_for_each_entry(pair, &he->pairs.head, pairs.node) {
73{ 37 u64 period = get_field(pair);
74 return 7; 38 u64 total = pair->hists->stats.total_period;
75}
76 39
77static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) 40 if (!total)
78{ 41 continue;
79 struct hists *hists = he->hists;
80 double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
81
82 return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
83}
84
85static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
86{
87 struct hists *hists = he->hists;
88 double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
89 const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
90 42
91 return scnprintf(hpp->buf, hpp->size, fmt, percent); 43 evsel = hists_to_evsel(pair->hists);
92} 44 idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
93
94static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp)
95{
96 return scnprintf(hpp->buf, hpp->size, "guest sys");
97}
98 45
99static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused) 46 while (idx_delta--) {
100{ 47 /*
101 return 9; 48 * zero-fill group members in the middle which
102} 49 * have no sample
50 */
51 ret += print_fn(hpp->buf + ret, hpp->size - ret,
52 fmt, 0.0);
53 }
103 54
104static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp, 55 ret += print_fn(hpp->buf + ret, hpp->size - ret,
105 struct hist_entry *he) 56 fmt, 100.0 * period / total);
106{
107 struct hists *hists = he->hists;
108 double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
109 57
110 return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); 58 prev_idx = perf_evsel__group_idx(evsel);
111} 59 }
112 60
113static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp, 61 idx_delta = nr_members - prev_idx - 1;
114 struct hist_entry *he)
115{
116 struct hists *hists = he->hists;
117 double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
118 const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
119 62
120 return scnprintf(hpp->buf, hpp->size, fmt, percent); 63 while (idx_delta--) {
64 /*
65 * zero-fill group members at last which have no sample
66 */
67 ret += print_fn(hpp->buf + ret, hpp->size - ret,
68 fmt, 0.0);
69 }
70 }
71 return ret;
121} 72}
122 73
123static int hpp__header_overhead_guest_us(struct perf_hpp *hpp) 74static int __hpp__raw_fmt(struct perf_hpp *hpp, struct hist_entry *he,
75 u64 (*get_field)(struct hist_entry *),
76 const char *fmt, hpp_snprint_fn print_fn)
124{ 77{
125 return scnprintf(hpp->buf, hpp->size, "guest usr"); 78 int ret;
126}
127 79
128static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused) 80 ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
129{ 81 return ret;
130 return 9;
131} 82}
132 83
133static int hpp__color_overhead_guest_us(struct perf_hpp *hpp,
134 struct hist_entry *he)
135{
136 struct hists *hists = he->hists;
137 double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
138 84
139 return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); 85#define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
140} 86static int hpp__header_##_type(struct perf_hpp *hpp) \
87{ \
88 int len = _min_width; \
89 \
90 if (symbol_conf.event_group) { \
91 struct perf_evsel *evsel = hpp->ptr; \
92 \
93 len = max(len, evsel->nr_members * _unit_width); \
94 } \
95 return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \
96}
97
98#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
99static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \
100{ \
101 int len = _min_width; \
102 \
103 if (symbol_conf.event_group) { \
104 struct perf_evsel *evsel = hpp->ptr; \
105 \
106 len = max(len, evsel->nr_members * _unit_width); \
107 } \
108 return len; \
109}
110
111#define __HPP_COLOR_PERCENT_FN(_type, _field) \
112static u64 he_get_##_field(struct hist_entry *he) \
113{ \
114 return he->stat._field; \
115} \
116 \
117static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
118{ \
119 return __hpp__percent_fmt(hpp, he, he_get_##_field, " %6.2f%%", \
120 (hpp_snprint_fn)percent_color_snprintf); \
121}
122
123#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
124static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
125{ \
126 const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
127 return __hpp__percent_fmt(hpp, he, he_get_##_field, fmt, \
128 scnprintf); \
129}
130
131#define __HPP_ENTRY_RAW_FN(_type, _field) \
132static u64 he_get_raw_##_field(struct hist_entry *he) \
133{ \
134 return he->stat._field; \
135} \
136 \
137static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
138{ \
139 const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
140 return __hpp__raw_fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf); \
141}
142
143#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
144__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
145__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
146__HPP_COLOR_PERCENT_FN(_type, _field) \
147__HPP_ENTRY_PERCENT_FN(_type, _field)
148
149#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
150__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
151__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
152__HPP_ENTRY_RAW_FN(_type, _field)
153
154
155HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
156HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
157HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
158HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
159HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
160
161HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
162HPP_RAW_FNS(period, "Period", period, 12, 12)
141 163
142static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp,
143 struct hist_entry *he)
144{
145 struct hists *hists = he->hists;
146 double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
147 const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
148
149 return scnprintf(hpp->buf, hpp->size, fmt, percent);
150}
151 164
152static int hpp__header_baseline(struct perf_hpp *hpp) 165static int hpp__header_baseline(struct perf_hpp *hpp)
153{ 166{
@@ -179,7 +192,7 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
179{ 192{
180 double percent = baseline_percent(he); 193 double percent = baseline_percent(he);
181 194
182 if (hist_entry__has_pairs(he)) 195 if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
183 return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); 196 return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
184 else 197 else
185 return scnprintf(hpp->buf, hpp->size, " "); 198 return scnprintf(hpp->buf, hpp->size, " ");
@@ -196,44 +209,6 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
196 return scnprintf(hpp->buf, hpp->size, " "); 209 return scnprintf(hpp->buf, hpp->size, " ");
197} 210}
198 211
199static int hpp__header_samples(struct perf_hpp *hpp)
200{
201 const char *fmt = symbol_conf.field_sep ? "%s" : "%11s";
202
203 return scnprintf(hpp->buf, hpp->size, fmt, "Samples");
204}
205
206static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused)
207{
208 return 11;
209}
210
211static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he)
212{
213 const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64;
214
215 return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events);
216}
217
218static int hpp__header_period(struct perf_hpp *hpp)
219{
220 const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
221
222 return scnprintf(hpp->buf, hpp->size, fmt, "Period");
223}
224
225static int hpp__width_period(struct perf_hpp *hpp __maybe_unused)
226{
227 return 12;
228}
229
230static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he)
231{
232 const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
233
234 return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period);
235}
236
237static int hpp__header_period_baseline(struct perf_hpp *hpp) 212static int hpp__header_period_baseline(struct perf_hpp *hpp)
238{ 213{
239 const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; 214 const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
@@ -254,6 +229,7 @@ static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *h
254 229
255 return scnprintf(hpp->buf, hpp->size, fmt, period); 230 return scnprintf(hpp->buf, hpp->size, fmt, period);
256} 231}
232
257static int hpp__header_delta(struct perf_hpp *hpp) 233static int hpp__header_delta(struct perf_hpp *hpp)
258{ 234{
259 const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; 235 const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
@@ -408,9 +384,20 @@ struct perf_hpp_fmt perf_hpp__format[] = {
408 384
409LIST_HEAD(perf_hpp__list); 385LIST_HEAD(perf_hpp__list);
410 386
387
411#undef HPP__COLOR_PRINT_FNS 388#undef HPP__COLOR_PRINT_FNS
412#undef HPP__PRINT_FNS 389#undef HPP__PRINT_FNS
413 390
391#undef HPP_PERCENT_FNS
392#undef HPP_RAW_FNS
393
394#undef __HPP_HEADER_FN
395#undef __HPP_WIDTH_FN
396#undef __HPP_COLOR_PERCENT_FN
397#undef __HPP_ENTRY_PERCENT_FN
398#undef __HPP_ENTRY_RAW_FN
399
400
414void perf_hpp__init(void) 401void perf_hpp__init(void)
415{ 402{
416 if (symbol_conf.show_cpu_utilization) { 403 if (symbol_conf.show_cpu_utilization) {
@@ -508,12 +495,15 @@ unsigned int hists__sort_list_width(struct hists *hists)
508 struct perf_hpp_fmt *fmt; 495 struct perf_hpp_fmt *fmt;
509 struct sort_entry *se; 496 struct sort_entry *se;
510 int i = 0, ret = 0; 497 int i = 0, ret = 0;
498 struct perf_hpp dummy_hpp = {
499 .ptr = hists_to_evsel(hists),
500 };
511 501
512 perf_hpp__for_each_format(fmt) { 502 perf_hpp__for_each_format(fmt) {
513 if (i) 503 if (i)
514 ret += 2; 504 ret += 2;
515 505
516 ret += fmt->width(NULL); 506 ret += fmt->width(&dummy_hpp);
517 } 507 }
518 508
519 list_for_each_entry(se, &hist_entry__sort_list, list) 509 list_for_each_entry(se, &hist_entry__sort_list, list)
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
index f9798298e3e0..ff1f60cf442e 100644
--- a/tools/perf/ui/stdio/hist.c
+++ b/tools/perf/ui/stdio/hist.c
@@ -3,6 +3,7 @@
3#include "../../util/util.h" 3#include "../../util/util.h"
4#include "../../util/hist.h" 4#include "../../util/hist.h"
5#include "../../util/sort.h" 5#include "../../util/sort.h"
6#include "../../util/evsel.h"
6 7
7 8
8static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) 9static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
@@ -347,6 +348,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
347 struct perf_hpp dummy_hpp = { 348 struct perf_hpp dummy_hpp = {
348 .buf = bf, 349 .buf = bf,
349 .size = sizeof(bf), 350 .size = sizeof(bf),
351 .ptr = hists_to_evsel(hists),
350 }; 352 };
351 bool first = true; 353 bool first = true;
352 354
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index dc8aee97a488..eddd5ebcd690 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -117,6 +117,9 @@ void __perf_evlist__set_leader(struct list_head *list)
117 struct perf_evsel *evsel, *leader; 117 struct perf_evsel *evsel, *leader;
118 118
119 leader = list_entry(list->next, struct perf_evsel, node); 119 leader = list_entry(list->next, struct perf_evsel, node);
120 evsel = list_entry(list->prev, struct perf_evsel, node);
121
122 leader->nr_members = evsel->idx - leader->idx + 1;
120 123
121 list_for_each_entry(evsel, list, node) { 124 list_for_each_entry(evsel, list, node) {
122 if (evsel != leader) 125 if (evsel != leader)
@@ -126,8 +129,10 @@ void __perf_evlist__set_leader(struct list_head *list)
126 129
127void perf_evlist__set_leader(struct perf_evlist *evlist) 130void perf_evlist__set_leader(struct perf_evlist *evlist)
128{ 131{
129 if (evlist->nr_entries) 132 if (evlist->nr_entries) {
133 evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0;
130 __perf_evlist__set_leader(&evlist->entries); 134 __perf_evlist__set_leader(&evlist->entries);
135 }
131} 136}
132 137
133int perf_evlist__add_default(struct perf_evlist *evlist) 138int perf_evlist__add_default(struct perf_evlist *evlist)
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 457e2350d21d..73579a25a93e 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -21,6 +21,7 @@ struct perf_evlist {
21 struct list_head entries; 21 struct list_head entries;
22 struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; 22 struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
23 int nr_entries; 23 int nr_entries;
24 int nr_groups;
24 int nr_fds; 25 int nr_fds;
25 int nr_mmaps; 26 int nr_mmaps;
26 int mmap_len; 27 int mmap_len;
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index baa26ddbcc7b..a54701504606 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -434,6 +434,31 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
434 return evsel->name ?: "unknown"; 434 return evsel->name ?: "unknown";
435} 435}
436 436
437const char *perf_evsel__group_name(struct perf_evsel *evsel)
438{
439 return evsel->group_name ?: "anon group";
440}
441
442int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
443{
444 int ret;
445 struct perf_evsel *pos;
446 const char *group_name = perf_evsel__group_name(evsel);
447
448 ret = scnprintf(buf, size, "%s", group_name);
449
450 ret += scnprintf(buf + ret, size - ret, " { %s",
451 perf_evsel__name(evsel));
452
453 for_each_group_member(pos, evsel)
454 ret += scnprintf(buf + ret, size - ret, ", %s",
455 perf_evsel__name(pos));
456
457 ret += scnprintf(buf + ret, size - ret, " }");
458
459 return ret;
460}
461
437/* 462/*
438 * The enable_on_exec/disabled value strategy: 463 * The enable_on_exec/disabled value strategy:
439 * 464 *
@@ -1364,7 +1389,27 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
1364 struct perf_attr_details *details, FILE *fp) 1389 struct perf_attr_details *details, FILE *fp)
1365{ 1390{
1366 bool first = true; 1391 bool first = true;
1367 int printed = fprintf(fp, "%s", perf_evsel__name(evsel)); 1392 int printed = 0;
1393
1394 if (symbol_conf.event_group) {
1395 struct perf_evsel *pos;
1396
1397 if (!perf_evsel__is_group_leader(evsel))
1398 return 0;
1399
1400 if (evsel->nr_members > 1)
1401 printed += fprintf(fp, "%s{", evsel->group_name ?: "");
1402
1403 printed += fprintf(fp, "%s", perf_evsel__name(evsel));
1404 for_each_group_member(pos, evsel)
1405 printed += fprintf(fp, ",%s", perf_evsel__name(pos));
1406
1407 if (evsel->nr_members > 1)
1408 printed += fprintf(fp, "}");
1409 goto out;
1410 }
1411
1412 printed += fprintf(fp, "%s", perf_evsel__name(evsel));
1368 1413
1369 if (details->verbose || details->freq) { 1414 if (details->verbose || details->freq) {
1370 printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64, 1415 printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64,
@@ -1405,7 +1450,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
1405 if_print(bp_type); 1450 if_print(bp_type);
1406 if_print(branch_sample_type); 1451 if_print(branch_sample_type);
1407 } 1452 }
1408 1453out:
1409 fputc('\n', fp); 1454 fputc('\n', fp);
1410 return ++printed; 1455 return ++printed;
1411} 1456}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index cbf42322a27e..8512f6a8a6ea 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -74,10 +74,13 @@ struct perf_evsel {
74 bool needs_swap; 74 bool needs_swap;
75 /* parse modifier helper */ 75 /* parse modifier helper */
76 int exclude_GH; 76 int exclude_GH;
77 int nr_members;
77 struct perf_evsel *leader; 78 struct perf_evsel *leader;
78 char *group_name; 79 char *group_name;
79}; 80};
80 81
82#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
83
81struct cpu_map; 84struct cpu_map;
82struct thread_map; 85struct thread_map;
83struct perf_evlist; 86struct perf_evlist;
@@ -111,6 +114,8 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
111int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, 114int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
112 char *bf, size_t size); 115 char *bf, size_t size);
113const char *perf_evsel__name(struct perf_evsel *evsel); 116const char *perf_evsel__name(struct perf_evsel *evsel);
117const char *perf_evsel__group_name(struct perf_evsel *evsel);
118int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
114 119
115int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); 120int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
116int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); 121int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
@@ -259,4 +264,15 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
259int perf_evsel__open_strerror(struct perf_evsel *evsel, 264int perf_evsel__open_strerror(struct perf_evsel *evsel,
260 struct perf_target *target, 265 struct perf_target *target,
261 int err, char *msg, size_t size); 266 int err, char *msg, size_t size);
267
268static inline int perf_evsel__group_idx(struct perf_evsel *evsel)
269{
270 return evsel->idx - evsel->leader->idx;
271}
272
273#define for_each_group_member(_evsel, _leader) \
274for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \
275 (_evsel) && (_evsel)->leader == (_leader); \
276 (_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node))
277
262#endif /* __PERF_EVSEL_H */ 278#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index f6081cb3fca3..8022b5254f75 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -1085,6 +1085,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused,
1085} 1085}
1086 1086
1087/* 1087/*
1088 * File format:
1089 *
1090 * struct group_descs {
1091 * u32 nr_groups;
1092 * struct group_desc {
1093 * char name[];
1094 * u32 leader_idx;
1095 * u32 nr_members;
1096 * }[nr_groups];
1097 * };
1098 */
1099static int write_group_desc(int fd, struct perf_header *h __maybe_unused,
1100 struct perf_evlist *evlist)
1101{
1102 u32 nr_groups = evlist->nr_groups;
1103 struct perf_evsel *evsel;
1104 int ret;
1105
1106 ret = do_write(fd, &nr_groups, sizeof(nr_groups));
1107 if (ret < 0)
1108 return ret;
1109
1110 list_for_each_entry(evsel, &evlist->entries, node) {
1111 if (perf_evsel__is_group_leader(evsel) &&
1112 evsel->nr_members > 1) {
1113 const char *name = evsel->group_name ?: "{anon_group}";
1114 u32 leader_idx = evsel->idx;
1115 u32 nr_members = evsel->nr_members;
1116
1117 ret = do_write_string(fd, name);
1118 if (ret < 0)
1119 return ret;
1120
1121 ret = do_write(fd, &leader_idx, sizeof(leader_idx));
1122 if (ret < 0)
1123 return ret;
1124
1125 ret = do_write(fd, &nr_members, sizeof(nr_members));
1126 if (ret < 0)
1127 return ret;
1128 }
1129 }
1130 return 0;
1131}
1132
1133/*
1088 * default get_cpuid(): nothing gets recorded 1134 * default get_cpuid(): nothing gets recorded
1089 * actual implementation must be in arch/$(ARCH)/util/header.c 1135 * actual implementation must be in arch/$(ARCH)/util/header.c
1090 */ 1136 */
@@ -1447,6 +1493,31 @@ error:
1447 fprintf(fp, "# pmu mappings: unable to read\n"); 1493 fprintf(fp, "# pmu mappings: unable to read\n");
1448} 1494}
1449 1495
1496static void print_group_desc(struct perf_header *ph, int fd __maybe_unused,
1497 FILE *fp)
1498{
1499 struct perf_session *session;
1500 struct perf_evsel *evsel;
1501 u32 nr = 0;
1502
1503 session = container_of(ph, struct perf_session, header);
1504
1505 list_for_each_entry(evsel, &session->evlist->entries, node) {
1506 if (perf_evsel__is_group_leader(evsel) &&
1507 evsel->nr_members > 1) {
1508 fprintf(fp, "# group: %s{%s", evsel->group_name ?: "",
1509 perf_evsel__name(evsel));
1510
1511 nr = evsel->nr_members - 1;
1512 } else if (nr) {
1513 fprintf(fp, ",%s", perf_evsel__name(evsel));
1514
1515 if (--nr == 0)
1516 fprintf(fp, "}\n");
1517 }
1518 }
1519}
1520
1450static int __event_process_build_id(struct build_id_event *bev, 1521static int __event_process_build_id(struct build_id_event *bev,
1451 char *filename, 1522 char *filename,
1452 struct perf_session *session) 1523 struct perf_session *session)
@@ -1961,6 +2032,98 @@ error:
1961 return -1; 2032 return -1;
1962} 2033}
1963 2034
2035static int process_group_desc(struct perf_file_section *section __maybe_unused,
2036 struct perf_header *ph, int fd,
2037 void *data __maybe_unused)
2038{
2039 size_t ret = -1;
2040 u32 i, nr, nr_groups;
2041 struct perf_session *session;
2042 struct perf_evsel *evsel, *leader = NULL;
2043 struct group_desc {
2044 char *name;
2045 u32 leader_idx;
2046 u32 nr_members;
2047 } *desc;
2048
2049 if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups))
2050 return -1;
2051
2052 if (ph->needs_swap)
2053 nr_groups = bswap_32(nr_groups);
2054
2055 ph->env.nr_groups = nr_groups;
2056 if (!nr_groups) {
2057 pr_debug("group desc not available\n");
2058 return 0;
2059 }
2060
2061 desc = calloc(nr_groups, sizeof(*desc));
2062 if (!desc)
2063 return -1;
2064
2065 for (i = 0; i < nr_groups; i++) {
2066 desc[i].name = do_read_string(fd, ph);
2067 if (!desc[i].name)
2068 goto out_free;
2069
2070 if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32))
2071 goto out_free;
2072
2073 if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32))
2074 goto out_free;
2075
2076 if (ph->needs_swap) {
2077 desc[i].leader_idx = bswap_32(desc[i].leader_idx);
2078 desc[i].nr_members = bswap_32(desc[i].nr_members);
2079 }
2080 }
2081
2082 /*
2083 * Rebuild group relationship based on the group_desc
2084 */
2085 session = container_of(ph, struct perf_session, header);
2086 session->evlist->nr_groups = nr_groups;
2087
2088 i = nr = 0;
2089 list_for_each_entry(evsel, &session->evlist->entries, node) {
2090 if (evsel->idx == (int) desc[i].leader_idx) {
2091 evsel->leader = evsel;
2092 /* {anon_group} is a dummy name */
2093 if (strcmp(desc[i].name, "{anon_group}"))
2094 evsel->group_name = desc[i].name;
2095 evsel->nr_members = desc[i].nr_members;
2096
2097 if (i >= nr_groups || nr > 0) {
2098 pr_debug("invalid group desc\n");
2099 goto out_free;
2100 }
2101
2102 leader = evsel;
2103 nr = evsel->nr_members - 1;
2104 i++;
2105 } else if (nr) {
2106 /* This is a group member */
2107 evsel->leader = leader;
2108
2109 nr--;
2110 }
2111 }
2112
2113 if (i != nr_groups || nr != 0) {
2114 pr_debug("invalid group desc\n");
2115 goto out_free;
2116 }
2117
2118 ret = 0;
2119out_free:
2120 while ((int) --i >= 0)
2121 free(desc[i].name);
2122 free(desc);
2123
2124 return ret;
2125}
2126
1964struct feature_ops { 2127struct feature_ops {
1965 int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); 2128 int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
1966 void (*print)(struct perf_header *h, int fd, FILE *fp); 2129 void (*print)(struct perf_header *h, int fd, FILE *fp);
@@ -2000,6 +2163,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
2000 FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), 2163 FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
2001 FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), 2164 FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
2002 FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), 2165 FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
2166 FEAT_OPP(HEADER_GROUP_DESC, group_desc),
2003}; 2167};
2004 2168
2005struct header_print_data { 2169struct header_print_data {
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 20f0344accb1..c9fc55cada6d 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -29,6 +29,7 @@ enum {
29 HEADER_NUMA_TOPOLOGY, 29 HEADER_NUMA_TOPOLOGY,
30 HEADER_BRANCH_STACK, 30 HEADER_BRANCH_STACK,
31 HEADER_PMU_MAPPINGS, 31 HEADER_PMU_MAPPINGS,
32 HEADER_GROUP_DESC,
32 HEADER_LAST_FEATURE, 33 HEADER_LAST_FEATURE,
33 HEADER_FEAT_BITS = 256, 34 HEADER_FEAT_BITS = 256,
34}; 35};
@@ -79,6 +80,7 @@ struct perf_session_env {
79 char *numa_nodes; 80 char *numa_nodes;
80 int nr_pmu_mappings; 81 int nr_pmu_mappings;
81 char *pmu_mappings; 82 char *pmu_mappings;
83 int nr_groups;
82}; 84};
83 85
84struct perf_header { 86struct perf_header {
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index 8170a3d11ffa..f855941bebea 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -4,6 +4,7 @@
4#include "hist.h" 4#include "hist.h"
5#include "session.h" 5#include "session.h"
6#include "sort.h" 6#include "sort.h"
7#include "evsel.h"
7#include <math.h> 8#include <math.h>
8 9
9static bool hists__filter_entry_by_dso(struct hists *hists, 10static bool hists__filter_entry_by_dso(struct hists *hists,
@@ -540,6 +541,62 @@ void hists__collapse_resort_threaded(struct hists *hists)
540 * reverse the map, sort on period. 541 * reverse the map, sort on period.
541 */ 542 */
542 543
544static int period_cmp(u64 period_a, u64 period_b)
545{
546 if (period_a > period_b)
547 return 1;
548 if (period_a < period_b)
549 return -1;
550 return 0;
551}
552
553static int hist_entry__sort_on_period(struct hist_entry *a,
554 struct hist_entry *b)
555{
556 int ret;
557 int i, nr_members;
558 struct perf_evsel *evsel;
559 struct hist_entry *pair;
560 u64 *periods_a, *periods_b;
561
562 ret = period_cmp(a->stat.period, b->stat.period);
563 if (ret || !symbol_conf.event_group)
564 return ret;
565
566 evsel = hists_to_evsel(a->hists);
567 nr_members = evsel->nr_members;
568 if (nr_members <= 1)
569 return ret;
570
571 periods_a = zalloc(sizeof(periods_a) * nr_members);
572 periods_b = zalloc(sizeof(periods_b) * nr_members);
573
574 if (!periods_a || !periods_b)
575 goto out;
576
577 list_for_each_entry(pair, &a->pairs.head, pairs.node) {
578 evsel = hists_to_evsel(pair->hists);
579 periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period;
580 }
581
582 list_for_each_entry(pair, &b->pairs.head, pairs.node) {
583 evsel = hists_to_evsel(pair->hists);
584 periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period;
585 }
586
587 for (i = 1; i < nr_members; i++) {
588 ret = period_cmp(periods_a[i], periods_b[i]);
589 if (ret)
590 break;
591 }
592
593out:
594 free(periods_a);
595 free(periods_b);
596
597 return ret;
598}
599
543static void __hists__insert_output_entry(struct rb_root *entries, 600static void __hists__insert_output_entry(struct rb_root *entries,
544 struct hist_entry *he, 601 struct hist_entry *he,
545 u64 min_callchain_hits) 602 u64 min_callchain_hits)
@@ -556,7 +613,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
556 parent = *p; 613 parent = *p;
557 iter = rb_entry(parent, struct hist_entry, rb_node); 614 iter = rb_entry(parent, struct hist_entry, rb_node);
558 615
559 if (he->stat.period > iter->stat.period) 616 if (hist_entry__sort_on_period(he, iter) > 0)
560 p = &(*p)->rb_left; 617 p = &(*p)->rb_left;
561 else 618 else
562 p = &(*p)->rb_right; 619 p = &(*p)->rb_right;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 02f6421f03a0..4e0f5c2a9fda 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -884,6 +884,7 @@ int parse_events(struct perf_evlist *evlist, const char *str)
884 if (!ret) { 884 if (!ret) {
885 int entries = data.idx - evlist->nr_entries; 885 int entries = data.idx - evlist->nr_entries;
886 perf_evlist__splice_list_tail(evlist, &data.list, entries); 886 perf_evlist__splice_list_tail(evlist, &data.list, entries);
887 evlist->nr_groups += data.nr_groups;
887 return 0; 888 return 0;
888 } 889 }
889 890
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 2cd2c42a69c5..8a4859315fd9 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -64,6 +64,7 @@ struct parse_events_term {
64struct parse_events_evlist { 64struct parse_events_evlist {
65 struct list_head list; 65 struct list_head list;
66 int idx; 66 int idx;
67 int nr_groups;
67}; 68};
68 69
69struct parse_events_terms { 70struct parse_events_terms {
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 9d43c86176ff..4de2fdca98c8 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -23,6 +23,14 @@ do { \
23 YYABORT; \ 23 YYABORT; \
24} while (0) 24} while (0)
25 25
26static inc_group_count(struct list_head *list,
27 struct parse_events_evlist *data)
28{
29 /* Count groups only have more than 1 members */
30 if (!list_is_last(list->next, list))
31 data->nr_groups++;
32}
33
26%} 34%}
27 35
28%token PE_START_EVENTS PE_START_TERMS 36%token PE_START_EVENTS PE_START_TERMS
@@ -123,6 +131,7 @@ PE_NAME '{' events '}'
123{ 131{
124 struct list_head *list = $3; 132 struct list_head *list = $3;
125 133
134 inc_group_count(list, _data);
126 parse_events__set_leader($1, list); 135 parse_events__set_leader($1, list);
127 $$ = list; 136 $$ = list;
128} 137}
@@ -131,6 +140,7 @@ PE_NAME '{' events '}'
131{ 140{
132 struct list_head *list = $2; 141 struct list_head *list = $2;
133 142
143 inc_group_count(list, _data);
134 parse_events__set_leader(NULL, list); 144 parse_events__set_leader(NULL, list);
135 $$ = list; 145 $$ = list;
136} 146}
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index d97377ac2f16..b62ca37c4b77 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -96,7 +96,8 @@ struct symbol_conf {
96 initialized, 96 initialized,
97 kptr_restrict, 97 kptr_restrict,
98 annotate_asm_raw, 98 annotate_asm_raw,
99 annotate_src; 99 annotate_src,
100 event_group;
100 const char *vmlinux_name, 101 const char *vmlinux_name,
101 *kallsyms_name, 102 *kallsyms_name,
102 *source_prefix, 103 *source_prefix,