perf tools: Implement branch_type event parameter
authorAndi Kleen <ak@linux.intel.com>
Wed, 12 Oct 2016 21:02:06 +0000 (14:02 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Mon, 24 Oct 2016 14:07:35 +0000 (11:07 -0300)
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@linux.intel.com>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Link: http://lkml.kernel.org/r/1476306127-19721-1-git-send-email-andi@firstfloor.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/evsel.c
tools/perf/util/evsel.h
tools/perf/util/parse-branch-options.c
tools/perf/util/parse-branch-options.h
tools/perf/util/parse-events.c

index 8bc271141d9df9051aa93bdf4677ba79ff5473e0..e58a2fbf3b160c2acd827d0c6b9aeca5a0c8f1ce 100644 (file)
@@ -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;
index b1503b0ecdff511c14493552f063bba0a88dabaf..8cd7cd2274836f9b2725d1b54e80feb78d12f863 100644 (file)
@@ -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;
 };
 
index afc088dd7d20948929aa087661f13f913b850b97..3634d6974300c07a5f9cb208ef36a3bfb3296913 100644 (file)
@@ -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);
+}
index b9d9470c2e82d30ee6ae87e2a65bf051a1b48876..6086fd90eb23a3c2454f0d7db8c7cd0509b0de64 100644 (file)
@@ -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 */
index 4e778eae151046c358da9e0ea0b03ef92170bec0..3c876b8ba4de68afaf312e4bc6df07a4b58f0211 100644 (file)
@@ -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;