[PATCH] perf: Add support for group events

From: Matt Fleming
Date: Tue May 31 2011 - 06:19:01 EST


Allow events to be grouped so that they are scheduled together on the
performance hardware.

Signed-off-by: Matt Fleming <matt.fleming@xxxxxxxxxxxxxxx>
---
tools/perf/builtin-record.c | 17 ++++++++++-----
tools/perf/util/evlist.c | 23 +++++++++++++++++---
tools/perf/util/evsel.c | 3 ++
tools/perf/util/evsel.h | 2 +
tools/perf/util/parse-events.c | 43 +++++++++++++++++++++++++++++++++++++++-
5 files changed, 77 insertions(+), 11 deletions(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 8e2c857..4c9412b 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -163,10 +163,11 @@ static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist)
struct perf_event_attr *attr = &evsel->attr;
int track = !evsel->idx; /* only the first counter needs these */

- attr->inherit = !no_inherit;
- attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
- PERF_FORMAT_TOTAL_TIME_RUNNING |
- PERF_FORMAT_ID;
+ attr->inherit = !no_inherit &&
+ (!(attr->read_format & PERF_FORMAT_GROUP));
+ attr->read_format |= PERF_FORMAT_TOTAL_TIME_ENABLED |
+ PERF_FORMAT_TOTAL_TIME_RUNNING |
+ PERF_FORMAT_ID;

attr->sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID;

@@ -176,9 +177,13 @@ static void config_attr(struct perf_evsel *evsel, struct perf_evlist *evlist)
/*
* We default some events to a 1 default interval. But keep
* it a weak assumption overridable by the user.
+ *
+ * We only allow the default to be overridden if the event is
+ * not part of a group, or if the event is the leader of a group.
*/
- if (!attr->sample_period || (user_freq != UINT_MAX &&
- user_interval != ULLONG_MAX)) {
+ if ((!evsel->group || (evsel->group == evsel)) &&
+ (!attr->sample_period || (user_freq != UINT_MAX &&
+ user_interval != ULLONG_MAX))) {
if (freq) {
attr->sample_type |= PERF_SAMPLE_PERIOD;
attr->freq = 1;
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 50aa348..a7f0f8c 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -133,17 +133,31 @@ static int perf_evlist__id_add_fd(struct perf_evlist *evlist,
{
u64 read_data[4] = { 0, };
int id_idx = 1; /* The first entry is the counter value */
+ size_t data_sz;
+ u64 *data;
+
+ data_sz = sizeof(u64) * 4;
+
+ if (evsel->group == evsel) {
+ data_sz += evsel->group_cnt * sizeof(u64) * 4;
+ data = malloc(data_sz);
+ if (!data)
+ return -1;
+ } else
+ data = read_data;

if (!(evsel->attr.read_format & PERF_FORMAT_ID) ||
- read(fd, &read_data, sizeof(read_data)) == -1)
+ read(fd, data, data_sz) == -1)
return -1;

if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
++id_idx;
if (evsel->attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
++id_idx;
+ if (evsel->group_cnt)
+ ++id_idx;

- perf_evlist__id_add(evlist, evsel, cpu, thread, read_data[id_idx]);
+ perf_evlist__id_add(evlist, evsel, cpu, thread, data[id_idx]);
return 0;
}

@@ -466,9 +480,10 @@ u64 perf_evlist__sample_type(struct perf_evlist *evlist)
u64 type = 0;

list_for_each_entry(pos, &evlist->entries, node) {
+ u64 mask = PERF_SAMPLE_PERIOD | PERF_SAMPLE_READ;
if (!type)
- type = pos->attr.sample_type;
- else if (type != pos->attr.sample_type)
+ type = (pos->attr.sample_type & ~mask);
+ else if (type != (pos->attr.sample_type & ~mask))
die("non matching sample_type");
}

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index cca29ed..53960e1 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -208,6 +208,9 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
if (!evsel->cgrp)
pid = threads->map[thread];

+ if (evsel->group && evsel->group != evsel)
+ group_fd = FD(evsel->group, cpu, thread);
+
FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
pid,
cpus->map[cpu],
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index f79bb2c..14be4b9 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -61,6 +61,8 @@ struct perf_evsel {
off_t id_offset;
};
struct cgroup_sel *cgrp;
+ struct perf_evsel *group;
+ u64 group_cnt;
};

struct cpu_map;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 41982c3..60a3479 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -737,6 +737,9 @@ parse_event_modifier(const char **strp, struct perf_event_attr *attr)
if (*str == ',')
return 0;

+ if (*str == '}')
+ return 0;
+
if (*str++ != ':')
return -1;

@@ -825,18 +828,44 @@ modifier:
int parse_events(const struct option *opt, const char *str, int unset __used)
{
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+ struct perf_evsel *group_evsel = NULL;
struct perf_event_attr attr;
enum event_result ret;
const char *ostr;
+ u64 group_cnt = 0;

for (;;) {
ostr = str;
memset(&attr, 0, sizeof(attr));
+
+ if (*str == '{') {
+ /* Already parsing a group? */
+ if (group_evsel != NULL)
+ return -1;
+
+ /* Create an event group */
+ attr.config = PERF_COUNT_SW_TASK_CLOCK;
+ attr.type = PERF_TYPE_SOFTWARE;
+ attr.read_format = PERF_FORMAT_GROUP |
+ PERF_FORMAT_TOTAL_TIME_RUNNING |
+ PERF_FORMAT_TOTAL_TIME_ENABLED;
+ attr.sample_type = PERF_SAMPLE_READ;
+
+ group_evsel = perf_evsel__new(&attr, evlist->nr_entries);
+ if (group_evsel == NULL)
+ return -1;
+
+ group_evsel->group = group_evsel;
+ perf_evlist__add(evlist, group_evsel);
+ str++;
+ }
+
+ memset(&attr, 0, sizeof(attr));
ret = parse_event_symbols(opt, &str, &attr);
if (ret == EVT_FAILED)
return -1;

- if (!(*str == 0 || *str == ',' || isspace(*str)))
+ if (!(*str == 0 || *str == ',' || isspace(*str) || *str == '}'))
return -1;

if (ret != EVT_HANDLED_ALL) {
@@ -844,12 +873,24 @@ int parse_events(const struct option *opt, const char *str, int unset __used)
evsel = perf_evsel__new(&attr, evlist->nr_entries);
if (evsel == NULL)
return -1;
+
+ if (group_evsel)
+ evsel->group = group_evsel;
+
perf_evlist__add(evlist, evsel);

evsel->name = calloc(str - ostr + 1, 1);
if (!evsel->name)
return -1;
strncpy(evsel->name, ostr, str - ostr);
+ group_cnt++;
+ }
+
+ if (*str == '}') {
+ group_evsel->group_cnt = group_cnt;
+ group_evsel = NULL;
+ group_cnt = 0;
+ str++;
}

if (*str == 0)
--
1.7.4.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/