[RFC PATCH 3/4] perf: Support for starter and stopper in tools

From: Frederic Weisbecker
Date: Mon Mar 14 2011 - 15:18:26 EST


Add --starter and --stopper options in perf record
and perf stat. These options must follow the event
that wants to be the target of the starter/stopper and
must be followed by the index of the desired event in the
command line, starting from 0.

For example in:

perf record -e irq:softirq_entry -e irq:softirq_exit \
-e lock:lock_acquire -a

If we want to count/sample lock acquire event only outside softirqs,
we will take softirq_exit as the starter and softirq_entry as the
stopper. Thus the stopper is the event 0 and the starter is the
event 1, which leads us to the given command line:

perf record -e irq:softirq_entry -e irq:softirq_exit \
-e lock:lock_acquire --starter 1 --stopper 0 -a

Signed-off-by: Frederic Weisbecker <fweisbec@xxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxx>
Cc: Peter Zijlstra <a.p.zijlstra@xxxxxxxxx>
Cc: Arnaldo Carvalho de Melo <acme@xxxxxxxxxx>
Cc: Paul Mackerras <paulus@xxxxxxxxx>
Cc: Stephane Eranian <eranian@xxxxxxxxxx>
Cc: Steven Rostedt <rostedt@xxxxxxxxxxx>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@xxxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Hitoshi Mitake <mitake@xxxxxxxxxxxxxxxxxxxxx>
---
tools/perf/builtin-record.c | 18 +++++++++++-
tools/perf/builtin-stat.c | 20 ++++++++++++-
tools/perf/util/evlist.c | 12 +++-----
tools/perf/util/evlist.h | 4 +-
tools/perf/util/evsel.c | 53 ++++++++++++++++++++++++++++++++++++
tools/perf/util/evsel.h | 13 +++++++++
tools/perf/util/parse-events.c | 58 ++++++++++++++++++++++++++++++++++++++++
tools/perf/util/parse-events.h | 2 +
8 files changed, 167 insertions(+), 13 deletions(-)

diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 6febcc1..7cc690e 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -323,12 +323,24 @@ try_again:
}
}

- if (perf_evlist__set_filters(evlist)) {
+ if (perf_evlist__for_each_evsel(evlist, perf_evsel__set_filter)) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
exit(-1);
}

+ if (perf_evlist__for_each_evsel(evlist, perf_evsel__set_starter)) {
+ error("failed to set starter with %d (%s)\n", errno,
+ strerror(errno));
+ exit(-1);
+ }
+
+ if (perf_evlist__for_each_evsel(evlist, perf_evsel__set_stopper)) {
+ error("failed to set stopper with %d (%s)\n", errno,
+ strerror(errno));
+ exit(-1);
+ }
+
if (perf_evlist__mmap(evlist, mmap_pages, false) < 0)
die("failed to mmap with %d (%s)\n", errno, strerror(errno));

@@ -729,6 +741,10 @@ const struct option record_options[] = {
parse_events),
OPT_CALLBACK(0, "filter", &evsel_list, "filter",
"event filter", parse_filter),
+ OPT_CALLBACK(0, "starter", &evsel_list, "starter",
+ "event starter", parse_starter),
+ OPT_CALLBACK(0, "stopper", &evsel_list, "stopper",
+ "event stopper", parse_stopper),
OPT_INTEGER('p', "pid", &target_pid,
"record events on existing process id"),
OPT_INTEGER('t', "tid", &target_tid,
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index e2109f9..ba89a4d 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -333,8 +333,20 @@ static int run_perf_stat(int argc __used, const char **argv)
}
}

- if (perf_evlist__set_filters(evsel_list)) {
- error("failed to set filter with %d (%s)\n", errno,
+ if (perf_evlist__for_each_evsel(evsel_list, perf_evsel__set_filter)) {
+ pr_err("failed to set filter with %d (%s)\n", errno,
+ strerror(errno));
+ return -1;
+ }
+
+ if (perf_evlist__for_each_evsel(evsel_list, perf_evsel__set_starter)) {
+ pr_err("failed to set starter with %d (%s)\n", errno,
+ strerror(errno));
+ return -1;
+ }
+
+ if (perf_evlist__for_each_evsel(evsel_list, perf_evsel__set_stopper)) {
+ pr_err("failed to set stopper with %d (%s)\n", errno,
strerror(errno));
return -1;
}
@@ -642,6 +654,10 @@ static const struct option options[] = {
parse_events),
OPT_CALLBACK(0, "filter", &evsel_list, "filter",
"event filter", parse_filter),
+ OPT_CALLBACK(0, "starter", &evsel_list, "starter",
+ "event starter", parse_starter),
+ OPT_CALLBACK(0, "stopper", &evsel_list, "stopper",
+ "event stopper", parse_stopper),
OPT_BOOLEAN('i', "no-inherit", &no_inherit,
"child tasks do not inherit counters"),
OPT_INTEGER('p', "pid", &target_pid,
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index d852cef..b70eb92 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -365,27 +365,23 @@ void perf_evlist__delete_maps(struct perf_evlist *evlist)
evlist->threads = NULL;
}

-int perf_evlist__set_filters(struct perf_evlist *evlist)
+int perf_evlist__for_each_evsel(struct perf_evlist *evlist,
+ int (*call)(struct perf_evsel *, int, int))
{
const struct thread_map *threads = evlist->threads;
const struct cpu_map *cpus = evlist->cpus;
struct perf_evsel *evsel;
- char *filter;
int thread;
int cpu;
int err;
- int fd;

list_for_each_entry(evsel, &evlist->entries, node) {
- filter = evsel->filter;
- if (!filter)
- continue;
for (cpu = 0; cpu < cpus->nr; cpu++) {
for (thread = 0; thread < threads->nr; thread++) {
- fd = FD(evsel, cpu, thread);
- err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
+ err = call(evsel, cpu, thread);
if (err)
return err;
+
}
}
}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 8b1cb7a..1262e15 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -63,6 +63,6 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
pid_t target_tid, const char *cpu_list);
void perf_evlist__delete_maps(struct perf_evlist *evlist);
-int perf_evlist__set_filters(struct perf_evlist *evlist);
-
+int perf_evlist__for_each_evsel(struct perf_evlist *evlist,
+ int (*call)(struct perf_evsel *, int, int));
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 662596a..7b2ba9f 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -21,6 +21,8 @@ void perf_evsel__init(struct perf_evsel *evsel,
evsel->idx = idx;
evsel->attr = *attr;
INIT_LIST_HEAD(&evsel->node);
+ INIT_LIST_HEAD(&evsel->starter_list);
+ INIT_LIST_HEAD(&evsel->stopper_list);
}

struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
@@ -395,3 +397,54 @@ int perf_event__parse_sample(const union perf_event *event, u64 type,

return 0;
}
+
+int perf_evsel__set_filter(struct perf_evsel *evsel, int cpu,
+ int thread)
+{
+ char *filter;
+ int fd;
+
+ filter = evsel->filter;
+ if (!filter)
+ return 0;
+
+ fd = FD(evsel, cpu, thread);
+
+ return ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
+}
+
+int perf_evsel__set_starter(struct perf_evsel *evsel, int cpu,
+ int thread)
+{
+ struct perf_evsel *target;
+ int fd, fd_target;
+ int ret = 0;
+
+ list_for_each_entry(target, &evsel->starter_list, starter_entry) {
+ fd = FD(evsel, cpu, thread);
+ fd_target = FD(target, cpu, thread);
+ ret = ioctl(fd, PERF_EVENT_IOC_SET_STARTER, fd_target);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+int perf_evsel__set_stopper(struct perf_evsel *evsel, int cpu,
+ int thread)
+{
+ struct perf_evsel *target;
+ int fd, fd_target;
+ int ret = 0;
+
+ list_for_each_entry(target, &evsel->stopper_list, stopper_entry) {
+ fd = FD(evsel, cpu, thread);
+ fd_target = FD(target, cpu, thread);
+ ret = ioctl(fd, PERF_EVENT_IOC_SET_STOPPER, fd_target);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 6710ab5..e35d01f 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -61,6 +61,10 @@ struct perf_evsel {
off_t id_offset;
};
struct cgroup_sel *cgrp;
+ struct list_head starter_list;
+ struct list_head starter_entry;
+ struct list_head stopper_list;
+ struct list_head stopper_entry;
};

struct cpu_map;
@@ -123,6 +127,15 @@ static inline int perf_evsel__read_on_cpu_scaled(struct perf_evsel *evsel,
int __perf_evsel__read(struct perf_evsel *evsel, int ncpus, int nthreads,
bool scale);

+int perf_evsel__set_filter(struct perf_evsel *evsel, int cpu,
+ int thread);
+
+int perf_evsel__set_starter(struct perf_evsel *evsel, int cpu,
+ int thread);
+
+int perf_evsel__set_stopper(struct perf_evsel *evsel, int cpu,
+ int thread);
+
/**
* perf_evsel__read - Read the aggregate results on all CPUs
*
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 54a7e26..7dd494f 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -845,6 +845,64 @@ int parse_filter(const struct option *opt, const char *str,
return 0;
}

+static int parse_starter_stopper(const struct option *opt,
+ const char *str, int starter)
+{
+ struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
+ struct perf_evsel *last = NULL, *trigger = NULL;
+ char *end;
+ unsigned long i = 0;
+ int found = 0;
+ unsigned long idx;
+
+ if (evlist->nr_entries > 0)
+ last = list_entry(evlist->entries.prev, struct perf_evsel, node);
+
+ if (last == NULL) {
+ fprintf(stderr, "--starter/--stopper options should follow a -e tracepoint option\n");
+ return -1;
+ }
+
+ idx = strtoul(str, &end, 10);
+ if (str == end) {
+ //FIXME: Clarify that message, and fix above error handling
+ fprintf(stderr, "--starter/--stopper options should be followed by an event index\n");
+ return -1;
+ }
+
+ list_for_each_entry(trigger, &evlist->entries, node) {
+ if (i++ == idx) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ fprintf(stderr, "--starter/--stopper should be followed by a number "
+ "matching the nth event from the command line\n");
+ return -1;
+ }
+
+ if (starter)
+ list_add_tail(&last->starter_entry, &trigger->starter_list);
+ else
+ list_add_tail(&last->stopper_entry, &trigger->stopper_list);
+
+ return 0;
+}
+
+int parse_starter(const struct option *opt, const char *str,
+ int unset __used)
+{
+ return parse_starter_stopper(opt, str, 1);
+}
+
+int parse_stopper(const struct option *opt, const char *str,
+ int unset __used)
+{
+ return parse_starter_stopper(opt, str, 0);
+}
+
static const char * const event_type_descriptors[] = {
"Hardware event",
"Software event",
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index 212f88e..72d73b5 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -25,6 +25,8 @@ extern const char *__event_name(int type, u64 config);

extern int parse_events(const struct option *opt, const char *str, int unset);
extern int parse_filter(const struct option *opt, const char *str, int unset);
+extern int parse_starter(const struct option *opt, const char *str, int unset);
+extern int parse_stopper(const struct option *opt, const char *str, int unset);

#define EVENTS_HELP_MAX (128*1024)

--
1.7.3.2

--
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/