Commit 9c4c5fd9 authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 152fefa9 2ac3634a
What: /sys/devices/cpu/events/
/sys/devices/cpu/events/branch-misses
/sys/devices/cpu/events/cache-references
/sys/devices/cpu/events/cache-misses
/sys/devices/cpu/events/stalled-cycles-frontend
/sys/devices/cpu/events/branch-instructions
/sys/devices/cpu/events/stalled-cycles-backend
/sys/devices/cpu/events/instructions
/sys/devices/cpu/events/cpu-cycles
Date: 2013/01/08
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
Description: Generic performance monitoring events
A collection of performance monitoring events that may be
supported by many/most CPUs. These events can be monitored
using the 'perf(1)' tool.
The contents of each file would look like:
event=0xNNNN
where 'N' is a hex digit and the number '0xNNNN' shows the
"raw code" for the perf event identified by the file's
"basename".
What: /sys/devices/cpu/events/PM_LD_MISS_L1
/sys/devices/cpu/events/PM_LD_REF_L1
/sys/devices/cpu/events/PM_CYC
/sys/devices/cpu/events/PM_BRU_FIN
/sys/devices/cpu/events/PM_GCT_NOSLOT_CYC
/sys/devices/cpu/events/PM_BRU_MPRED
/sys/devices/cpu/events/PM_INST_CMPL
/sys/devices/cpu/events/PM_CMPLU_STALL
Date: 2013/01/08
Contact: Linux kernel mailing list <linux-kernel@vger.kernel.org>
Linux Powerpc mailing list <linuxppc-dev@ozlabs.org>
Description: POWER-systems specific performance monitoring events
A collection of performance monitoring events that may be
supported by the POWER CPU. These events can be monitored
using the 'perf(1)' tool.
These events may not be supported by other CPUs.
The contents of each file would look like:
event=0xNNNN
where 'N' is a hex digit and the number '0xNNNN' shows the
"raw code" for the perf event identified by the file's
"basename".
Further, multiple terms like 'event=0xNNNN' can be specified
and separated with comma. All available terms are defined in
the /sys/bus/event_source/devices/<dev>/format file.
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <asm/hw_irq.h> #include <asm/hw_irq.h>
#include <linux/device.h>
#define MAX_HWEVENTS 8 #define MAX_HWEVENTS 8
#define MAX_EVENT_ALTERNATIVES 8 #define MAX_EVENT_ALTERNATIVES 8
...@@ -35,6 +36,7 @@ struct power_pmu { ...@@ -35,6 +36,7 @@ struct power_pmu {
void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]); void (*disable_pmc)(unsigned int pmc, unsigned long mmcr[]);
int (*limited_pmc_event)(u64 event_id); int (*limited_pmc_event)(u64 event_id);
u32 flags; u32 flags;
const struct attribute_group **attr_groups;
int n_generic; int n_generic;
int *generic_events; int *generic_events;
int (*cache_events)[PERF_COUNT_HW_CACHE_MAX] int (*cache_events)[PERF_COUNT_HW_CACHE_MAX]
...@@ -109,3 +111,27 @@ extern unsigned long perf_instruction_pointer(struct pt_regs *regs); ...@@ -109,3 +111,27 @@ extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
* If an event_id is not subject to the constraint expressed by a particular * If an event_id is not subject to the constraint expressed by a particular
* field, then it will have 0 in both the mask and value for that field. * field, then it will have 0 in both the mask and value for that field.
*/ */
extern ssize_t power_events_sysfs_show(struct device *dev,
struct device_attribute *attr, char *page);
/*
* EVENT_VAR() is same as PMU_EVENT_VAR with a suffix.
*
* Having a suffix allows us to have aliases in sysfs - eg: the generic
* event 'cpu-cycles' can have two entries in sysfs: 'cpu-cycles' and
* 'PM_CYC' where the latter is the name by which the event is known in
* POWER CPU specification.
*/
#define EVENT_VAR(_id, _suffix) event_attr_##_id##_suffix
#define EVENT_PTR(_id, _suffix) &EVENT_VAR(_id, _suffix)
#define EVENT_ATTR(_name, _id, _suffix) \
PMU_EVENT_ATTR(_name, EVENT_VAR(_id, _suffix), PME_PM_##_id, \
power_events_sysfs_show)
#define GENERIC_EVENT_ATTR(_name, _id) EVENT_ATTR(_name, _id, _g)
#define GENERIC_EVENT_PTR(_id) EVENT_PTR(_id, _g)
#define POWER_EVENT_ATTR(_name, _id) EVENT_ATTR(PM_##_name, _id, _p)
#define POWER_EVENT_PTR(_id) EVENT_PTR(_id, _p)
...@@ -1305,6 +1305,16 @@ static int power_pmu_event_idx(struct perf_event *event) ...@@ -1305,6 +1305,16 @@ static int power_pmu_event_idx(struct perf_event *event)
return event->hw.idx; return event->hw.idx;
} }
ssize_t power_events_sysfs_show(struct device *dev,
struct device_attribute *attr, char *page)
{
struct perf_pmu_events_attr *pmu_attr;
pmu_attr = container_of(attr, struct perf_pmu_events_attr, attr);
return sprintf(page, "event=0x%02llx\n", pmu_attr->id);
}
struct pmu power_pmu = { struct pmu power_pmu = {
.pmu_enable = power_pmu_enable, .pmu_enable = power_pmu_enable,
.pmu_disable = power_pmu_disable, .pmu_disable = power_pmu_disable,
...@@ -1537,6 +1547,8 @@ int __cpuinit register_power_pmu(struct power_pmu *pmu) ...@@ -1537,6 +1547,8 @@ int __cpuinit register_power_pmu(struct power_pmu *pmu)
pr_info("%s performance monitor hardware support registered\n", pr_info("%s performance monitor hardware support registered\n",
pmu->name); pmu->name);
power_pmu.attr_groups = ppmu->attr_groups;
#ifdef MSR_HV #ifdef MSR_HV
/* /*
* Use FCHV to ignore kernel events if MSR.HV is set. * Use FCHV to ignore kernel events if MSR.HV is set.
......
...@@ -50,6 +50,18 @@ ...@@ -50,6 +50,18 @@
#define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8) #define MMCR1_PMCSEL_SH(n) (MMCR1_PMC1SEL_SH - (n) * 8)
#define MMCR1_PMCSEL_MSK 0xff #define MMCR1_PMCSEL_MSK 0xff
/*
* Power7 event codes.
*/
#define PME_PM_CYC 0x1e
#define PME_PM_GCT_NOSLOT_CYC 0x100f8
#define PME_PM_CMPLU_STALL 0x4000a
#define PME_PM_INST_CMPL 0x2
#define PME_PM_LD_REF_L1 0xc880
#define PME_PM_LD_MISS_L1 0x400f0
#define PME_PM_BRU_FIN 0x10068
#define PME_PM_BRU_MPRED 0x400f6
/* /*
* Layout of constraint bits: * Layout of constraint bits:
* 6666555555555544444444443333333333222222222211111111110000000000 * 6666555555555544444444443333333333222222222211111111110000000000
...@@ -307,14 +319,14 @@ static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[]) ...@@ -307,14 +319,14 @@ static void power7_disable_pmc(unsigned int pmc, unsigned long mmcr[])
} }
static int power7_generic_events[] = { static int power7_generic_events[] = {
[PERF_COUNT_HW_CPU_CYCLES] = 0x1e, [PERF_COUNT_HW_CPU_CYCLES] = PME_PM_CYC,
[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = 0x100f8, /* GCT_NOSLOT_CYC */ [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = PME_PM_GCT_NOSLOT_CYC,
[PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = 0x4000a, /* CMPLU_STALL */ [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = PME_PM_CMPLU_STALL,
[PERF_COUNT_HW_INSTRUCTIONS] = 2, [PERF_COUNT_HW_INSTRUCTIONS] = PME_PM_INST_CMPL,
[PERF_COUNT_HW_CACHE_REFERENCES] = 0xc880, /* LD_REF_L1_LSU*/ [PERF_COUNT_HW_CACHE_REFERENCES] = PME_PM_LD_REF_L1,
[PERF_COUNT_HW_CACHE_MISSES] = 0x400f0, /* LD_MISS_L1 */ [PERF_COUNT_HW_CACHE_MISSES] = PME_PM_LD_MISS_L1,
[PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = 0x10068, /* BRU_FIN */ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = PME_PM_BRU_FIN,
[PERF_COUNT_HW_BRANCH_MISSES] = 0x400f6, /* BR_MPRED */ [PERF_COUNT_HW_BRANCH_MISSES] = PME_PM_BRU_MPRED,
}; };
#define C(x) PERF_COUNT_HW_CACHE_##x #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,6 +374,57 @@ static int power7_cache_events[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
}, },
}; };
GENERIC_EVENT_ATTR(cpu-cycles, CYC);
GENERIC_EVENT_ATTR(stalled-cycles-frontend, GCT_NOSLOT_CYC);
GENERIC_EVENT_ATTR(stalled-cycles-backend, CMPLU_STALL);
GENERIC_EVENT_ATTR(instructions, INST_CMPL);
GENERIC_EVENT_ATTR(cache-references, LD_REF_L1);
GENERIC_EVENT_ATTR(cache-misses, LD_MISS_L1);
GENERIC_EVENT_ATTR(branch-instructions, BRU_FIN);
GENERIC_EVENT_ATTR(branch-misses, BRU_MPRED);
POWER_EVENT_ATTR(CYC, CYC);
POWER_EVENT_ATTR(GCT_NOSLOT_CYC, GCT_NOSLOT_CYC);
POWER_EVENT_ATTR(CMPLU_STALL, CMPLU_STALL);
POWER_EVENT_ATTR(INST_CMPL, INST_CMPL);
POWER_EVENT_ATTR(LD_REF_L1, LD_REF_L1);
POWER_EVENT_ATTR(LD_MISS_L1, LD_MISS_L1);
POWER_EVENT_ATTR(BRU_FIN, BRU_FIN)
POWER_EVENT_ATTR(BRU_MPRED, BRU_MPRED);
static struct attribute *power7_events_attr[] = {
GENERIC_EVENT_PTR(CYC),
GENERIC_EVENT_PTR(GCT_NOSLOT_CYC),
GENERIC_EVENT_PTR(CMPLU_STALL),
GENERIC_EVENT_PTR(INST_CMPL),
GENERIC_EVENT_PTR(LD_REF_L1),
GENERIC_EVENT_PTR(LD_MISS_L1),
GENERIC_EVENT_PTR(BRU_FIN),
GENERIC_EVENT_PTR(BRU_MPRED),
POWER_EVENT_PTR(CYC),
POWER_EVENT_PTR(GCT_NOSLOT_CYC),
POWER_EVENT_PTR(CMPLU_STALL),
POWER_EVENT_PTR(INST_CMPL),
POWER_EVENT_PTR(LD_REF_L1),
POWER_EVENT_PTR(LD_MISS_L1),
POWER_EVENT_PTR(BRU_FIN),
POWER_EVENT_PTR(BRU_MPRED),
NULL
};
static struct attribute_group power7_pmu_events_group = {
.name = "events",
.attrs = power7_events_attr,
};
static const struct attribute_group *power7_pmu_attr_groups[] = {
&power7_pmu_events_group,
NULL,
};
static struct power_pmu power7_pmu = { static struct power_pmu power7_pmu = {
.name = "POWER7", .name = "POWER7",
.n_counter = 6, .n_counter = 6,
...@@ -373,6 +436,7 @@ static struct power_pmu power7_pmu = { ...@@ -373,6 +436,7 @@ static struct power_pmu power7_pmu = {
.get_alternatives = power7_get_alternatives, .get_alternatives = power7_get_alternatives,
.disable_pmc = power7_disable_pmc, .disable_pmc = power7_disable_pmc,
.flags = PPMU_ALT_SIPR, .flags = PPMU_ALT_SIPR,
.attr_groups = power7_pmu_attr_groups,
.n_generic = ARRAY_SIZE(power7_generic_events), .n_generic = ARRAY_SIZE(power7_generic_events),
.generic_events = power7_generic_events, .generic_events = power7_generic_events,
.cache_events = &power7_cache_events, .cache_events = &power7_cache_events,
......
...@@ -1310,11 +1310,6 @@ static struct attribute_group x86_pmu_format_group = { ...@@ -1310,11 +1310,6 @@ static struct attribute_group x86_pmu_format_group = {
.attrs = NULL, .attrs = NULL,
}; };
struct perf_pmu_events_attr {
struct device_attribute attr;
u64 id;
};
/* /*
* Remove all undefined events (x86_pmu.event_map(id) == 0) * Remove all undefined events (x86_pmu.event_map(id) == 0)
* out of events_attr attributes. * out of events_attr attributes.
...@@ -1348,11 +1343,9 @@ static ssize_t events_sysfs_show(struct device *dev, struct device_attribute *at ...@@ -1348,11 +1343,9 @@ static ssize_t events_sysfs_show(struct device *dev, struct device_attribute *at
#define EVENT_VAR(_id) event_attr_##_id #define EVENT_VAR(_id) event_attr_##_id
#define EVENT_PTR(_id) &event_attr_##_id.attr.attr #define EVENT_PTR(_id) &event_attr_##_id.attr.attr
#define EVENT_ATTR(_name, _id) \ #define EVENT_ATTR(_name, _id) \
static struct perf_pmu_events_attr EVENT_VAR(_id) = { \ PMU_EVENT_ATTR(_name, EVENT_VAR(_id), PERF_COUNT_HW_##_id, \
.attr = __ATTR(_name, 0444, events_sysfs_show, NULL), \ events_sysfs_show)
.id = PERF_COUNT_HW_##_id, \
};
EVENT_ATTR(cpu-cycles, CPU_CYCLES ); EVENT_ATTR(cpu-cycles, CPU_CYCLES );
EVENT_ATTR(instructions, INSTRUCTIONS ); EVENT_ATTR(instructions, INSTRUCTIONS );
......
...@@ -817,6 +817,17 @@ do { \ ...@@ -817,6 +817,17 @@ do { \
} while (0) } while (0)
struct perf_pmu_events_attr {
struct device_attribute attr;
u64 id;
};
#define PMU_EVENT_ATTR(_name, _var, _id, _show) \
static struct perf_pmu_events_attr _var = { \
.attr = __ATTR(_name, 0444, _show, NULL), \
.id = _id, \
};
#define PMU_FORMAT_ATTR(_name, _format) \ #define PMU_FORMAT_ATTR(_name, _format) \
static ssize_t \ static ssize_t \
_name##_show(struct device *dev, \ _name##_show(struct device *dev, \
......
...@@ -28,6 +28,10 @@ OPTIONS ...@@ -28,6 +28,10 @@ OPTIONS
--verbose=:: --verbose=::
Show all fields. Show all fields.
-g::
--group::
Show event group information.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-record[1], linkperf:perf-list[1], linkperf:perf-record[1], linkperf:perf-list[1],
......
...@@ -203,6 +203,9 @@ OPTIONS ...@@ -203,6 +203,9 @@ OPTIONS
--objdump=<path>:: --objdump=<path>::
Path to objdump binary. Path to objdump binary.
--group::
Show event group information together.
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-annotate[1] linkperf:perf-stat[1], linkperf:perf-annotate[1]
...@@ -39,6 +39,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -39,6 +39,8 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"), OPT_BOOLEAN('F', "freq", &details.freq, "Show the sample frequency"),
OPT_BOOLEAN('v', "verbose", &details.verbose, OPT_BOOLEAN('v', "verbose", &details.verbose,
"Show all event attr details"), "Show all event attr details"),
OPT_BOOLEAN('g', "group", &symbol_conf.event_group,
"Show event group information"),
OPT_END() OPT_END()
}; };
const char * const evlist_usage[] = { const char * const evlist_usage[] = {
...@@ -50,5 +52,10 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -50,5 +52,10 @@ int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
if (argc) if (argc)
usage_with_options(evlist_usage, options); usage_with_options(evlist_usage, options);
if (symbol_conf.event_group && (details.verbose || details.freq)) {
pr_err("--group option is not compatible with other options\n");
usage_with_options(evlist_usage, options);
}
return __cmd_evlist(input_name, &details); return __cmd_evlist(input_name, &details);
} }
...@@ -486,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv) ...@@ -486,6 +486,9 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
goto out_delete_session; goto out_delete_session;
} }
if (!evsel_list->nr_groups)
perf_header__clear_feat(&session->header, HEADER_GROUP_DESC);
/* /*
* perf_session__delete(session) will be called at perf_record__exit() * perf_session__delete(session) will be called at perf_record__exit()
*/ */
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "builtin.h" #include "builtin.h"
#include "util/util.h" #include "util/util.h"
#include "util/cache.h"
#include "util/annotate.h" #include "util/annotate.h"
#include "util/color.h" #include "util/color.h"
...@@ -54,6 +55,16 @@ struct perf_report { ...@@ -54,6 +55,16 @@ struct perf_report {
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
}; };
static int perf_report_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "report.group")) {
symbol_conf.event_group = perf_config_bool(var, value);
return 0;
}
return perf_default_config(var, value, cb);
}
static int perf_report__add_branch_hist_entry(struct perf_tool *tool, static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
struct addr_location *al, struct addr_location *al,
struct perf_sample *sample, struct perf_sample *sample,
...@@ -299,6 +310,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self, ...@@ -299,6 +310,21 @@ static size_t hists__fprintf_nr_sample_events(struct hists *self,
char unit; char unit;
unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE]; unsigned long nr_samples = self->stats.nr_events[PERF_RECORD_SAMPLE];
u64 nr_events = self->stats.total_period; u64 nr_events = self->stats.total_period;
struct perf_evsel *evsel = hists_to_evsel(self);
char buf[512];
size_t size = sizeof(buf);
if (symbol_conf.event_group && evsel->nr_members > 1) {
struct perf_evsel *pos;
perf_evsel__group_desc(evsel, buf, size);
evname = buf;
for_each_group_member(pos, evsel) {
nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
nr_events += pos->hists.stats.total_period;
}
}
nr_samples = convert_unit(nr_samples, &unit); nr_samples = convert_unit(nr_samples, &unit);
ret = fprintf(fp, "# Samples: %lu%c", nr_samples, unit); 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,6 +345,10 @@ static int perf_evlist__tty_browse_hists(struct perf_evlist *evlist,
struct hists *hists = &pos->hists; struct hists *hists = &pos->hists;
const char *evname = perf_evsel__name(pos); const char *evname = perf_evsel__name(pos);
if (symbol_conf.event_group &&
!perf_evsel__is_group_leader(pos))
continue;
hists__fprintf_nr_sample_events(hists, evname, stdout); hists__fprintf_nr_sample_events(hists, evname, stdout);
hists__fprintf(hists, true, 0, 0, stdout); hists__fprintf(hists, true, 0, 0, stdout);
fprintf(stdout, "\n\n"); fprintf(stdout, "\n\n");
...@@ -416,8 +446,16 @@ static int __cmd_report(struct perf_report *rep) ...@@ -416,8 +446,16 @@ static int __cmd_report(struct perf_report *rep)
hists->symbol_filter_str = rep->symbol_filter_str; hists->symbol_filter_str = rep->symbol_filter_str;
hists__collapse_resort(hists); hists__collapse_resort(hists);
hists__output_resort(hists);
nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE]; nr_samples += hists->stats.nr_events[PERF_RECORD_SAMPLE];
/* Non-group events are considered as leader */
if (symbol_conf.event_group &&
!perf_evsel__is_group_leader(pos)) {
struct hists *leader_hists = &pos->leader->hists;
hists__match(leader_hists, hists);
hists__link(leader_hists, hists);
}
} }
if (nr_samples == 0) { if (nr_samples == 0) {
...@@ -425,6 +463,9 @@ static int __cmd_report(struct perf_report *rep) ...@@ -425,6 +463,9 @@ static int __cmd_report(struct perf_report *rep)
goto out_delete; goto out_delete;
} }
list_for_each_entry(pos, &session->evlist->entries, node)
hists__output_resort(&pos->hists);
if (use_browser > 0) { if (use_browser > 0) {
if (use_browser == 1) { if (use_browser == 1) {
perf_evlist__tui_browse_hists(session->evlist, help, 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,6 +679,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
"Specify disassembler style (e.g. -M intel for intel syntax)"), "Specify disassembler style (e.g. -M intel for intel syntax)"),
OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period, OPT_BOOLEAN(0, "show-total-period", &symbol_conf.show_total_period,
"Show a column with the sum of periods"), "Show a column with the sum of periods"),
OPT_BOOLEAN(0, "group", &symbol_conf.event_group,
"Show event group information together"),
OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "", OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
"use branch records for histogram filling", parse_branch_mode), "use branch records for histogram filling", parse_branch_mode),
OPT_STRING(0, "objdump", &objdump_path, "path", 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,6 +688,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
OPT_END() OPT_END()
}; };
perf_config(perf_report_config, NULL);
argc = parse_options(argc, argv, options, report_usage, 0); argc = parse_options(argc, argv, options, report_usage, 0);
if (report.use_stdio) if (report.use_stdio)
......
...@@ -68,6 +68,8 @@ ...@@ -68,6 +68,8 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/types.h> #include <linux/types.h>
static volatile int done;
static void perf_top__update_print_entries(struct perf_top *top) static void perf_top__update_print_entries(struct perf_top *top)
{ {
if (top->print_entries > 9) if (top->print_entries > 9)
...@@ -431,8 +433,10 @@ static int perf_top__key_mapped(struct perf_top *top, int c) ...@@ -431,8 +433,10 @@ static int perf_top__key_mapped(struct perf_top *top, int c)
return 0; return 0;
} }
static void perf_top__handle_keypress(struct perf_top *top, int c) static bool perf_top__handle_keypress(struct perf_top *top, int c)
{ {
bool ret = true;
if (!perf_top__key_mapped(top, c)) { if (!perf_top__key_mapped(top, c)) {
struct pollfd stdin_poll = { .fd = 0, .events = POLLIN }; struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };
struct termios tc, save; struct termios tc, save;
...@@ -453,7 +457,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) ...@@ -453,7 +457,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
tcsetattr(0, TCSAFLUSH, &save); tcsetattr(0, TCSAFLUSH, &save);
if (!perf_top__key_mapped(top, c)) if (!perf_top__key_mapped(top, c))
return; return ret;
} }
switch (c) { switch (c) {
...@@ -515,7 +519,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) ...@@ -515,7 +519,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
printf("exiting.\n"); printf("exiting.\n");
if (top->dump_symtab) if (top->dump_symtab)
perf_session__fprintf_dsos(top->session, stderr); perf_session__fprintf_dsos(top->session, stderr);
exit(0); ret = false;
break;
case 's': case 's':
perf_top__prompt_symbol(top, "Enter details symbol"); perf_top__prompt_symbol(top, "Enter details symbol");
break; break;
...@@ -538,6 +543,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c) ...@@ -538,6 +543,8 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
default: default:
break; break;
} }
return ret;
} }
static void perf_top__sort_new_samples(void *arg) static void perf_top__sort_new_samples(void *arg)
...@@ -579,8 +586,7 @@ static void *display_thread_tui(void *arg) ...@@ -579,8 +586,7 @@ static void *display_thread_tui(void *arg)
perf_evlist__tui_browse_hists(top->evlist, help, &hbt, perf_evlist__tui_browse_hists(top->evlist, help, &hbt,
&top->session->header.env); &top->session->header.env);
exit_browser(0); done = 1;
exit(0);
return NULL; return NULL;
} }
...@@ -604,7 +610,7 @@ static void *display_thread(void *arg) ...@@ -604,7 +610,7 @@ static void *display_thread(void *arg)
/* trash return*/ /* trash return*/
getc(stdin); getc(stdin);
while (1) { while (!done) {
perf_top__print_sym_table(top); perf_top__print_sym_table(top);
/* /*
* Either timeout expired or we got an EINTR due to SIGWINCH, * Either timeout expired or we got an EINTR due to SIGWINCH,
...@@ -618,15 +624,14 @@ static void *display_thread(void *arg) ...@@ -618,15 +624,14 @@ static void *display_thread(void *arg)
continue; continue;
/* Fall trhu */ /* Fall trhu */
default: default:
goto process_hotkey; c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save);
if (perf_top__handle_keypress(top, c))
goto repeat;
done = 1;
} }
} }
process_hotkey:
c = getc(stdin);
tcsetattr(0, TCSAFLUSH, &save);
perf_top__handle_keypress(top, c);
goto repeat;
return NULL; return NULL;
} }
...@@ -705,7 +710,7 @@ static void perf_event__process_sample(struct perf_tool *tool, ...@@ -705,7 +710,7 @@ static void perf_event__process_sample(struct perf_tool *tool,
} }
if (!machine) { if (!machine) {
pr_err("%u unprocessable samples recorded.\n", pr_err("%u unprocessable samples recorded.\r",
top->session->stats.nr_unprocessable_samples++); top->session->stats.nr_unprocessable_samples++);
return; return;
} }
...@@ -868,7 +873,7 @@ static void perf_top__mmap_read(struct perf_top *top) ...@@ -868,7 +873,7 @@ static void perf_top__mmap_read(struct perf_top *top)
perf_top__mmap_read_idx(top, i); perf_top__mmap_read_idx(top, i);
} }
static void perf_top__start_counters(struct perf_top *top) static int perf_top__start_counters(struct perf_top *top)
{ {
char msg[512]; char msg[512];
struct perf_evsel *counter; struct perf_evsel *counter;
...@@ -900,11 +905,10 @@ static void perf_top__start_counters(struct perf_top *top) ...@@ -900,11 +905,10 @@ static void perf_top__start_counters(struct perf_top *top)
goto out_err; goto out_err;
} }
return; return 0;
out_err: out_err:
exit_browser(0); return -1;
exit(0);
} }
static int perf_top__setup_sample_type(struct perf_top *top) static int perf_top__setup_sample_type(struct perf_top *top)
...@@ -948,7 +952,11 @@ static int __cmd_top(struct perf_top *top) ...@@ -948,7 +952,11 @@ static int __cmd_top(struct perf_top *top)
else else
perf_event__synthesize_threads(&top->tool, perf_event__process, perf_event__synthesize_threads(&top->tool, perf_event__process,
&top->session->machines.host); &top->session->machines.host);
perf_top__start_counters(top);
ret = perf_top__start_counters(top);
if (ret)
goto out_delete;
top->session->evlist = top->evlist; top->session->evlist = top->evlist;
perf_session__set_id_hdr_size(top->session); perf_session__set_id_hdr_size(top->session);
...@@ -968,10 +976,11 @@ static int __cmd_top(struct perf_top *top) ...@@ -968,10 +976,11 @@ static int __cmd_top(struct perf_top *top)
perf_top__mmap_read(top); perf_top__mmap_read(top);
ret = -1;
if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui : if (pthread_create(&thread, NULL, (use_browser > 0 ? display_thread_tui :
display_thread), top)) { display_thread), top)) {
ui__error("Could not create display thread.\n"); ui__error("Could not create display thread.\n");
exit(-1); goto out_delete;
} }
if (top->realtime_prio) { if (top->realtime_prio) {
...@@ -980,11 +989,11 @@ static int __cmd_top(struct perf_top *top) ...@@ -980,11 +989,11 @@ static int __cmd_top(struct perf_top *top)
param.sched_priority = top->realtime_prio; param.sched_priority = top->realtime_prio;
if (sched_setscheduler(0, SCHED_FIFO, &param)) { if (sched_setscheduler(0, SCHED_FIFO, &param)) {
ui__error("Could not set realtime priority.\n"); ui__error("Could not set realtime priority.\n");
exit(-1); goto out_delete;
} }
} }
while (1) { while (!done) {
u64 hits = top->samples; u64 hits = top->samples;
perf_top__mmap_read(top); perf_top__mmap_read(top);
...@@ -993,11 +1002,12 @@ static int __cmd_top(struct perf_top *top) ...@@ -993,11 +1002,12 @@ static int __cmd_top(struct perf_top *top)
ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100); ret = poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
} }
ret = 0;
out_delete: out_delete:
perf_session__delete(top->session); perf_session__delete(top->session);
top->session = NULL; top->session = NULL;
return 0; return ret;
} }
static int static int
...@@ -1154,7 +1164,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused) ...@@ -1154,7 +1164,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
if (!top.evlist->nr_entries && if (!top.evlist->nr_entries &&
perf_evlist__add_default(top.evlist) < 0) { perf_evlist__add_default(top.evlist) < 0) {
ui__error("Not enough memory for event selector list\n"); ui__error("Not enough memory for event selector list\n");
return -ENOMEM; goto out_delete_maps;
} }
symbol_conf.nr_events = top.evlist->nr_entries; 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,7 +1187,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
} else { } else {
ui__error("frequency and count are zero, aborting\n"); ui__error("frequency and count are zero, aborting\n");
status = -EINVAL; status = -EINVAL;
goto out_delete_evlist; goto out_delete_maps;
} }
top.sym_evsel = perf_evlist__first(top.evlist); 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,6 +1220,8 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
status = __cmd_top(&top); status = __cmd_top(&top);
out_delete_maps:
perf_evlist__delete_maps(top.evlist);
out_delete_evlist: out_delete_evlist:
perf_evlist__delete(top.evlist); perf_evlist__delete(top.evlist);
......
...@@ -23,6 +23,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist) ...@@ -23,6 +23,7 @@ static int test__checkevent_tracepoint(struct perf_evlist *evlist)
struct perf_evsel *evsel = perf_evlist__first(evlist); struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type); TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type", TEST_ASSERT_VAL("wrong sample_type",
PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type); PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
...@@ -35,6 +36,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist) ...@@ -35,6 +36,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
struct perf_evsel *evsel; struct perf_evsel *evsel;
TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1); TEST_ASSERT_VAL("wrong number of entries", evlist->nr_entries > 1);
TEST_ASSERT_VAL("wrong number of groups", 0 == evlist->nr_groups);
list_for_each_entry(evsel, &evlist->entries, node) { list_for_each_entry(evsel, &evlist->entries, node) {
TEST_ASSERT_VAL("wrong type", TEST_ASSERT_VAL("wrong type",
...@@ -510,6 +512,7 @@ static int test__group1(struct perf_evlist *evlist) ...@@ -510,6 +512,7 @@ static int test__group1(struct perf_evlist *evlist)
struct perf_evsel *evsel, *leader; struct perf_evsel *evsel, *leader;
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* instructions:k */ /* instructions:k */
evsel = leader = perf_evlist__first(evlist); evsel = leader = perf_evlist__first(evlist);
...@@ -523,6 +526,8 @@ static int test__group1(struct perf_evlist *evlist) ...@@ -523,6 +526,8 @@ static int test__group1(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* cycles:upp */ /* cycles:upp */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -537,6 +542,7 @@ static int test__group1(struct perf_evlist *evlist) ...@@ -537,6 +542,7 @@ static int test__group1(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
return 0; return 0;
} }
...@@ -546,6 +552,7 @@ static int test__group2(struct perf_evlist *evlist) ...@@ -546,6 +552,7 @@ static int test__group2(struct perf_evlist *evlist)
struct perf_evsel *evsel, *leader; struct perf_evsel *evsel, *leader;
TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* faults + :ku modifier */ /* faults + :ku modifier */
evsel = leader = perf_evlist__first(evlist); evsel = leader = perf_evlist__first(evlist);
...@@ -559,6 +566,8 @@ static int test__group2(struct perf_evlist *evlist) ...@@ -559,6 +566,8 @@ static int test__group2(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* cache-references + :u modifier */ /* cache-references + :u modifier */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -572,6 +581,7 @@ static int test__group2(struct perf_evlist *evlist) ...@@ -572,6 +581,7 @@ static int test__group2(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
/* cycles:k */ /* cycles:k */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -594,6 +604,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) ...@@ -594,6 +604,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
struct perf_evsel *evsel, *leader; struct perf_evsel *evsel, *leader;
TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
/* group1 syscalls:sys_enter_open:H */ /* group1 syscalls:sys_enter_open:H */
evsel = leader = perf_evlist__first(evlist); evsel = leader = perf_evlist__first(evlist);
...@@ -610,6 +621,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) ...@@ -610,6 +621,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong group name", TEST_ASSERT_VAL("wrong group name",
!strcmp(leader->group_name, "group1")); !strcmp(leader->group_name, "group1"));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* group1 cycles:kppp */ /* group1 cycles:kppp */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -625,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) ...@@ -625,6 +638,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name); TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
/* group2 cycles + G modifier */ /* group2 cycles + G modifier */
evsel = leader = perf_evsel__next(evsel); evsel = leader = perf_evsel__next(evsel);
...@@ -640,6 +654,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) ...@@ -640,6 +654,8 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong group name", TEST_ASSERT_VAL("wrong group name",
!strcmp(leader->group_name, "group2")); !strcmp(leader->group_name, "group2"));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* group2 1:3 + G modifier */ /* group2 1:3 + G modifier */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -652,6 +668,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused) ...@@ -652,6 +668,7 @@ static int test__group3(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
/* instructions:u */ /* instructions:u */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -674,6 +691,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) ...@@ -674,6 +691,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
struct perf_evsel *evsel, *leader; struct perf_evsel *evsel, *leader;
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 1 == evlist->nr_groups);
/* cycles:u + p */ /* cycles:u + p */
evsel = leader = perf_evlist__first(evlist); evsel = leader = perf_evlist__first(evlist);
...@@ -689,6 +707,8 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) ...@@ -689,6 +707,8 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name); TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* instructions:kp + p */ /* instructions:kp + p */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -703,6 +723,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused) ...@@ -703,6 +723,7 @@ static int test__group4(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2); TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
return 0; return 0;
} }
...@@ -712,6 +733,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) ...@@ -712,6 +733,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
struct perf_evsel *evsel, *leader; struct perf_evsel *evsel, *leader;
TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries); TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong number of groups", 2 == evlist->nr_groups);
/* cycles + G */ /* cycles + G */
evsel = leader = perf_evlist__first(evlist); evsel = leader = perf_evlist__first(evlist);
...@@ -726,6 +748,8 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) ...@@ -726,6 +748,8 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name); TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* instructions + G */ /* instructions + G */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -739,6 +763,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) ...@@ -739,6 +763,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
/* cycles:G */ /* cycles:G */
evsel = leader = perf_evsel__next(evsel); evsel = leader = perf_evsel__next(evsel);
...@@ -753,6 +778,8 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) ...@@ -753,6 +778,8 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong group name", !evsel->group_name); TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel)); TEST_ASSERT_VAL("wrong leader", perf_evsel__is_group_leader(evsel));
TEST_ASSERT_VAL("wrong nr_members", evsel->nr_members == 2);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 0);
/* instructions:G */ /* instructions:G */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
...@@ -766,6 +793,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused) ...@@ -766,6 +793,7 @@ static int test__group5(struct perf_evlist *evlist __maybe_unused)
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host); TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip); TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong leader", evsel->leader == leader); TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
TEST_ASSERT_VAL("wrong group_idx", perf_evsel__group_idx(evsel) == 1);
/* cycles */ /* cycles */
evsel = perf_evsel__next(evsel); evsel = perf_evsel__next(evsel);
......
...@@ -567,23 +567,123 @@ static int hist_browser__show_callchain(struct hist_browser *browser, ...@@ -567,23 +567,123 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
return row - first_row; return row - first_row;
} }
#define HPP__COLOR_FN(_name, _field) \ struct hpp_arg {
static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \ struct ui_browser *b;
struct hist_entry *he) \ char folded_sign;
bool current_entry;
};
static int __hpp__color_callchain(struct hpp_arg *arg)
{
if (!symbol_conf.use_callchain)
return 0;
slsmg_printf("%c ", arg->folded_sign);
return 2;
}
static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
u64 (*get_field)(struct hist_entry *),
int (*callchain_cb)(struct hpp_arg *))
{
int ret = 0;
double percent = 0.0;
struct hists *hists = he->hists;
struct hpp_arg *arg = hpp->ptr;
if (hists->stats.total_period)
percent = 100.0 * get_field(he) / hists->stats.total_period;
ui_browser__set_percent_color(arg->b, percent, arg->current_entry);
if (callchain_cb)
ret += callchain_cb(arg);
ret += scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
slsmg_printf("%s", hpp->buf);
if (symbol_conf.event_group) {
int prev_idx, idx_delta;
struct perf_evsel *evsel = hists_to_evsel(hists);
struct hist_entry *pair;
int nr_members = evsel->nr_members;
if (nr_members <= 1)
goto out;
prev_idx = perf_evsel__group_idx(evsel);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
u64 period = get_field(pair);
u64 total = pair->hists->stats.total_period;
if (!total)
continue;
evsel = hists_to_evsel(pair->hists);
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members in the middle which
* have no sample
*/
ui_browser__set_percent_color(arg->b, 0.0,
arg->current_entry);
ret += scnprintf(hpp->buf, hpp->size,
" %6.2f%%", 0.0);
slsmg_printf("%s", hpp->buf);
}
percent = 100.0 * period / total;
ui_browser__set_percent_color(arg->b, percent,
arg->current_entry);
ret += scnprintf(hpp->buf, hpp->size,
" %6.2f%%", percent);
slsmg_printf("%s", hpp->buf);
prev_idx = perf_evsel__group_idx(evsel);
}
idx_delta = nr_members - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members at last which have no sample
*/
ui_browser__set_percent_color(arg->b, 0.0,
arg->current_entry);
ret += scnprintf(hpp->buf, hpp->size,
" %6.2f%%", 0.0);
slsmg_printf("%s", hpp->buf);
}
}
out:
if (!arg->current_entry || !arg->b->navkeypressed)
ui_browser__set_color(arg->b, HE_COLORSET_NORMAL);
return ret;
}
#define __HPP_COLOR_PERCENT_FN(_type, _field, _cb) \
static u64 __hpp_get_##_field(struct hist_entry *he) \
{ \ { \
struct hists *hists = he->hists; \ return he->stat._field; \
double percent = 100.0 * he->stat._field / hists->stats.total_period; \ } \
*(double *)hpp->ptr = percent; \ \
return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \ static int hist_browser__hpp_color_##_type(struct perf_hpp *hpp, \
struct hist_entry *he) \
{ \
return __hpp__color_fmt(hpp, he, __hpp_get_##_field, _cb); \
} }
HPP__COLOR_FN(overhead, period) __HPP_COLOR_PERCENT_FN(overhead, period, __hpp__color_callchain)
HPP__COLOR_FN(overhead_sys, period_sys) __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys, NULL)
HPP__COLOR_FN(overhead_us, period_us) __HPP_COLOR_PERCENT_FN(overhead_us, period_us, NULL)
HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys, NULL)
HPP__COLOR_FN(overhead_guest_us, period_guest_us) __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us, NULL)
#undef HPP__COLOR_FN #undef __HPP_COLOR_PERCENT_FN
void hist_browser__init_hpp(void) void hist_browser__init_hpp(void)
{ {
...@@ -608,7 +708,6 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -608,7 +708,6 @@ static int hist_browser__show_entry(struct hist_browser *browser,
unsigned short row) unsigned short row)
{ {
char s[256]; char s[256];
double percent;
int printed = 0; int printed = 0;
int width = browser->b.width; int width = browser->b.width;
char folded_sign = ' '; char folded_sign = ' ';
...@@ -628,16 +727,20 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -628,16 +727,20 @@ static int hist_browser__show_entry(struct hist_browser *browser,
} }
if (row_offset == 0) { if (row_offset == 0) {
struct hpp_arg arg = {
.b = &browser->b,
.folded_sign = folded_sign,
.current_entry = current_entry,
};
struct perf_hpp hpp = { struct perf_hpp hpp = {
.buf = s, .buf = s,
.size = sizeof(s), .size = sizeof(s),
.ptr = &arg,
}; };
int i = 0;
ui_browser__gotorc(&browser->b, row, 0); ui_browser__gotorc(&browser->b, row, 0);
perf_hpp__for_each_format(fmt) { perf_hpp__for_each_format(fmt) {
if (!first) { if (!first) {
slsmg_printf(" "); slsmg_printf(" ");
width -= 2; width -= 2;
...@@ -645,27 +748,11 @@ static int hist_browser__show_entry(struct hist_browser *browser, ...@@ -645,27 +748,11 @@ static int hist_browser__show_entry(struct hist_browser *browser,
first = false; first = false;
if (fmt->color) { if (fmt->color) {
hpp.ptr = &percent;
/* It will set percent for us. See HPP__COLOR_FN above. */
width -= fmt->color(&hpp, entry); width -= fmt->color(&hpp, entry);
ui_browser__set_percent_color(&browser->b, percent, current_entry);
if (!i && symbol_conf.use_callchain) {
slsmg_printf("%c ", folded_sign);
width -= 2;
}
slsmg_printf("%s", s);
if (!current_entry || !browser->b.navkeypressed)
ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
} else { } else {
width -= fmt->entry(&hpp, entry); width -= fmt->entry(&hpp, entry);
slsmg_printf("%s", s); slsmg_printf("%s", s);
} }
i++;
} }
/* The scroll bar isn't being used */ /* 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,6 +1189,21 @@ static int hists__browser_title(struct hists *hists, char *bf, size_t size,
const struct thread *thread = hists->thread_filter; const struct thread *thread = hists->thread_filter;
unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE]; unsigned long nr_samples = hists->stats.nr_events[PERF_RECORD_SAMPLE];
u64 nr_events = hists->stats.total_period; u64 nr_events = hists->stats.total_period;
struct perf_evsel *evsel = hists_to_evsel(hists);
char buf[512];
size_t buflen = sizeof(buf);
if (symbol_conf.event_group && evsel->nr_members > 1) {
struct perf_evsel *pos;
perf_evsel__group_desc(evsel, buf, buflen);
ev_name = buf;
for_each_group_member(pos, evsel) {
nr_samples += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
nr_events += pos->hists.stats.total_period;
}
}
nr_samples = convert_unit(nr_samples, &unit); nr_samples = convert_unit(nr_samples, &unit);
printed = scnprintf(bf, size, printed = scnprintf(bf, size,
...@@ -1498,6 +1600,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser, ...@@ -1498,6 +1600,16 @@ static void perf_evsel_menu__write(struct ui_browser *browser,
ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED : ui_browser__set_color(browser, current_entry ? HE_COLORSET_SELECTED :
HE_COLORSET_NORMAL); HE_COLORSET_NORMAL);
if (symbol_conf.event_group && evsel->nr_members > 1) {
struct perf_evsel *pos;
ev_name = perf_evsel__group_name(evsel);
for_each_group_member(pos, evsel) {
nr_events += pos->hists.stats.nr_events[PERF_RECORD_SAMPLE];
}
}
nr_events = convert_unit(nr_events, &unit); nr_events = convert_unit(nr_events, &unit);
printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events, printed = scnprintf(bf, sizeof(bf), "%lu%c%s%s", nr_events,
unit, unit == ' ' ? "" : " ", ev_name); unit, unit == ' ' ? "" : " ", ev_name);
...@@ -1608,8 +1720,19 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu, ...@@ -1608,8 +1720,19 @@ static int perf_evsel_menu__run(struct perf_evsel_menu *menu,
return key; return key;
} }
static bool filter_group_entries(struct ui_browser *self __maybe_unused,
void *entry)
{
struct perf_evsel *evsel = list_entry(entry, struct perf_evsel, node);
if (symbol_conf.event_group && !perf_evsel__is_group_leader(evsel))
return true;
return false;
}
static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
const char *help, int nr_entries, const char *help,
struct hist_browser_timer *hbt, struct hist_browser_timer *hbt,
struct perf_session_env *env) struct perf_session_env *env)
{ {
...@@ -1620,7 +1743,8 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, ...@@ -1620,7 +1743,8 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
.refresh = ui_browser__list_head_refresh, .refresh = ui_browser__list_head_refresh,
.seek = ui_browser__list_head_seek, .seek = ui_browser__list_head_seek,
.write = perf_evsel_menu__write, .write = perf_evsel_menu__write,
.nr_entries = evlist->nr_entries, .filter = filter_group_entries,
.nr_entries = nr_entries,
.priv = evlist, .priv = evlist,
}, },
.env = env, .env = env,
...@@ -1636,20 +1760,37 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist, ...@@ -1636,20 +1760,37 @@ static int __perf_evlist__tui_browse_hists(struct perf_evlist *evlist,
menu.b.width = line_len; menu.b.width = line_len;
} }
return perf_evsel_menu__run(&menu, evlist->nr_entries, help, hbt); return perf_evsel_menu__run(&menu, nr_entries, help, hbt);
} }
int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help, int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
struct hist_browser_timer *hbt, struct hist_browser_timer *hbt,
struct perf_session_env *env) struct perf_session_env *env)
{ {
if (evlist->nr_entries == 1) { int nr_entries = evlist->nr_entries;
single_entry:
if (nr_entries == 1) {
struct perf_evsel *first = list_entry(evlist->entries.next, struct perf_evsel *first = list_entry(evlist->entries.next,
struct perf_evsel, node); struct perf_evsel, node);
const char *ev_name = perf_evsel__name(first); const char *ev_name = perf_evsel__name(first);
return perf_evsel__hists_browse(first, evlist->nr_entries, help,
return perf_evsel__hists_browse(first, nr_entries, help,
ev_name, false, hbt, env); ev_name, false, hbt, env);
} }
return __perf_evlist__tui_browse_hists(evlist, help, hbt, env); if (symbol_conf.event_group) {
struct perf_evsel *pos;
nr_entries = 0;
list_for_each_entry(pos, &evlist->entries, node)
if (perf_evsel__is_group_leader(pos))
nr_entries++;
if (nr_entries == 1)
goto single_entry;
}
return __perf_evlist__tui_browse_hists(evlist, nr_entries, help,
hbt, env);
} }
...@@ -8,32 +8,105 @@ ...@@ -8,32 +8,105 @@
#define MAX_COLUMNS 32 #define MAX_COLUMNS 32
#define HPP__COLOR_FN(_name, _field) \ static int __percent_color_snprintf(char *buf, size_t size, double percent)
static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \ {
struct hist_entry *he) \ int ret = 0;
const char *markup;
markup = perf_gtk__get_percent_color(percent);
if (markup)
ret += scnprintf(buf, size, markup);
ret += scnprintf(buf + ret, size - ret, " %6.2f%%", percent);
if (markup)
ret += scnprintf(buf + ret, size - ret, "</span>");
return ret;
}
static int __hpp__color_fmt(struct perf_hpp *hpp, struct hist_entry *he,
u64 (*get_field)(struct hist_entry *))
{
int ret;
double percent = 0.0;
struct hists *hists = he->hists;
if (hists->stats.total_period)
percent = 100.0 * get_field(he) / hists->stats.total_period;
ret = __percent_color_snprintf(hpp->buf, hpp->size, percent);
if (symbol_conf.event_group) {
int prev_idx, idx_delta;
struct perf_evsel *evsel = hists_to_evsel(hists);
struct hist_entry *pair;
int nr_members = evsel->nr_members;
if (nr_members <= 1)
return ret;
prev_idx = perf_evsel__group_idx(evsel);
list_for_each_entry(pair, &he->pairs.head, pairs.node) {
u64 period = get_field(pair);
u64 total = pair->hists->stats.total_period;
evsel = hists_to_evsel(pair->hists);
idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members in the middle which
* have no sample
*/
ret += __percent_color_snprintf(hpp->buf + ret,
hpp->size - ret,
0.0);
}
percent = 100.0 * period / total;
ret += __percent_color_snprintf(hpp->buf + ret,
hpp->size - ret,
percent);
prev_idx = perf_evsel__group_idx(evsel);
}
idx_delta = nr_members - prev_idx - 1;
while (idx_delta--) {
/*
* zero-fill group members at last which have no sample
*/
ret += __percent_color_snprintf(hpp->buf + ret,
hpp->size - ret,
0.0);
}
}
return ret;
}
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
static u64 he_get_##_field(struct hist_entry *he) \
{ \ { \
struct hists *hists = he->hists; \ return he->stat._field; \
double percent = 100.0 * he->stat._field / hists->stats.total_period; \ } \
const char *markup; \
int ret = 0; \
\ \
markup = perf_gtk__get_percent_color(percent); \ static int perf_gtk__hpp_color_##_type(struct perf_hpp *hpp, \
if (markup) \ struct hist_entry *he) \
ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \ { \
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \ return __hpp__color_fmt(hpp, he, he_get_##_field); \
if (markup) \
ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \
\
return ret; \
} }
HPP__COLOR_FN(overhead, period) __HPP_COLOR_PERCENT_FN(overhead, period)
HPP__COLOR_FN(overhead_sys, period_sys) __HPP_COLOR_PERCENT_FN(overhead_sys, period_sys)
HPP__COLOR_FN(overhead_us, period_us) __HPP_COLOR_PERCENT_FN(overhead_us, period_us)
HPP__COLOR_FN(overhead_guest_sys, period_guest_sys) __HPP_COLOR_PERCENT_FN(overhead_guest_sys, period_guest_sys)
HPP__COLOR_FN(overhead_guest_us, period_guest_us) __HPP_COLOR_PERCENT_FN(overhead_guest_us, period_guest_us)
#undef HPP__COLOR_FN #undef __HPP_COLOR_PERCENT_FN
void perf_gtk__init_hpp(void) void perf_gtk__init_hpp(void)
...@@ -70,6 +143,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) ...@@ -70,6 +143,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
struct perf_hpp hpp = { struct perf_hpp hpp = {
.buf = s, .buf = s,
.size = sizeof(s), .size = sizeof(s),
.ptr = hists_to_evsel(hists),
}; };
nr_cols = 0; nr_cols = 0;
...@@ -96,7 +170,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists) ...@@ -96,7 +170,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
fmt->header(&hpp); fmt->header(&hpp);
gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view), gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
-1, s, -1, ltrim(s),
renderer, "markup", renderer, "markup",
col_idx++, NULL); col_idx++, NULL);
} }
...@@ -196,6 +270,18 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist, ...@@ -196,6 +270,18 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
const char *evname = perf_evsel__name(pos); const char *evname = perf_evsel__name(pos);
GtkWidget *scrolled_window; GtkWidget *scrolled_window;
GtkWidget *tab_label; GtkWidget *tab_label;
char buf[512];
size_t size = sizeof(buf);
if (symbol_conf.event_group) {
if (!perf_evsel__is_group_leader(pos))
continue;
if (pos->nr_members > 1) {
perf_evsel__group_desc(pos, buf, size);
evname = buf;
}
}
scrolled_window = gtk_scrolled_window_new(NULL, NULL); scrolled_window = gtk_scrolled_window_new(NULL, NULL);
......
...@@ -3,151 +3,164 @@ ...@@ -3,151 +3,164 @@
#include "../util/hist.h" #include "../util/hist.h"
#include "../util/util.h" #include "../util/util.h"
#include "../util/sort.h" #include "../util/sort.h"
#include "../util/evsel.h"
/* hist period print (hpp) functions */ /* hist period print (hpp) functions */
static int hpp__header_overhead(struct perf_hpp *hpp)
{
return scnprintf(hpp->buf, hpp->size, "Overhead");
}
static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused)
{
return 8;
}
static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period / hists->stats.total_period;
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
}
static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period / hists->stats.total_period;
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
return scnprintf(hpp->buf, hpp->size, fmt, percent); typedef int (*hpp_snprint_fn)(char *buf, size_t size, const char *fmt, ...);
}
static int hpp__header_overhead_sys(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
return scnprintf(hpp->buf, hpp->size, fmt, "sys");
}
static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused)
{
return 7;
}
static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) static int __hpp__percent_fmt(struct perf_hpp *hpp, struct hist_entry *he,
u64 (*get_field)(struct hist_entry *),
const char *fmt, hpp_snprint_fn print_fn)
{ {
int ret;
double percent = 0.0;
struct hists *hists = he->hists; struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent); if (hists->stats.total_period)
} percent = 100.0 * get_field(he) / hists->stats.total_period;
static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he) ret = print_fn(hpp->buf, hpp->size, fmt, percent);
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_sys / hists->stats.total_period;
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
return scnprintf(hpp->buf, hpp->size, fmt, percent); if (symbol_conf.event_group) {
} int prev_idx, idx_delta;
struct perf_evsel *evsel = hists_to_evsel(hists);
struct hist_entry *pair;
int nr_members = evsel->nr_members;
static int hpp__header_overhead_us(struct perf_hpp *hpp) if (nr_members <= 1)
{ return ret;
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
return scnprintf(hpp->buf, hpp->size, fmt, "user"); prev_idx = perf_evsel__group_idx(evsel);
}
static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused) list_for_each_entry(pair, &he->pairs.head, pairs.node) {
{ u64 period = get_field(pair);
return 7; u64 total = pair->hists->stats.total_period;
}
static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he) if (!total)
{ continue;
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
}
static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_us / hists->stats.total_period;
const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
return scnprintf(hpp->buf, hpp->size, fmt, percent); evsel = hists_to_evsel(pair->hists);
} idx_delta = perf_evsel__group_idx(evsel) - prev_idx - 1;
static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp)
{
return scnprintf(hpp->buf, hpp->size, "guest sys");
}
static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused) while (idx_delta--) {
{ /*
return 9; * zero-fill group members in the middle which
} * have no sample
*/
ret += print_fn(hpp->buf + ret, hpp->size - ret,
fmt, 0.0);
}
static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp, ret += print_fn(hpp->buf + ret, hpp->size - ret,
struct hist_entry *he) fmt, 100.0 * period / total);
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); prev_idx = perf_evsel__group_idx(evsel);
} }
static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp, idx_delta = nr_members - prev_idx - 1;
struct hist_entry *he)
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_guest_sys / hists->stats.total_period;
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
return scnprintf(hpp->buf, hpp->size, fmt, percent); while (idx_delta--) {
/*
* zero-fill group members at last which have no sample
*/
ret += print_fn(hpp->buf + ret, hpp->size - ret,
fmt, 0.0);
}
}
return ret;
} }
static int hpp__header_overhead_guest_us(struct perf_hpp *hpp) static int __hpp__raw_fmt(struct perf_hpp *hpp, struct hist_entry *he,
u64 (*get_field)(struct hist_entry *),
const char *fmt, hpp_snprint_fn print_fn)
{ {
return scnprintf(hpp->buf, hpp->size, "guest usr"); int ret;
}
static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused) ret = print_fn(hpp->buf, hpp->size, fmt, get_field(he));
{ return ret;
return 9;
} }
static int hpp__color_overhead_guest_us(struct perf_hpp *hpp,
struct hist_entry *he)
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent); #define __HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
} static int hpp__header_##_type(struct perf_hpp *hpp) \
{ \
int len = _min_width; \
\
if (symbol_conf.event_group) { \
struct perf_evsel *evsel = hpp->ptr; \
\
len = max(len, evsel->nr_members * _unit_width); \
} \
return scnprintf(hpp->buf, hpp->size, "%*s", len, _str); \
}
#define __HPP_WIDTH_FN(_type, _min_width, _unit_width) \
static int hpp__width_##_type(struct perf_hpp *hpp __maybe_unused) \
{ \
int len = _min_width; \
\
if (symbol_conf.event_group) { \
struct perf_evsel *evsel = hpp->ptr; \
\
len = max(len, evsel->nr_members * _unit_width); \
} \
return len; \
}
#define __HPP_COLOR_PERCENT_FN(_type, _field) \
static u64 he_get_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
} \
\
static int hpp__color_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
{ \
return __hpp__percent_fmt(hpp, he, he_get_##_field, " %6.2f%%", \
(hpp_snprint_fn)percent_color_snprintf); \
}
#define __HPP_ENTRY_PERCENT_FN(_type, _field) \
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
{ \
const char *fmt = symbol_conf.field_sep ? " %.2f" : " %6.2f%%"; \
return __hpp__percent_fmt(hpp, he, he_get_##_field, fmt, \
scnprintf); \
}
#define __HPP_ENTRY_RAW_FN(_type, _field) \
static u64 he_get_raw_##_field(struct hist_entry *he) \
{ \
return he->stat._field; \
} \
\
static int hpp__entry_##_type(struct perf_hpp *hpp, struct hist_entry *he) \
{ \
const char *fmt = symbol_conf.field_sep ? " %"PRIu64 : " %11"PRIu64; \
return __hpp__raw_fmt(hpp, he, he_get_raw_##_field, fmt, scnprintf); \
}
#define HPP_PERCENT_FNS(_type, _str, _field, _min_width, _unit_width) \
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
__HPP_COLOR_PERCENT_FN(_type, _field) \
__HPP_ENTRY_PERCENT_FN(_type, _field)
#define HPP_RAW_FNS(_type, _str, _field, _min_width, _unit_width) \
__HPP_HEADER_FN(_type, _str, _min_width, _unit_width) \
__HPP_WIDTH_FN(_type, _min_width, _unit_width) \
__HPP_ENTRY_RAW_FN(_type, _field)
HPP_PERCENT_FNS(overhead, "Overhead", period, 8, 8)
HPP_PERCENT_FNS(overhead_sys, "sys", period_sys, 8, 8)
HPP_PERCENT_FNS(overhead_us, "usr", period_us, 8, 8)
HPP_PERCENT_FNS(overhead_guest_sys, "guest sys", period_guest_sys, 9, 8)
HPP_PERCENT_FNS(overhead_guest_us, "guest usr", period_guest_us, 9, 8)
HPP_RAW_FNS(samples, "Samples", nr_events, 12, 12)
HPP_RAW_FNS(period, "Period", period, 12, 12)
static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp,
struct hist_entry *he)
{
struct hists *hists = he->hists;
double percent = 100.0 * he->stat.period_guest_us / hists->stats.total_period;
const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
return scnprintf(hpp->buf, hpp->size, fmt, percent);
}
static int hpp__header_baseline(struct perf_hpp *hpp) static int hpp__header_baseline(struct perf_hpp *hpp)
{ {
...@@ -179,7 +192,7 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he) ...@@ -179,7 +192,7 @@ static int hpp__color_baseline(struct perf_hpp *hpp, struct hist_entry *he)
{ {
double percent = baseline_percent(he); double percent = baseline_percent(he);
if (hist_entry__has_pairs(he)) if (hist_entry__has_pairs(he) || symbol_conf.field_sep)
return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent); return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
else else
return scnprintf(hpp->buf, hpp->size, " "); return scnprintf(hpp->buf, hpp->size, " ");
...@@ -196,44 +209,6 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he) ...@@ -196,44 +209,6 @@ static int hpp__entry_baseline(struct perf_hpp *hpp, struct hist_entry *he)
return scnprintf(hpp->buf, hpp->size, " "); return scnprintf(hpp->buf, hpp->size, " ");
} }
static int hpp__header_samples(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%11s";
return scnprintf(hpp->buf, hpp->size, fmt, "Samples");
}
static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused)
{
return 11;
}
static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he)
{
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64;
return scnprintf(hpp->buf, hpp->size, fmt, he->stat.nr_events);
}
static int hpp__header_period(struct perf_hpp *hpp)
{
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
return scnprintf(hpp->buf, hpp->size, fmt, "Period");
}
static int hpp__width_period(struct perf_hpp *hpp __maybe_unused)
{
return 12;
}
static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he)
{
const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
return scnprintf(hpp->buf, hpp->size, fmt, he->stat.period);
}
static int hpp__header_period_baseline(struct perf_hpp *hpp) static int hpp__header_period_baseline(struct perf_hpp *hpp)
{ {
const char *fmt = symbol_conf.field_sep ? "%s" : "%12s"; 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,6 +229,7 @@ static int hpp__entry_period_baseline(struct perf_hpp *hpp, struct hist_entry *h
return scnprintf(hpp->buf, hpp->size, fmt, period); return scnprintf(hpp->buf, hpp->size, fmt, period);
} }
static int hpp__header_delta(struct perf_hpp *hpp) static int hpp__header_delta(struct perf_hpp *hpp)
{ {
const char *fmt = symbol_conf.field_sep ? "%s" : "%7s"; const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
...@@ -408,9 +384,20 @@ struct perf_hpp_fmt perf_hpp__format[] = { ...@@ -408,9 +384,20 @@ struct perf_hpp_fmt perf_hpp__format[] = {
LIST_HEAD(perf_hpp__list); LIST_HEAD(perf_hpp__list);
#undef HPP__COLOR_PRINT_FNS #undef HPP__COLOR_PRINT_FNS
#undef HPP__PRINT_FNS #undef HPP__PRINT_FNS
#undef HPP_PERCENT_FNS
#undef HPP_RAW_FNS
#undef __HPP_HEADER_FN
#undef __HPP_WIDTH_FN
#undef __HPP_COLOR_PERCENT_FN
#undef __HPP_ENTRY_PERCENT_FN
#undef __HPP_ENTRY_RAW_FN
void perf_hpp__init(void) void perf_hpp__init(void)
{ {
if (symbol_conf.show_cpu_utilization) { if (symbol_conf.show_cpu_utilization) {
...@@ -508,12 +495,15 @@ unsigned int hists__sort_list_width(struct hists *hists) ...@@ -508,12 +495,15 @@ unsigned int hists__sort_list_width(struct hists *hists)
struct perf_hpp_fmt *fmt; struct perf_hpp_fmt *fmt;
struct sort_entry *se; struct sort_entry *se;
int i = 0, ret = 0; int i = 0, ret = 0;
struct perf_hpp dummy_hpp = {
.ptr = hists_to_evsel(hists),
};
perf_hpp__for_each_format(fmt) { perf_hpp__for_each_format(fmt) {
if (i) if (i)
ret += 2; ret += 2;
ret += fmt->width(NULL); ret += fmt->width(&dummy_hpp);
} }
list_for_each_entry(se, &hist_entry__sort_list, list) list_for_each_entry(se, &hist_entry__sort_list, list)
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "../../util/util.h" #include "../../util/util.h"
#include "../../util/hist.h" #include "../../util/hist.h"
#include "../../util/sort.h" #include "../../util/sort.h"
#include "../../util/evsel.h"
static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin) static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
...@@ -347,6 +348,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, ...@@ -347,6 +348,7 @@ size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows,
struct perf_hpp dummy_hpp = { struct perf_hpp dummy_hpp = {
.buf = bf, .buf = bf,
.size = sizeof(bf), .size = sizeof(bf),
.ptr = hists_to_evsel(hists),
}; };
bool first = true; bool first = true;
......
...@@ -117,6 +117,9 @@ void __perf_evlist__set_leader(struct list_head *list) ...@@ -117,6 +117,9 @@ void __perf_evlist__set_leader(struct list_head *list)
struct perf_evsel *evsel, *leader; struct perf_evsel *evsel, *leader;
leader = list_entry(list->next, struct perf_evsel, node); leader = list_entry(list->next, struct perf_evsel, node);
evsel = list_entry(list->prev, struct perf_evsel, node);
leader->nr_members = evsel->idx - leader->idx + 1;
list_for_each_entry(evsel, list, node) { list_for_each_entry(evsel, list, node) {
if (evsel != leader) if (evsel != leader)
...@@ -126,8 +129,10 @@ void __perf_evlist__set_leader(struct list_head *list) ...@@ -126,8 +129,10 @@ void __perf_evlist__set_leader(struct list_head *list)
void perf_evlist__set_leader(struct perf_evlist *evlist) void perf_evlist__set_leader(struct perf_evlist *evlist)
{ {
if (evlist->nr_entries) if (evlist->nr_entries) {
evlist->nr_groups = evlist->nr_entries > 1 ? 1 : 0;
__perf_evlist__set_leader(&evlist->entries); __perf_evlist__set_leader(&evlist->entries);
}
} }
int perf_evlist__add_default(struct perf_evlist *evlist) int perf_evlist__add_default(struct perf_evlist *evlist)
......
...@@ -21,6 +21,7 @@ struct perf_evlist { ...@@ -21,6 +21,7 @@ struct perf_evlist {
struct list_head entries; struct list_head entries;
struct hlist_head heads[PERF_EVLIST__HLIST_SIZE]; struct hlist_head heads[PERF_EVLIST__HLIST_SIZE];
int nr_entries; int nr_entries;
int nr_groups;
int nr_fds; int nr_fds;
int nr_mmaps; int nr_mmaps;
int mmap_len; int mmap_len;
......
...@@ -434,6 +434,31 @@ const char *perf_evsel__name(struct perf_evsel *evsel) ...@@ -434,6 +434,31 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
return evsel->name ?: "unknown"; return evsel->name ?: "unknown";
} }
const char *perf_evsel__group_name(struct perf_evsel *evsel)
{
return evsel->group_name ?: "anon group";
}
int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size)
{
int ret;
struct perf_evsel *pos;
const char *group_name = perf_evsel__group_name(evsel);
ret = scnprintf(buf, size, "%s", group_name);
ret += scnprintf(buf + ret, size - ret, " { %s",
perf_evsel__name(evsel));
for_each_group_member(pos, evsel)
ret += scnprintf(buf + ret, size - ret, ", %s",
perf_evsel__name(pos));
ret += scnprintf(buf + ret, size - ret, " }");
return ret;
}
/* /*
* The enable_on_exec/disabled value strategy: * The enable_on_exec/disabled value strategy:
* *
...@@ -1364,7 +1389,27 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, ...@@ -1364,7 +1389,27 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
struct perf_attr_details *details, FILE *fp) struct perf_attr_details *details, FILE *fp)
{ {
bool first = true; bool first = true;
int printed = fprintf(fp, "%s", perf_evsel__name(evsel)); int printed = 0;
if (symbol_conf.event_group) {
struct perf_evsel *pos;
if (!perf_evsel__is_group_leader(evsel))
return 0;
if (evsel->nr_members > 1)
printed += fprintf(fp, "%s{", evsel->group_name ?: "");
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
for_each_group_member(pos, evsel)
printed += fprintf(fp, ",%s", perf_evsel__name(pos));
if (evsel->nr_members > 1)
printed += fprintf(fp, "}");
goto out;
}
printed += fprintf(fp, "%s", perf_evsel__name(evsel));
if (details->verbose || details->freq) { if (details->verbose || details->freq) {
printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64, printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64,
...@@ -1405,7 +1450,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, ...@@ -1405,7 +1450,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
if_print(bp_type); if_print(bp_type);
if_print(branch_sample_type); if_print(branch_sample_type);
} }
out:
fputc('\n', fp); fputc('\n', fp);
return ++printed; return ++printed;
} }
......
...@@ -74,10 +74,13 @@ struct perf_evsel { ...@@ -74,10 +74,13 @@ struct perf_evsel {
bool needs_swap; bool needs_swap;
/* parse modifier helper */ /* parse modifier helper */
int exclude_GH; int exclude_GH;
int nr_members;
struct perf_evsel *leader; struct perf_evsel *leader;
char *group_name; char *group_name;
}; };
#define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
struct cpu_map; struct cpu_map;
struct thread_map; struct thread_map;
struct perf_evlist; struct perf_evlist;
...@@ -111,6 +114,8 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; ...@@ -111,6 +114,8 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size); char *bf, size_t size);
const char *perf_evsel__name(struct perf_evsel *evsel); const char *perf_evsel__name(struct perf_evsel *evsel);
const char *perf_evsel__group_name(struct perf_evsel *evsel);
int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads); int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads);
...@@ -259,4 +264,15 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, ...@@ -259,4 +264,15 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err,
int perf_evsel__open_strerror(struct perf_evsel *evsel, int perf_evsel__open_strerror(struct perf_evsel *evsel,
struct perf_target *target, struct perf_target *target,
int err, char *msg, size_t size); int err, char *msg, size_t size);
static inline int perf_evsel__group_idx(struct perf_evsel *evsel)
{
return evsel->idx - evsel->leader->idx;
}
#define for_each_group_member(_evsel, _leader) \
for ((_evsel) = list_entry((_leader)->node.next, struct perf_evsel, node); \
(_evsel) && (_evsel)->leader == (_leader); \
(_evsel) = list_entry((_evsel)->node.next, struct perf_evsel, node))
#endif /* __PERF_EVSEL_H */ #endif /* __PERF_EVSEL_H */
...@@ -1084,6 +1084,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused, ...@@ -1084,6 +1084,52 @@ static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused,
return 0; return 0;
} }
/*
* File format:
*
* struct group_descs {
* u32 nr_groups;
* struct group_desc {
* char name[];
* u32 leader_idx;
* u32 nr_members;
* }[nr_groups];
* };
*/
static int write_group_desc(int fd, struct perf_header *h __maybe_unused,
struct perf_evlist *evlist)
{
u32 nr_groups = evlist->nr_groups;
struct perf_evsel *evsel;
int ret;
ret = do_write(fd, &nr_groups, sizeof(nr_groups));
if (ret < 0)
return ret;
list_for_each_entry(evsel, &evlist->entries, node) {
if (perf_evsel__is_group_leader(evsel) &&
evsel->nr_members > 1) {
const char *name = evsel->group_name ?: "{anon_group}";
u32 leader_idx = evsel->idx;
u32 nr_members = evsel->nr_members;
ret = do_write_string(fd, name);
if (ret < 0)
return ret;
ret = do_write(fd, &leader_idx, sizeof(leader_idx));
if (ret < 0)
return ret;
ret = do_write(fd, &nr_members, sizeof(nr_members));
if (ret < 0)
return ret;
}
}
return 0;
}
/* /*
* default get_cpuid(): nothing gets recorded * default get_cpuid(): nothing gets recorded
* actual implementation must be in arch/$(ARCH)/util/header.c * actual implementation must be in arch/$(ARCH)/util/header.c
...@@ -1447,6 +1493,31 @@ static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused, ...@@ -1447,6 +1493,31 @@ static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
fprintf(fp, "# pmu mappings: unable to read\n"); fprintf(fp, "# pmu mappings: unable to read\n");
} }
static void print_group_desc(struct perf_header *ph, int fd __maybe_unused,
FILE *fp)
{
struct perf_session *session;
struct perf_evsel *evsel;
u32 nr = 0;
session = container_of(ph, struct perf_session, header);
list_for_each_entry(evsel, &session->evlist->entries, node) {
if (perf_evsel__is_group_leader(evsel) &&
evsel->nr_members > 1) {
fprintf(fp, "# group: %s{%s", evsel->group_name ?: "",
perf_evsel__name(evsel));
nr = evsel->nr_members - 1;
} else if (nr) {
fprintf(fp, ",%s", perf_evsel__name(evsel));
if (--nr == 0)
fprintf(fp, "}\n");
}
}
}
static int __event_process_build_id(struct build_id_event *bev, static int __event_process_build_id(struct build_id_event *bev,
char *filename, char *filename,
struct perf_session *session) struct perf_session *session)
...@@ -1961,6 +2032,98 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused ...@@ -1961,6 +2032,98 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused
return -1; return -1;
} }
static int process_group_desc(struct perf_file_section *section __maybe_unused,
struct perf_header *ph, int fd,
void *data __maybe_unused)
{
size_t ret = -1;
u32 i, nr, nr_groups;
struct perf_session *session;
struct perf_evsel *evsel, *leader = NULL;
struct group_desc {
char *name;
u32 leader_idx;
u32 nr_members;
} *desc;
if (readn(fd, &nr_groups, sizeof(nr_groups)) != sizeof(nr_groups))
return -1;
if (ph->needs_swap)
nr_groups = bswap_32(nr_groups);
ph->env.nr_groups = nr_groups;
if (!nr_groups) {
pr_debug("group desc not available\n");
return 0;
}
desc = calloc(nr_groups, sizeof(*desc));
if (!desc)
return -1;
for (i = 0; i < nr_groups; i++) {
desc[i].name = do_read_string(fd, ph);
if (!desc[i].name)
goto out_free;
if (readn(fd, &desc[i].leader_idx, sizeof(u32)) != sizeof(u32))
goto out_free;
if (readn(fd, &desc[i].nr_members, sizeof(u32)) != sizeof(u32))
goto out_free;
if (ph->needs_swap) {
desc[i].leader_idx = bswap_32(desc[i].leader_idx);
desc[i].nr_members = bswap_32(desc[i].nr_members);
}
}
/*
* Rebuild group relationship based on the group_desc
*/
session = container_of(ph, struct perf_session, header);
session->evlist->nr_groups = nr_groups;
i = nr = 0;
list_for_each_entry(evsel, &session->evlist->entries, node) {
if (evsel->idx == (int) desc[i].leader_idx) {
evsel->leader = evsel;
/* {anon_group} is a dummy name */
if (strcmp(desc[i].name, "{anon_group}"))
evsel->group_name = desc[i].name;
evsel->nr_members = desc[i].nr_members;
if (i >= nr_groups || nr > 0) {
pr_debug("invalid group desc\n");
goto out_free;
}
leader = evsel;
nr = evsel->nr_members - 1;
i++;
} else if (nr) {
/* This is a group member */
evsel->leader = leader;
nr--;
}
}
if (i != nr_groups || nr != 0) {
pr_debug("invalid group desc\n");
goto out_free;
}
ret = 0;
out_free:
while ((int) --i >= 0)
free(desc[i].name);
free(desc);
return ret;
}
struct feature_ops { struct feature_ops {
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist); int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
void (*print)(struct perf_header *h, int fd, FILE *fp); 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,6 +2163,7 @@ static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology), FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
FEAT_OPA(HEADER_BRANCH_STACK, branch_stack), FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings), FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
FEAT_OPP(HEADER_GROUP_DESC, group_desc),
}; };
struct header_print_data { struct header_print_data {
......
...@@ -29,6 +29,7 @@ enum { ...@@ -29,6 +29,7 @@ enum {
HEADER_NUMA_TOPOLOGY, HEADER_NUMA_TOPOLOGY,
HEADER_BRANCH_STACK, HEADER_BRANCH_STACK,
HEADER_PMU_MAPPINGS, HEADER_PMU_MAPPINGS,
HEADER_GROUP_DESC,
HEADER_LAST_FEATURE, HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256, HEADER_FEAT_BITS = 256,
}; };
...@@ -79,6 +80,7 @@ struct perf_session_env { ...@@ -79,6 +80,7 @@ struct perf_session_env {
char *numa_nodes; char *numa_nodes;
int nr_pmu_mappings; int nr_pmu_mappings;
char *pmu_mappings; char *pmu_mappings;
int nr_groups;
}; };
struct perf_header { struct perf_header {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "hist.h" #include "hist.h"
#include "session.h" #include "session.h"
#include "sort.h" #include "sort.h"
#include "evsel.h"
#include <math.h> #include <math.h>
static bool hists__filter_entry_by_dso(struct hists *hists, static bool hists__filter_entry_by_dso(struct hists *hists,
...@@ -540,6 +541,62 @@ void hists__collapse_resort_threaded(struct hists *hists) ...@@ -540,6 +541,62 @@ void hists__collapse_resort_threaded(struct hists *hists)
* reverse the map, sort on period. * reverse the map, sort on period.
*/ */
static int period_cmp(u64 period_a, u64 period_b)
{
if (period_a > period_b)
return 1;
if (period_a < period_b)
return -1;
return 0;
}
static int hist_entry__sort_on_period(struct hist_entry *a,
struct hist_entry *b)
{
int ret;
int i, nr_members;
struct perf_evsel *evsel;
struct hist_entry *pair;
u64 *periods_a, *periods_b;
ret = period_cmp(a->stat.period, b->stat.period);
if (ret || !symbol_conf.event_group)
return ret;
evsel = hists_to_evsel(a->hists);
nr_members = evsel->nr_members;
if (nr_members <= 1)
return ret;
periods_a = zalloc(sizeof(periods_a) * nr_members);
periods_b = zalloc(sizeof(periods_b) * nr_members);
if (!periods_a || !periods_b)
goto out;
list_for_each_entry(pair, &a->pairs.head, pairs.node) {
evsel = hists_to_evsel(pair->hists);
periods_a[perf_evsel__group_idx(evsel)] = pair->stat.period;
}
list_for_each_entry(pair, &b->pairs.head, pairs.node) {
evsel = hists_to_evsel(pair->hists);
periods_b[perf_evsel__group_idx(evsel)] = pair->stat.period;
}
for (i = 1; i < nr_members; i++) {
ret = period_cmp(periods_a[i], periods_b[i]);
if (ret)
break;
}
out:
free(periods_a);
free(periods_b);
return ret;
}
static void __hists__insert_output_entry(struct rb_root *entries, static void __hists__insert_output_entry(struct rb_root *entries,
struct hist_entry *he, struct hist_entry *he,
u64 min_callchain_hits) u64 min_callchain_hits)
...@@ -556,7 +613,7 @@ static void __hists__insert_output_entry(struct rb_root *entries, ...@@ -556,7 +613,7 @@ static void __hists__insert_output_entry(struct rb_root *entries,
parent = *p; parent = *p;
iter = rb_entry(parent, struct hist_entry, rb_node); iter = rb_entry(parent, struct hist_entry, rb_node);
if (he->stat.period > iter->stat.period) if (hist_entry__sort_on_period(he, iter) > 0)
p = &(*p)->rb_left; p = &(*p)->rb_left;
else else
p = &(*p)->rb_right; p = &(*p)->rb_right;
......
...@@ -884,6 +884,7 @@ int parse_events(struct perf_evlist *evlist, const char *str) ...@@ -884,6 +884,7 @@ int parse_events(struct perf_evlist *evlist, const char *str)
if (!ret) { if (!ret) {
int entries = data.idx - evlist->nr_entries; int entries = data.idx - evlist->nr_entries;
perf_evlist__splice_list_tail(evlist, &data.list, entries); perf_evlist__splice_list_tail(evlist, &data.list, entries);
evlist->nr_groups += data.nr_groups;
return 0; return 0;
} }
......
...@@ -64,6 +64,7 @@ struct parse_events_term { ...@@ -64,6 +64,7 @@ struct parse_events_term {
struct parse_events_evlist { struct parse_events_evlist {
struct list_head list; struct list_head list;
int idx; int idx;
int nr_groups;
}; };
struct parse_events_terms { struct parse_events_terms {
......
...@@ -23,6 +23,14 @@ do { \ ...@@ -23,6 +23,14 @@ do { \
YYABORT; \ YYABORT; \
} while (0) } while (0)
static inc_group_count(struct list_head *list,
struct parse_events_evlist *data)
{
/* Count groups only have more than 1 members */
if (!list_is_last(list->next, list))
data->nr_groups++;
}
%} %}
%token PE_START_EVENTS PE_START_TERMS %token PE_START_EVENTS PE_START_TERMS
...@@ -123,6 +131,7 @@ PE_NAME '{' events '}' ...@@ -123,6 +131,7 @@ PE_NAME '{' events '}'
{ {
struct list_head *list = $3; struct list_head *list = $3;
inc_group_count(list, _data);
parse_events__set_leader($1, list); parse_events__set_leader($1, list);
$$ = list; $$ = list;
} }
...@@ -131,6 +140,7 @@ PE_NAME '{' events '}' ...@@ -131,6 +140,7 @@ PE_NAME '{' events '}'
{ {
struct list_head *list = $2; struct list_head *list = $2;
inc_group_count(list, _data);
parse_events__set_leader(NULL, list); parse_events__set_leader(NULL, list);
$$ = list; $$ = list;
} }
......
...@@ -96,7 +96,8 @@ struct symbol_conf { ...@@ -96,7 +96,8 @@ struct symbol_conf {
initialized, initialized,
kptr_restrict, kptr_restrict,
annotate_asm_raw, annotate_asm_raw,
annotate_src; annotate_src,
event_group;
const char *vmlinux_name, const char *vmlinux_name,
*kallsyms_name, *kallsyms_name,
*source_prefix, *source_prefix,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment