perf tools: Add record/stat support for toggling events Signed-off-by: Jiri Olsa <jolsa@redhat.com> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Corey Ashford <cjashfor@linux.vnet.ibm.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Ingo Molnar <mingo@elte.hu> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a41ac415..b3cf903 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c
@@ -226,6 +226,13 @@ goto out; } + if (perf_evlist__apply_toggle(evlist)) { + error("failed to set toggling %d (%s)\n", errno, + strerror(errno)); + rc = -1; + goto out; + } + if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) { if (errno == EPERM) { pr_err("Permission error mapping pages.\n"
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index f686d5f..5d5db81 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c
@@ -462,6 +462,8 @@ if (group) perf_evlist__set_leader(evsel_list); + perf_evlist__mark_toggled(evsel_list); + list_for_each_entry(counter, &evsel_list->entries, node) { if (create_perf_stat_counter(counter) < 0) { /* @@ -496,6 +498,12 @@ return -1; } + if (perf_evlist__apply_toggle(evsel_list)) { + error("failed to set toggling with %d (%s)\n", errno, + strerror(errno)); + return -1; + } + /* * Enable counters and exec the command: */
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 5df4ca9..ab42be1 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c
@@ -16,6 +16,7 @@ #include "evsel.h" #include "debug.h" #include <unistd.h> +#include "asm/bug.h" #include "parse-events.h" @@ -706,6 +707,85 @@ evlist->threads = NULL; } +static struct perf_evsel * +perf_evlist__find_evsel_by_name(struct perf_evlist *evlist, char *name) +{ + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) + if (strstr(perf_evsel__name(evsel), name)) + return evsel; + + return NULL; +} + +static int apply_toggle(struct perf_evsel *evsel, struct perf_evsel *toggled, + int ncpus, int nthreads) +{ + int cpu, thread, err = 0; + + for (cpu = 0; cpu < ncpus; cpu++) { + for (thread = 0; thread < nthreads; thread++) { + int fd = FD(evsel, cpu, thread); + u64 args[2] = { + FD(toggled, cpu, thread), + evsel->toggle_flag + }; + + err = ioctl(fd, PERF_EVENT_IOC_SET_TOGGLE, args); + if (err) + break; + } + } + + return err; +} + +int perf_evlist__apply_toggle(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + int err = 0; + const int ncpus = cpu_map__nr(evlist->cpus), + nthreads = thread_map__nr(evlist->threads); + + list_for_each_entry(evsel, &evlist->entries, node) { + struct perf_evsel *toggled; + + if (!evsel->toggle_flag) + continue; + + toggled = perf_evlist__find_evsel_by_name(evlist, + evsel->toggle_name); + if (WARN_ONCE(!toggled, "toggled support internal error\n")) + continue; + + err = apply_toggle(evsel, toggled, ncpus, nthreads); + if (err) + break; + } + + return err; +} + +void perf_evlist__mark_toggled(struct perf_evlist *evlist) +{ + struct perf_evsel *evsel; + + list_for_each_entry(evsel, &evlist->entries, node) { + struct perf_evsel *toggled; + + if (!evsel->toggle_flag) + continue; + + toggled = perf_evlist__find_evsel_by_name(evlist, + evsel->toggle_name); + if (WARN_ONCE(!toggled, "toggled support internal error\n")) + continue; + + toggled->is_toggled = true; + } +} + int perf_evlist__apply_filters(struct perf_evlist *evlist) { struct perf_evsel *evsel; @@ -714,6 +794,7 @@ nthreads = thread_map__nr(evlist->threads); list_for_each_entry(evsel, &evlist->entries, node) { + if (evsel->filter == NULL) continue;
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 841a394..e4fe688 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h
@@ -124,6 +124,8 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct perf_target *target); void perf_evlist__delete_maps(struct perf_evlist *evlist); +void perf_evlist__mark_toggled(struct perf_evlist *evlist); +int perf_evlist__apply_toggle(struct perf_evlist *evlist); int perf_evlist__apply_filters(struct perf_evlist *evlist); void __perf_evlist__set_leader(struct list_head *list);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 79232d9..b4a3dd2 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c
@@ -693,6 +693,9 @@ */ if (perf_target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) attr->enable_on_exec = 1; + + if (evsel->is_toggled) + attr->paused = 1; } int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index e70415b..69f4183 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h
@@ -93,6 +93,7 @@ /* toggle event config */ char toggle_flag; char *toggle_name; + bool is_toggled; }; #define hists_to_evsel(h) container_of(h, struct perf_evsel, hists)
diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index 18d73aa..7f7eeb4 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c
@@ -88,6 +88,8 @@ if (evlist->cpus->map[0] < 0) opts->no_inherit = true; + perf_evlist__mark_toggled(evlist); + list_for_each_entry(evsel, &evlist->entries, node) perf_evsel__config(evsel, opts);