perf bpf: Support BPF program attach to tracepoints
To support 98b5c2c65c
("perf, bpf: allow bpf programs attach to
tracepoints"), this patch allows BPF scripts to select tracepoints in
their section name.
Example:
# cat test_tracepoint.c
/*********************************************/
#include <uapi/linux/bpf.h>
#define SEC(NAME) __attribute__((section(NAME), used))
SEC("raw_syscalls:sys_enter")
int func(void *ctx)
{
/*
* /sys/kernel/debug/tracing/events/raw_syscalls/sys_enter/format:
* ...
* field:long id; offset:8; size:8; signed:1;
* ...
* ctx + 8 select 'id'
*/
u64 id = *((u64 *)(ctx + 8));
if (id == 1)
return 1;
return 0;
}
SEC("_write=sys_write")
int _write(void *ctx)
{
return 1;
}
char _license[] SEC("license") = "GPL";
int _version SEC("version") = LINUX_VERSION_CODE;
/*********************************************/
# perf record -e ./test_tracepoint.c dd if=/dev/zero of=/dev/null count=5
5+0 records in
5+0 records out
2560 bytes (2.6 kB) copied, 6.2281e-05 s, 41.1 MB/s
[ perf record: Woken up 1 times to write data ]
# perf script
dd 13436 [005] 1596.490869: raw_syscalls:sys_enter: NR 1 (1, 178d000, 200, 7ffe82470d60, ffffffffffffe020, fffff
dd 13436 [005] 1596.490871: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490873: raw_syscalls:sys_enter: NR 1 (1, 178d000, 200, ffffffffffffe000, ffffffffffffe020, f
dd 13436 [005] 1596.490874: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490876: raw_syscalls:sys_enter: NR 1 (1, 178d000, 200, ffffffffffffe000, ffffffffffffe020, f
dd 13436 [005] 1596.490876: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490878: raw_syscalls:sys_enter: NR 1 (1, 178d000, 200, ffffffffffffe000, ffffffffffffe020, f
dd 13436 [005] 1596.490879: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490881: raw_syscalls:sys_enter: NR 1 (1, 178d000, 200, ffffffffffffe000, ffffffffffffe020, f
dd 13436 [005] 1596.490882: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490900: raw_syscalls:sys_enter: NR 1 (2, 7ffe8246e640, 1f, 40acb8, 7f44bac74700, 7f44baa4fba
dd 13436 [005] 1596.490901: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490917: raw_syscalls:sys_enter: NR 1 (2, 7ffe8246e640, 1a, fffffffa, 7f44bac74700, 7f44baa4f
dd 13436 [005] 1596.490918: perf_bpf_probe:_write: (ffffffff812351e0)
dd 13436 [005] 1596.490932: raw_syscalls:sys_enter: NR 1 (2, 7ffe8246e640, 1a, fffffff9, 7f44bac74700, 7f44baa4f
dd 13436 [005] 1596.490933: perf_bpf_probe:_write: (ffffffff812351e0)
Committer note:
Further testing:
# trace --no-sys --event /home/acme/bpf/tracepoint.c cat /etc/passwd > /dev/null
0.000 raw_syscalls:sys_enter:NR 1 (1, 7f0490504000, c48, 7f0490503010, ffffffffffffffff, 0))
0.006 perf_bpf_probe:_write:(ffffffff81241bc0))
#
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1468406646-21642-6-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
cd102d70fe
commit
b4ee6d415e
|
@ -37,6 +37,9 @@ DEFINE_PRINT_FN(info, 1)
|
|||
DEFINE_PRINT_FN(debug, 1)
|
||||
|
||||
struct bpf_prog_priv {
|
||||
bool is_tp;
|
||||
char *sys_name;
|
||||
char *evt_name;
|
||||
struct perf_probe_event pev;
|
||||
bool need_prologue;
|
||||
struct bpf_insn *insns_buf;
|
||||
|
@ -118,6 +121,8 @@ clear_prog_priv(struct bpf_program *prog __maybe_unused,
|
|||
cleanup_perf_probe_events(&priv->pev, 1);
|
||||
zfree(&priv->insns_buf);
|
||||
zfree(&priv->type_mapping);
|
||||
zfree(&priv->sys_name);
|
||||
zfree(&priv->evt_name);
|
||||
free(priv);
|
||||
}
|
||||
|
||||
|
@ -269,7 +274,8 @@ nextline:
|
|||
}
|
||||
|
||||
static int
|
||||
parse_prog_config(const char *config_str, struct perf_probe_event *pev)
|
||||
parse_prog_config(const char *config_str, const char **p_main_str,
|
||||
bool *is_tp, struct perf_probe_event *pev)
|
||||
{
|
||||
int err;
|
||||
const char *main_str = parse_prog_config_kvpair(config_str, pev);
|
||||
|
@ -277,6 +283,22 @@ parse_prog_config(const char *config_str, struct perf_probe_event *pev)
|
|||
if (IS_ERR(main_str))
|
||||
return PTR_ERR(main_str);
|
||||
|
||||
*p_main_str = main_str;
|
||||
if (!strchr(main_str, '=')) {
|
||||
/* Is a tracepoint event? */
|
||||
const char *s = strchr(main_str, ':');
|
||||
|
||||
if (!s) {
|
||||
pr_debug("bpf: '%s' is not a valid tracepoint\n",
|
||||
config_str);
|
||||
return -BPF_LOADER_ERRNO__CONFIG;
|
||||
}
|
||||
|
||||
*is_tp = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*is_tp = false;
|
||||
err = parse_perf_probe_command(main_str, pev);
|
||||
if (err < 0) {
|
||||
pr_debug("bpf: '%s' is not a valid config string\n",
|
||||
|
@ -292,7 +314,8 @@ config_bpf_program(struct bpf_program *prog)
|
|||
{
|
||||
struct perf_probe_event *pev = NULL;
|
||||
struct bpf_prog_priv *priv = NULL;
|
||||
const char *config_str;
|
||||
const char *config_str, *main_str;
|
||||
bool is_tp = false;
|
||||
int err;
|
||||
|
||||
/* Initialize per-program probing setting */
|
||||
|
@ -313,10 +336,19 @@ config_bpf_program(struct bpf_program *prog)
|
|||
pev = &priv->pev;
|
||||
|
||||
pr_debug("bpf: config program '%s'\n", config_str);
|
||||
err = parse_prog_config(config_str, pev);
|
||||
err = parse_prog_config(config_str, &main_str, &is_tp, pev);
|
||||
if (err)
|
||||
goto errout;
|
||||
|
||||
if (is_tp) {
|
||||
char *s = strchr(main_str, ':');
|
||||
|
||||
priv->is_tp = true;
|
||||
priv->sys_name = strndup(main_str, s - main_str);
|
||||
priv->evt_name = strdup(s + 1);
|
||||
goto set_priv;
|
||||
}
|
||||
|
||||
if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) {
|
||||
pr_debug("bpf: '%s': group for event is set and not '%s'.\n",
|
||||
config_str, PERF_BPF_PROBE_GROUP);
|
||||
|
@ -339,6 +371,7 @@ config_bpf_program(struct bpf_program *prog)
|
|||
}
|
||||
pr_debug("bpf: config '%s' is ok\n", config_str);
|
||||
|
||||
set_priv:
|
||||
err = bpf_program__set_priv(prog, priv, clear_prog_priv);
|
||||
if (err) {
|
||||
pr_debug("Failed to set priv for program '%s'\n", config_str);
|
||||
|
@ -387,7 +420,7 @@ preproc_gen_prologue(struct bpf_program *prog, int n,
|
|||
size_t prologue_cnt = 0;
|
||||
int i, err;
|
||||
|
||||
if (IS_ERR(priv) || !priv)
|
||||
if (IS_ERR(priv) || !priv || priv->is_tp)
|
||||
goto errout;
|
||||
|
||||
pev = &priv->pev;
|
||||
|
@ -544,6 +577,11 @@ static int hook_load_preprocessor(struct bpf_program *prog)
|
|||
return -BPF_LOADER_ERRNO__INTERNAL;
|
||||
}
|
||||
|
||||
if (priv->is_tp) {
|
||||
priv->need_prologue = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pev = &priv->pev;
|
||||
for (i = 0; i < pev->ntevs; i++) {
|
||||
struct probe_trace_event *tev = &pev->tevs[i];
|
||||
|
@ -610,6 +648,13 @@ int bpf__probe(struct bpf_object *obj)
|
|||
err = PTR_ERR(priv);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (priv->is_tp) {
|
||||
bpf_program__set_tracepoint(prog);
|
||||
continue;
|
||||
}
|
||||
|
||||
bpf_program__set_kprobe(prog);
|
||||
pev = &priv->pev;
|
||||
|
||||
err = convert_perf_probe_events(pev, 1);
|
||||
|
@ -650,7 +695,7 @@ int bpf__unprobe(struct bpf_object *obj)
|
|||
struct bpf_prog_priv *priv = bpf_program__priv(prog);
|
||||
int i;
|
||||
|
||||
if (IS_ERR(priv) || !priv)
|
||||
if (IS_ERR(priv) || !priv || priv->is_tp)
|
||||
continue;
|
||||
|
||||
for (i = 0; i < priv->pev.ntevs; i++) {
|
||||
|
@ -711,6 +756,16 @@ int bpf__foreach_event(struct bpf_object *obj,
|
|||
return -BPF_LOADER_ERRNO__INTERNAL;
|
||||
}
|
||||
|
||||
if (priv->is_tp) {
|
||||
fd = bpf_program__fd(prog);
|
||||
err = (*func)(priv->sys_name, priv->evt_name, fd, arg);
|
||||
if (err) {
|
||||
pr_debug("bpf: tracepoint call back failed, stop iterate\n");
|
||||
return err;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
pev = &priv->pev;
|
||||
for (i = 0; i < pev->ntevs; i++) {
|
||||
tev = &pev->tevs[i];
|
||||
|
|
Loading…
Reference in New Issue