diff options
author | Ingo Molnar <mingo@kernel.org> | 2013-02-01 05:17:09 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2013-02-01 05:17:09 -0500 |
commit | 9c4c5fd9e6207f04dbf59c5a9699fded144542e6 (patch) | |
tree | 7b08598257898727183d6bb8b2c4824b1b0d52a5 /tools | |
parent | 152fefa921535665f95840c08062844ab2f5593e (diff) | |
parent | 2ac3634a7e1c8eedc961030c87c5c36ebd5bbf8e (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.txt | 4 | ||||
-rw-r--r-- | tools/perf/Documentation/perf-report.txt | 3 | ||||
-rw-r--r-- | tools/perf/builtin-evlist.c | 7 | ||||
-rw-r--r-- | tools/perf/builtin-record.c | 3 | ||||
-rw-r--r-- | tools/perf/builtin-report.c | 47 | ||||
-rw-r--r-- | tools/perf/builtin-top.c | 62 | ||||
-rw-r--r-- | tools/perf/tests/parse-events.c | 28 | ||||
-rw-r--r-- | tools/perf/ui/browsers/hists.c | 217 | ||||
-rw-r--r-- | tools/perf/ui/gtk/hists.c | 130 | ||||
-rw-r--r-- | tools/perf/ui/hist.c | 306 | ||||
-rw-r--r-- | tools/perf/ui/stdio/hist.c | 2 | ||||
-rw-r--r-- | tools/perf/util/evlist.c | 7 | ||||
-rw-r--r-- | tools/perf/util/evlist.h | 1 | ||||
-rw-r--r-- | tools/perf/util/evsel.c | 49 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 16 | ||||
-rw-r--r-- | tools/perf/util/header.c | 164 | ||||
-rw-r--r-- | tools/perf/util/header.h | 2 | ||||
-rw-r--r-- | tools/perf/util/hist.c | 59 | ||||
-rw-r--r-- | tools/perf/util/parse-events.c | 1 | ||||
-rw-r--r-- | tools/perf/util/parse-events.h | 1 | ||||
-rw-r--r-- | tools/perf/util/parse-events.y | 10 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 3 |
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 | |||
31 | SEE ALSO | 35 | SEE ALSO |
32 | -------- | 36 | -------- |
33 | linkperf:perf-record[1], linkperf:perf-list[1], | 37 | linkperf: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 | |||
206 | SEE ALSO | 209 | SEE ALSO |
207 | -------- | 210 | -------- |
208 | linkperf:perf-stat[1], linkperf:perf-annotate[1] | 211 | linkperf: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 | ||
58 | static 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 | |||
57 | static int perf_report__add_branch_hist_entry(struct perf_tool *tool, | 68 | static 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 | ||
71 | static volatile int done; | ||
72 | |||
71 | static void perf_top__update_print_entries(struct perf_top *top) | 73 | static 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 | ||
434 | static void perf_top__handle_keypress(struct perf_top *top, int c) | 436 | static 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 | ||
543 | static void perf_top__sort_new_samples(void *arg) | 550 | static 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 | } |
624 | process_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 | ||
871 | static void perf_top__start_counters(struct perf_top *top) | 876 | static 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 | ||
905 | out_err: | 910 | out_err: |
906 | exit_browser(0); | 911 | return -1; |
907 | exit(0); | ||
908 | } | 912 | } |
909 | 913 | ||
910 | static int perf_top__setup_sample_type(struct perf_top *top) | 914 | static 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, ¶m)) { | 990 | if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
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; | ||
996 | out_delete: | 1006 | out_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 | ||
1003 | static int | 1013 | static 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 | ||
1223 | out_delete_maps: | ||
1224 | perf_evlist__delete_maps(top.evlist); | ||
1213 | out_delete_evlist: | 1225 | out_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) \ | 570 | struct hpp_arg { |
571 | static 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 | |||
576 | static 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 | |||
585 | static 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 | } | ||
661 | out: | ||
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) \ | ||
669 | static 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); \ | 674 | static 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 | ||
580 | HPP__COLOR_FN(overhead, period) | 680 | __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain) |
581 | HPP__COLOR_FN(overhead_sys, period_sys) | 681 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL) |
582 | HPP__COLOR_FN(overhead_us, period_us) | 682 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL) |
583 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | 683 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL) |
584 | HPP__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 | ||
588 | void hist_browser__init_hpp(void) | 688 | void 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 | ||
1723 | static 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 | |||
1611 | static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, | 1734 | static 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 | ||
1642 | int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, | 1766 | int 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 | |||
1772 | single_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) \ | 11 | static int __percent_color_snprintf(char *buf, size_t size, double percent) |
12 | static 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 | |||
29 | static 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) \ | ||
92 | static 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); \ | 97 | static 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 | ||
30 | HPP__COLOR_FN(overhead, period) | 103 | __HPP_COLOR_PERCENT_FN(overhead, period) |
31 | HPP__COLOR_FN(overhead_sys, period_sys) | 104 | __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys) |
32 | HPP__COLOR_FN(overhead_us, period_us) | 105 | __HPP_COLOR_PERCENT_FN(overhead_us, period_us) |
33 | HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) | 106 | __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys) |
34 | HPP__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 | ||
39 | void perf_gtk__init_hpp(void) | 112 | void 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 */ |
9 | static int hpp__header_overhead(struct perf_hpp *hpp) | ||
10 | { | ||
11 | return scnprintf(hpp->buf, hpp->size, "Overhead"); | ||
12 | } | ||
13 | |||
14 | static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused) | ||
15 | { | ||
16 | return 8; | ||
17 | } | ||
18 | |||
19 | static 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 | |||
27 | static 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); | 10 | typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...); |
34 | } | ||
35 | |||
36 | static 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 | |||
43 | static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused) | ||
44 | { | ||
45 | return 7; | ||
46 | } | ||
47 | 11 | ||
48 | static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) | 12 | static 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 | ||
56 | static 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 | ||
65 | static 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 | ||
72 | static 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 | ||
77 | static 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 | |||
85 | static 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 | |||
94 | static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp) | ||
95 | { | ||
96 | return scnprintf(hpp->buf, hpp->size, "guest sys"); | ||
97 | } | ||
98 | 45 | ||
99 | static 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 | ||
104 | static 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 | ||
113 | static 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 | ||
123 | static int hpp__header_overhead_guest_us(struct perf_hpp *hpp) | 74 | static 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 | ||
128 | static 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 | ||
133 | static 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 | } | 86 | static 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) \ | ||
99 | static 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) \ | ||
112 | static u64 he_get_##_field(struct hist_entry *he) \ | ||
113 | { \ | ||
114 | return he->stat._field; \ | ||
115 | } \ | ||
116 | \ | ||
117 | static 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) \ | ||
124 | static 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) \ | ||
132 | static u64 he_get_raw_##_field(struct hist_entry *he) \ | ||
133 | { \ | ||
134 | return he->stat._field; \ | ||
135 | } \ | ||
136 | \ | ||
137 | static 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 | |||
155 | HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8) | ||
156 | HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8) | ||
157 | HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8) | ||
158 | HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8) | ||
159 | HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8) | ||
160 | |||
161 | HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12) | ||
162 | HPP_RAW_FNS(period, "Period", period, 12, 12) | ||
141 | 163 | ||
142 | static 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 | ||
152 | static int hpp__header_baseline(struct perf_hpp *hpp) | 165 | static 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 | ||
199 | static 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 | |||
206 | static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused) | ||
207 | { | ||
208 | return 11; | ||
209 | } | ||
210 | |||
211 | static 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 | |||
218 | static 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 | |||
225 | static int hpp__width_period(struct perf_hpp *hpp __maybe_unused) | ||
226 | { | ||
227 | return 12; | ||
228 | } | ||
229 | |||
230 | static 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 | |||
237 | static int hpp__header_period_baseline(struct perf_hpp *hpp) | 212 | static 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 | |||
257 | static int hpp__header_delta(struct perf_hpp *hpp) | 233 | static 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 | ||
409 | LIST_HEAD(perf_hpp__list); | 385 | LIST_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 | |||
414 | void perf_hpp__init(void) | 401 | void 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 | ||
8 | static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) | 9 | static 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 | ||
127 | void perf_evlist__set_leader(struct perf_evlist *evlist) | 130 | void 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 | ||
133 | int perf_evlist__add_default(struct perf_evlist *evlist) | 138 | int 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 | ||
437 | const char *perf_evsel__group_name(struct perf_evsel *evsel) | ||
438 | { | ||
439 | return evsel->group_name ?: "anon group"; | ||
440 | } | ||
441 | |||
442 | int 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 | 1453 | out: | |
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 | |||
81 | struct cpu_map; | 84 | struct cpu_map; |
82 | struct thread_map; | 85 | struct thread_map; |
83 | struct perf_evlist; | 86 | struct perf_evlist; |
@@ -111,6 +114,8 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; | |||
111 | int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, | 114 | int __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); |
113 | const char *perf_evsel__name(struct perf_evsel *evsel); | 116 | const char *perf_evsel__name(struct perf_evsel *evsel); |
117 | const char *perf_evsel__group_name(struct perf_evsel *evsel); | ||
118 | int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); | ||
114 | 119 | ||
115 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); | 120 | int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); |
116 | int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); | 121 | int 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, | |||
259 | int perf_evsel__open_strerror(struct perf_evsel *evsel, | 264 | int 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 | |||
268 | static 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) \ | ||
274 | for ((_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 | */ | ||
1099 | static 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 | ||
1496 | static 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 | |||
1450 | static int __event_process_build_id(struct build_id_event *bev, | 1521 | static 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 | ||
2035 | static 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; | ||
2119 | out_free: | ||
2120 | while ((int) --i >= 0) | ||
2121 | free(desc[i].name); | ||
2122 | free(desc); | ||
2123 | |||
2124 | return ret; | ||
2125 | } | ||
2126 | |||
1964 | struct feature_ops { | 2127 | struct 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 | ||
2005 | struct header_print_data { | 2169 | struct 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 | ||
84 | struct perf_header { | 86 | struct 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 | ||
9 | static bool hists__filter_entry_by_dso(struct hists *hists, | 10 | static 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 | ||
544 | static 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 | |||
553 | static 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 | |||
593 | out: | ||
594 | free(periods_a); | ||
595 | free(periods_b); | ||
596 | |||
597 | return ret; | ||
598 | } | ||
599 | |||
543 | static void __hists__insert_output_entry(struct rb_root *entries, | 600 | static 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 { | |||
64 | struct parse_events_evlist { | 64 | struct 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 | ||
69 | struct parse_events_terms { | 70 | struct 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 | ||
26 | static 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, |