perf bpf: Use same BPF program if arguments are identical
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>
This commit is contained in:
parent
ad0dd7aed5
commit
d35b32891a
|
@ -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 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
|
|||
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");
|
||||
|
|
Loading…
Reference in New Issue