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