From: Andi Kleen <a...@linux.intel.com>

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 <a...@linux.intel.com>
---
 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

Reply via email to