diff options
author | Wang Nan <wangnan0@huawei.com> | 2016-07-13 06:44:05 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2016-07-13 22:09:04 -0400 |
commit | b4ee6d415e731b9d8a51451da0ebe33450c355d2 (patch) | |
tree | 533e82570803fae1deedf84c62d433d5f74613ef /tools/perf/util/bpf-loader.c | |
parent | cd102d70fe957b060b9df6bc4f54684de3fe00cd (diff) |
perf bpf: Support BPF program attach to tracepoints
To support 98b5c2c65c29 ("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>
Diffstat (limited to 'tools/perf/util/bpf-loader.c')
-rw-r--r-- | tools/perf/util/bpf-loader.c | 65 |
1 files changed, 60 insertions, 5 deletions
diff --git a/tools/perf/util/bpf-loader.c b/tools/perf/util/bpf-loader.c index f22701497112..1f12e4e40006 100644 --- a/tools/perf/util/bpf-loader.c +++ b/tools/perf/util/bpf-loader.c | |||
@@ -37,6 +37,9 @@ DEFINE_PRINT_FN(info, 1) | |||
37 | DEFINE_PRINT_FN(debug, 1) | 37 | DEFINE_PRINT_FN(debug, 1) |
38 | 38 | ||
39 | struct bpf_prog_priv { | 39 | struct bpf_prog_priv { |
40 | bool is_tp; | ||
41 | char *sys_name; | ||
42 | char *evt_name; | ||
40 | struct perf_probe_event pev; | 43 | struct perf_probe_event pev; |
41 | bool need_prologue; | 44 | bool need_prologue; |
42 | struct bpf_insn *insns_buf; | 45 | struct bpf_insn *insns_buf; |
@@ -118,6 +121,8 @@ clear_prog_priv(struct bpf_program *prog __maybe_unused, | |||
118 | cleanup_perf_probe_events(&priv->pev, 1); | 121 | cleanup_perf_probe_events(&priv->pev, 1); |
119 | zfree(&priv->insns_buf); | 122 | zfree(&priv->insns_buf); |
120 | zfree(&priv->type_mapping); | 123 | zfree(&priv->type_mapping); |
124 | zfree(&priv->sys_name); | ||
125 | zfree(&priv->evt_name); | ||
121 | free(priv); | 126 | free(priv); |
122 | } | 127 | } |
123 | 128 | ||
@@ -269,7 +274,8 @@ nextline: | |||
269 | } | 274 | } |
270 | 275 | ||
271 | static int | 276 | static int |
272 | parse_prog_config(const char *config_str, struct perf_probe_event *pev) | 277 | parse_prog_config(const char *config_str, const char **p_main_str, |
278 | bool *is_tp, struct perf_probe_event *pev) | ||
273 | { | 279 | { |
274 | int err; | 280 | int err; |
275 | const char *main_str = parse_prog_config_kvpair(config_str, pev); | 281 | 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) | |||
277 | if (IS_ERR(main_str)) | 283 | if (IS_ERR(main_str)) |
278 | return PTR_ERR(main_str); | 284 | return PTR_ERR(main_str); |
279 | 285 | ||
286 | *p_main_str = main_str; | ||
287 | if (!strchr(main_str, '=')) { | ||
288 | /* Is a tracepoint event? */ | ||
289 | const char *s = strchr(main_str, ':'); | ||
290 | |||
291 | if (!s) { | ||
292 | pr_debug("bpf: '%s' is not a valid tracepoint\n", | ||
293 | config_str); | ||
294 | return -BPF_LOADER_ERRNO__CONFIG; | ||
295 | } | ||
296 | |||
297 | *is_tp = true; | ||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | *is_tp = false; | ||
280 | err = parse_perf_probe_command(main_str, pev); | 302 | err = parse_perf_probe_command(main_str, pev); |
281 | if (err < 0) { | 303 | if (err < 0) { |
282 | pr_debug("bpf: '%s' is not a valid config string\n", | 304 | pr_debug("bpf: '%s' is not a valid config string\n", |
@@ -292,7 +314,8 @@ config_bpf_program(struct bpf_program *prog) | |||
292 | { | 314 | { |
293 | struct perf_probe_event *pev = NULL; | 315 | struct perf_probe_event *pev = NULL; |
294 | struct bpf_prog_priv *priv = NULL; | 316 | struct bpf_prog_priv *priv = NULL; |
295 | const char *config_str; | 317 | const char *config_str, *main_str; |
318 | bool is_tp = false; | ||
296 | int err; | 319 | int err; |
297 | 320 | ||
298 | /* Initialize per-program probing setting */ | 321 | /* Initialize per-program probing setting */ |
@@ -313,10 +336,19 @@ config_bpf_program(struct bpf_program *prog) | |||
313 | pev = &priv->pev; | 336 | pev = &priv->pev; |
314 | 337 | ||
315 | pr_debug("bpf: config program '%s'\n", config_str); | 338 | pr_debug("bpf: config program '%s'\n", config_str); |
316 | err = parse_prog_config(config_str, pev); | 339 | err = parse_prog_config(config_str, &main_str, &is_tp, pev); |
317 | if (err) | 340 | if (err) |
318 | goto errout; | 341 | goto errout; |
319 | 342 | ||
343 | if (is_tp) { | ||
344 | char *s = strchr(main_str, ':'); | ||
345 | |||
346 | priv->is_tp = true; | ||
347 | priv->sys_name = strndup(main_str, s - main_str); | ||
348 | priv->evt_name = strdup(s + 1); | ||
349 | goto set_priv; | ||
350 | } | ||
351 | |||
320 | if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { | 352 | if (pev->group && strcmp(pev->group, PERF_BPF_PROBE_GROUP)) { |
321 | pr_debug("bpf: '%s': group for event is set and not '%s'.\n", | 353 | pr_debug("bpf: '%s': group for event is set and not '%s'.\n", |
322 | config_str, PERF_BPF_PROBE_GROUP); | 354 | config_str, PERF_BPF_PROBE_GROUP); |
@@ -339,6 +371,7 @@ config_bpf_program(struct bpf_program *prog) | |||
339 | } | 371 | } |
340 | pr_debug("bpf: config '%s' is ok\n", config_str); | 372 | pr_debug("bpf: config '%s' is ok\n", config_str); |
341 | 373 | ||
374 | set_priv: | ||
342 | err = bpf_program__set_priv(prog, priv, clear_prog_priv); | 375 | err = bpf_program__set_priv(prog, priv, clear_prog_priv); |
343 | if (err) { | 376 | if (err) { |
344 | pr_debug("Failed to set priv for program '%s'\n", config_str); | 377 | 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, | |||
387 | size_t prologue_cnt = 0; | 420 | size_t prologue_cnt = 0; |
388 | int i, err; | 421 | int i, err; |
389 | 422 | ||
390 | if (IS_ERR(priv) || !priv) | 423 | if (IS_ERR(priv) || !priv || priv->is_tp) |
391 | goto errout; | 424 | goto errout; |
392 | 425 | ||
393 | pev = &priv->pev; | 426 | pev = &priv->pev; |
@@ -544,6 +577,11 @@ static int hook_load_preprocessor(struct bpf_program *prog) | |||
544 | return -BPF_LOADER_ERRNO__INTERNAL; | 577 | return -BPF_LOADER_ERRNO__INTERNAL; |
545 | } | 578 | } |
546 | 579 | ||
580 | if (priv->is_tp) { | ||
581 | priv->need_prologue = false; | ||
582 | return 0; | ||
583 | } | ||
584 | |||
547 | pev = &priv->pev; | 585 | pev = &priv->pev; |
548 | for (i = 0; i < pev->ntevs; i++) { | 586 | for (i = 0; i < pev->ntevs; i++) { |
549 | struct probe_trace_event *tev = &pev->tevs[i]; | 587 | struct probe_trace_event *tev = &pev->tevs[i]; |
@@ -610,6 +648,13 @@ int bpf__probe(struct bpf_object *obj) | |||
610 | err = PTR_ERR(priv); | 648 | err = PTR_ERR(priv); |
611 | goto out; | 649 | goto out; |
612 | } | 650 | } |
651 | |||
652 | if (priv->is_tp) { | ||
653 | bpf_program__set_tracepoint(prog); | ||
654 | continue; | ||
655 | } | ||
656 | |||
657 | bpf_program__set_kprobe(prog); | ||
613 | pev = &priv->pev; | 658 | pev = &priv->pev; |
614 | 659 | ||
615 | err = convert_perf_probe_events(pev, 1); | 660 | err = convert_perf_probe_events(pev, 1); |
@@ -650,7 +695,7 @@ int bpf__unprobe(struct bpf_object *obj) | |||
650 | struct bpf_prog_priv *priv = bpf_program__priv(prog); | 695 | struct bpf_prog_priv *priv = bpf_program__priv(prog); |
651 | int i; | 696 | int i; |
652 | 697 | ||
653 | if (IS_ERR(priv) || !priv) | 698 | if (IS_ERR(priv) || !priv || priv->is_tp) |
654 | continue; | 699 | continue; |
655 | 700 | ||
656 | for (i = 0; i < priv->pev.ntevs; i++) { | 701 | for (i = 0; i < priv->pev.ntevs; i++) { |
@@ -711,6 +756,16 @@ int bpf__foreach_event(struct bpf_object *obj, | |||
711 | return -BPF_LOADER_ERRNO__INTERNAL; | 756 | return -BPF_LOADER_ERRNO__INTERNAL; |
712 | } | 757 | } |
713 | 758 | ||
759 | if (priv->is_tp) { | ||
760 | fd = bpf_program__fd(prog); | ||
761 | err = (*func)(priv->sys_name, priv->evt_name, fd, arg); | ||
762 | if (err) { | ||
763 | pr_debug("bpf: tracepoint call back failed, stop iterate\n"); | ||
764 | return err; | ||
765 | } | ||
766 | continue; | ||
767 | } | ||
768 | |||
714 | pev = &priv->pev; | 769 | pev = &priv->pev; |
715 | for (i = 0; i < pev->ntevs; i++) { | 770 | for (i = 0; i < pev->ntevs; i++) { |
716 | tev = &pev->tevs[i]; | 771 | tev = &pev->tevs[i]; |