lkml.org 
[lkml]   [2015]   [Nov]   [17]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/5] perf tools: Use same BPF program if arguments are identical
    Date
    This patch allows creating only one BPF program for different
    'probe_trace_event'(tev) generated by one 'perf_probe_event'(pev), if
    their prologues are identical.

    This is done by comparing argument list of different tev, and maps type
    of prologue and tev using a mapping array. This patch utilizes qsort to
    sort tevs. After sorting, tevs with identical argument list 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, extracts oldfd as its argument.

    Following cmdline shows BPF program loaded into 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: Arnaldo Carvalho de Melo <acme@redhat.com>
    Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
    Cc: Zefan Li <lizefan@huawei.com>
    Cc: pi3orama@163.com
    ---
    tools/perf/util/bpf-loader.c | 138 ++++++++++++++++++++++++++++++++++++++++---
    1 file changed, 131 insertions(+), 7 deletions(-)

    diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c
    index 190a1c7..36544e5 100644
    --- a/tools/perf/util/bpf-loader.c
    +++ b/tools/perf/util/bpf-loader.c
    @@ -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");
    --
    1.8.3.4


    \
     
     \ /
      Last update: 2015-11-17 10:01    [W:2.675 / U:0.036 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site