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/perf | |
| 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/perf')
| -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, |
