[PATCH 1/2] perf, tools: Implement branch_type event parameter

From: Andi Kleen
Date: Wed Oct 12 2016 - 17:09:58 EST


From: Andi Kleen <ak@xxxxxxxxxxxxxxx>

It can be useful to specify branch type state per event, for example
if we want to collect both software trace points and last branch PMU
events in a single collection. Currently this doesn't work because
the software trace point errors out with -b.

There was already a branch-type parameter to configure branch sample
types per event in the parser, but it was stubbed out. This patch
implements the necessary plumbing to actually enable it.

Now

perf record -e sched:sched_switch,cpu/cpu-cycles,branch_type=any/ ...

works

Signed-off-by: Andi Kleen <ak@xxxxxxxxxxxxxxx>
---
tools/perf/util/evsel.c | 9 ++++
tools/perf/util/evsel.h | 2 +
tools/perf/util/parse-branch-options.c | 85 +++++++++++++++++++---------------
tools/perf/util/parse-branch-options.h | 3 +-
tools/perf/util/parse-events.c | 15 ++++--
5 files changed, 71 insertions(+), 43 deletions(-)

diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 8bc271141d9d..e58a2fbf3b16 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -28,6 +28,7 @@
#include "debug.h"
#include "trace-event.h"
#include "stat.h"
+#include "util/parse-branch-options.h"

static struct {
bool sample_id_all;
@@ -708,6 +709,14 @@ static void apply_config_terms(struct perf_evsel *evsel,
case PERF_EVSEL__CONFIG_TERM_CALLGRAPH:
callgraph_buf = term->val.callgraph;
break;
+ case PERF_EVSEL__CONFIG_TERM_BRANCH:
+ if (term->val.branch && strcmp(term->val.branch, "no")) {
+ perf_evsel__set_sample_bit(evsel, BRANCH_STACK);
+ parse_branch_str(term->val.branch,
+ &attr->branch_sample_type);
+ } else
+ perf_evsel__reset_sample_bit(evsel, BRANCH_STACK);
+ break;
case PERF_EVSEL__CONFIG_TERM_STACK_USER:
dump_size = term->val.stack_user;
break;
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index b1503b0ecdff..8cd7cd227483 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -47,6 +47,7 @@ enum {
PERF_EVSEL__CONFIG_TERM_MAX_STACK,
PERF_EVSEL__CONFIG_TERM_OVERWRITE,
PERF_EVSEL__CONFIG_TERM_DRV_CFG,
+ PERF_EVSEL__CONFIG_TERM_BRANCH,
PERF_EVSEL__CONFIG_TERM_MAX,
};

@@ -63,6 +64,7 @@ struct perf_evsel_config_term {
int max_stack;
bool inherit;
bool overwrite;
+ char *branch;
} val;
};

diff --git a/tools/perf/util/parse-branch-options.c b/tools/perf/util/parse-branch-options.c
index afc088dd7d20..3634d6974300 100644
--- a/tools/perf/util/parse-branch-options.c
+++ b/tools/perf/util/parse-branch-options.c
@@ -31,59 +31,51 @@ static const struct branch_mode branch_modes[] = {
BRANCH_END
};

-int
-parse_branch_stack(const struct option *opt, const char *str, int unset)
+int parse_branch_str(const char *str, __u64 *mode)
{
#define ONLY_PLM \
(PERF_SAMPLE_BRANCH_USER |\
PERF_SAMPLE_BRANCH_KERNEL |\
PERF_SAMPLE_BRANCH_HV)

- uint64_t *mode = (uint64_t *)opt->value;
+ int ret = 0;
+ char *p, *s;
+ char *os = NULL;
const struct branch_mode *br;
- char *s, *os = NULL, *p;
- int ret = -1;

- if (unset)
+ if (str == NULL) {
+ *mode = PERF_SAMPLE_BRANCH_ANY;
return 0;
+ }

- /*
- * cannot set it twice, -b + --branch-filter for instance
- */
- if (*mode)
+ /* because str is read-only */
+ s = os = strdup(str);
+ if (!s)
return -1;

- /* str may be NULL in case no arg is passed to -b */
- if (str) {
- /* because str is read-only */
- s = os = strdup(str);
- if (!s)
- return -1;
-
- for (;;) {
- p = strchr(s, ',');
- if (p)
- *p = '\0';
-
- for (br = branch_modes; br->name; br++) {
- if (!strcasecmp(s, br->name))
- break;
- }
- if (!br->name) {
- ui__warning("unknown branch filter %s,"
- " check man page\n", s);
- goto error;
- }
-
- *mode |= br->mode;
-
- if (!p)
- break;
+ for (;;) {
+ p = strchr(s, ',');
+ if (p)
+ *p = '\0';

- s = p + 1;
+ for (br = branch_modes; br->name; br++) {
+ if (!strcasecmp(s, br->name))
+ break;
+ }
+ if (!br->name) {
+ ret = -1;
+ ui__warning("unknown branch filter %s,"
+ " check man page\n", s);
+ goto error;
}
+
+ *mode |= br->mode;
+
+ if (!p)
+ break;
+
+ s = p + 1;
}
- ret = 0;

/* default to any branch */
if ((*mode & ~ONLY_PLM) == 0) {
@@ -93,3 +85,20 @@ error:
free(os);
return ret;
}
+
+int
+parse_branch_stack(const struct option *opt, const char *str, int unset)
+{
+ __u64 *mode = (__u64 *)opt->value;
+
+ if (unset)
+ return 0;
+
+ /*
+ * cannot set it twice, -b + --branch-filter for instance
+ */
+ if (*mode)
+ return -1;
+
+ return parse_branch_str(str, mode);
+}
diff --git a/tools/perf/util/parse-branch-options.h b/tools/perf/util/parse-branch-options.h
index b9d9470c2e82..6086fd90eb23 100644
--- a/tools/perf/util/parse-branch-options.h
+++ b/tools/perf/util/parse-branch-options.h
@@ -1,5 +1,6 @@
#ifndef _PERF_PARSE_BRANCH_OPTIONS_H
#define _PERF_PARSE_BRANCH_OPTIONS_H 1
-struct option;
+#include <stdint.h>
int parse_branch_stack(const struct option *opt, const char *str, int unset);
+int parse_branch_str(const char *str, __u64 *mode);
#endif /* _PERF_PARSE_BRANCH_OPTIONS_H */
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 4e778eae1510..3c876b8ba4de 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -22,6 +22,7 @@
#include "cpumap.h"
#include "probe-file.h"
#include "asm/bug.h"
+#include "util/parse-branch-options.h"

#define MAX_NAME_LEN 100

@@ -973,10 +974,13 @@ do { \
CHECK_TYPE_VAL(NUM);
break;
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
- /*
- * TODO uncomment when the field is available
- * attr->branch_sample_type = term->val.num;
- */
+ CHECK_TYPE_VAL(STR);
+ if (strcmp(term->val.str, "no") &&
+ parse_branch_str(term->val.str, &attr->branch_sample_type)) {
+ err->str = strdup("invalid branch sample type");
+ err->idx = term->err_val;
+ return -EINVAL;
+ }
break;
case PARSE_EVENTS__TERM_TYPE_TIME:
CHECK_TYPE_VAL(NUM);
@@ -1119,6 +1123,9 @@ do { \
case PARSE_EVENTS__TERM_TYPE_CALLGRAPH:
ADD_CONFIG_TERM(CALLGRAPH, callgraph, term->val.str);
break;
+ case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
+ ADD_CONFIG_TERM(BRANCH, branch, term->val.str);
+ break;
case PARSE_EVENTS__TERM_TYPE_STACKSIZE:
ADD_CONFIG_TERM(STACK_USER, stack_user, term->val.num);
break;
--
2.5.5