perf bpf: Use same BPF program if arguments are identical
authorWang Nan <wangnan0@huawei.com>
Tue, 17 Nov 2015 08:32:47 +0000 (08:32 +0000)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 18 Nov 2015 20:51:05 +0000 (17:51 -0300)
This patch allows creating only one BPF program for different
'probe_trace_event'(tev) entries generated by one
'perf_probe_event'(pev) if their prologues are identical.

This is done by comparing the argument list of different tev instances,
and the maps type of prologue and tev using a mapping array. This patch
utilizes qsort to sort the tevs. After sorting, tevs with identical
argument lists will be grouped together.

Test result:

Sample BPF program:

  #define SEC(NAME) __attribute__((section(NAME), used))
  SEC("inlines=no;"
      "func=SyS_dup? oldfd")
  int func(void *ctx)
  {
      return 1;
  }

It would probe at SyS_dup2 and SyS_dup3, obtaining oldfd as its
argument.

The following cmdline shows a BPF program being loaded into the kernel
by perf:

 # perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog

Before this patch:

  # perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
  [1] 24858
  lrwx------ 1 root root 64 Nov 14 04:09 3 -> anon_inode:bpf-prog
  lrwx------ 1 root root 64 Nov 14 04:09 4 -> anon_inode:bpf-prog
  ...

After this patch:

  # perf record -e ./test_bpf_arg.c sleep 4 & sleep 1 && ls /proc/$!/fd/ -l | grep bpf-prog
  [1] 25699
  lrwx------ 1 root root 64 Nov 14 04:10 3 -> anon_inode:bpf-prog
  ...

Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1447749170-175898-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/util/bpf-loader.c

index 190a1c7f0649f1c867d5a46917d20f2f3eaa6b5e..36544e5ece435587c83e937880ad7ed101fb65a8 100644 (file)
@@ -38,6 +38,8 @@ struct bpf_prog_priv {
        struct perf_probe_event pev;
        bool need_prologue;
        struct bpf_insn *insns_buf;
+       int nr_types;
+       int *type_mapping;
 };
 
 static bool libbpf_initialized;
@@ -113,6 +115,7 @@ bpf_prog_priv__clear(struct bpf_program *prog __maybe_unused,
 
        cleanup_perf_probe_events(&priv->pev, 1);
        zfree(&priv->insns_buf);
+       zfree(&priv->type_mapping);
        free(priv);
 }
 
@@ -381,7 +384,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
        struct bpf_prog_priv *priv;
        struct bpf_insn *buf;
        size_t prologue_cnt = 0;
-       int err;
+       int i, err;
 
        err = bpf_program__get_private(prog, (void **)&priv);
        if (err || !priv)
@@ -389,10 +392,21 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
 
        pev = &priv->pev;
 
-       if (n < 0 || n >= pev->ntevs)
+       if (n < 0 || n >= priv->nr_types)
                goto errout;
 
-       tev = &pev->tevs[n];
+       /* Find a tev belongs to that type */
+       for (i = 0; i < pev->ntevs; i++) {
+               if (priv->type_mapping[i] == n)
+                       break;
+       }
+
+       if (i >= pev->ntevs) {
+               pr_debug("Internal error: prologue type %d not found\n", n);
+               return -BPF_LOADER_ERRNO__PROLOGUE;
+       }
+
+       tev = &pev->tevs[i];
 
        buf = priv->insns_buf;
        err = bpf__gen_prologue(tev->args, tev->nargs,
@@ -423,6 +437,101 @@ errout:
        return -BPF_LOADER_ERRNO__PROLOGUE;
 }
 
+/*
+ * compare_tev_args is reflexive, transitive and antisymmetric.
+ * I can proof it but this margin is too narrow to contain.
+ */
+static int compare_tev_args(const void *ptev1, const void *ptev2)
+{
+       int i, ret;
+       const struct probe_trace_event *tev1 =
+               *(const struct probe_trace_event **)ptev1;
+       const struct probe_trace_event *tev2 =
+               *(const struct probe_trace_event **)ptev2;
+
+       ret = tev2->nargs - tev1->nargs;
+       if (ret)
+               return ret;
+
+       for (i = 0; i < tev1->nargs; i++) {
+               struct probe_trace_arg *arg1, *arg2;
+               struct probe_trace_arg_ref *ref1, *ref2;
+
+               arg1 = &tev1->args[i];
+               arg2 = &tev2->args[i];
+
+               ret = strcmp(arg1->value, arg2->value);
+               if (ret)
+                       return ret;
+
+               ref1 = arg1->ref;
+               ref2 = arg2->ref;
+
+               while (ref1 && ref2) {
+                       ret = ref2->offset - ref1->offset;
+                       if (ret)
+                               return ret;
+
+                       ref1 = ref1->next;
+                       ref2 = ref2->next;
+               }
+
+               if (ref1 || ref2)
+                       return ref2 ? 1 : -1;
+       }
+
+       return 0;
+}
+
+/*
+ * Assign a type number to each tevs in a pev.
+ * mapping is an array with same slots as tevs in that pev.
+ * nr_types will be set to number of types.
+ */
+static int map_prologue(struct perf_probe_event *pev, int *mapping,
+                       int *nr_types)
+{
+       int i, type = 0;
+       struct probe_trace_event **ptevs;
+
+       size_t array_sz = sizeof(*ptevs) * pev->ntevs;
+
+       ptevs = malloc(array_sz);
+       if (!ptevs) {
+               pr_debug("No ehough memory: alloc ptevs failed\n");
+               return -ENOMEM;
+       }
+
+       pr_debug("In map_prologue, ntevs=%d\n", pev->ntevs);
+       for (i = 0; i < pev->ntevs; i++)
+               ptevs[i] = &pev->tevs[i];
+
+       qsort(ptevs, pev->ntevs, sizeof(*ptevs),
+             compare_tev_args);
+
+       for (i = 0; i < pev->ntevs; i++) {
+               int n;
+
+               n = ptevs[i] - pev->tevs;
+               if (i == 0) {
+                       mapping[n] = type;
+                       pr_debug("mapping[%d]=%d\n", n, type);
+                       continue;
+               }
+
+               if (compare_tev_args(ptevs + i, ptevs + i - 1) == 0)
+                       mapping[n] = type;
+               else
+                       mapping[n] = ++type;
+
+               pr_debug("mapping[%d]=%d\n", n, mapping[n]);
+       }
+       free(ptevs);
+       *nr_types = type + 1;
+
+       return 0;
+}
+
 static int hook_load_preprocessor(struct bpf_program *prog)
 {
        struct perf_probe_event *pev;
@@ -462,7 +571,19 @@ static int hook_load_preprocessor(struct bpf_program *prog)
                return -ENOMEM;
        }
 
-       err = bpf_program__set_prep(prog, pev->ntevs,
+       priv->type_mapping = malloc(sizeof(int) * pev->ntevs);
+       if (!priv->type_mapping) {
+               pr_debug("No enough memory: alloc type_mapping failed\n");
+               return -ENOMEM;
+       }
+       memset(priv->type_mapping, -1,
+              sizeof(int) * pev->ntevs);
+
+       err = map_prologue(pev, priv->type_mapping, &priv->nr_types);
+       if (err)
+               return err;
+
+       err = bpf_program__set_prep(prog, priv->nr_types,
                                    preproc_gen_prologue);
        return err;
 }
@@ -596,10 +717,13 @@ int bpf__foreach_tev(struct bpf_object *obj,
                for (i = 0; i < pev->ntevs; i++) {
                        tev = &pev->tevs[i];
 
-                       if (priv->need_prologue)
-                               fd = bpf_program__nth_fd(prog, i);
-                       else
+                       if (priv->need_prologue) {
+                               int type = priv->type_mapping[i];
+
+                               fd = bpf_program__nth_fd(prog, type);
+                       } else {
                                fd = bpf_program__fd(prog);
+                       }
 
                        if (fd < 0) {
                                pr_debug("bpf: failed to get file descriptor\n");