perf trace: Put the per-syscall entry/exit prog_array BPF map infrastructure in place

I.e. look for "syscalls_sys_enter" and "syscalls_sys_exit" BPF maps of
type PROG_ARRAY and populate it with the handlers as specified per
syscall, for now only 'open' is wiring it to something, in time all
syscalls that need to copy arguments entering a syscall or returning
from one will set these to the right handlers, reusing when possible
pre-existing ones.

Next step is to use bpf_tail_call() into that.

Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Luis Cláudio Gonçalves <lclaudio@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Link: https://lkml.kernel.org/n/tip-t0p4u43i9vbpzs1xtowna3gb@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
Arnaldo Carvalho de Melo 2019-07-16 11:27:03 -03:00
parent 6ff8fff456
commit 3803a22931
2 changed files with 86 additions and 4 deletions

View File

@ -1,4 +1,3 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* builtin-trace.c
*
@ -83,6 +82,10 @@ struct trace {
int max;
struct syscall *table;
struct bpf_map *map;
struct { // per syscall BPF_MAP_TYPE_PROG_ARRAY
struct bpf_map *sys_enter,
*sys_exit;
} prog_array;
struct {
struct perf_evsel *sys_enter,
*sys_exit,
@ -1619,6 +1622,22 @@ out_free:
goto out;
}
static __maybe_unused bool trace__syscall_enabled(struct trace *trace, int id)
{
bool in_ev_qualifier;
if (trace->ev_qualifier_ids.nr == 0)
return true;
in_ev_qualifier = bsearch(&id, trace->ev_qualifier_ids.entries,
trace->ev_qualifier_ids.nr, sizeof(int), intcmp) != NULL;
if (in_ev_qualifier)
return !trace->not_ev_qualifier;
return trace->not_ev_qualifier;
}
/*
* args is to be interpreted as a series of longs but we need to handle
* 8-byte unaligned accesses. args points to raw_data within the event
@ -2784,6 +2803,18 @@ static void trace__init_syscall_bpf_progs(struct trace *trace, int id)
}
}
static int trace__bpf_prog_sys_enter_fd(struct trace *trace, int id)
{
struct syscall *sc = trace__syscall_info(trace, NULL, id);
return sc ? bpf_program__fd(sc->bpf_prog.sys_enter) : bpf_program__fd(trace->syscalls.unaugmented_prog);
}
static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id)
{
struct syscall *sc = trace__syscall_info(trace, NULL, id);
return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->syscalls.unaugmented_prog);
}
static void trace__init_bpf_map_syscall_args(struct trace *trace, int id, struct bpf_map_syscall_entry *entry)
{
struct syscall *sc = trace__syscall_info(trace, NULL, id);
@ -2837,10 +2868,8 @@ static int __trace__init_syscalls_bpf_map(struct trace *trace, bool enabled)
int err = 0, key;
for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
if (enabled) {
if (enabled)
trace__init_bpf_map_syscall_args(trace, key, &value);
trace__init_syscall_bpf_progs(trace, key);
}
err = bpf_map_update_elem(fd, &key, &value, BPF_ANY);
if (err)
@ -2859,6 +2888,34 @@ static int trace__init_syscalls_bpf_map(struct trace *trace)
return __trace__init_syscalls_bpf_map(trace, enabled);
}
static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace)
{
int map_enter_fd = bpf_map__fd(trace->syscalls.prog_array.sys_enter),
map_exit_fd = bpf_map__fd(trace->syscalls.prog_array.sys_exit);
int err = 0, key;
for (key = 0; key < trace->sctbl->syscalls.nr_entries; ++key) {
int prog_fd;
if (!trace__syscall_enabled(trace, key))
continue;
trace__init_syscall_bpf_progs(trace, key);
// It'll get at least the "!raw_syscalls:unaugmented"
prog_fd = trace__bpf_prog_sys_enter_fd(trace, key);
err = bpf_map_update_elem(map_enter_fd, &key, &prog_fd, BPF_ANY);
if (err)
break;
prog_fd = trace__bpf_prog_sys_exit_fd(trace, key);
err = bpf_map_update_elem(map_exit_fd, &key, &prog_fd, BPF_ANY);
if (err)
break;
}
return err;
}
#else
static int trace__set_ev_qualifier_bpf_filter(struct trace *trace __maybe_unused)
{
@ -2875,6 +2932,11 @@ static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace
{
return NULL;
}
static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace __maybe_unused)
{
return 0;
}
#endif // HAVE_LIBBPF_SUPPORT
static int trace__set_ev_qualifier_filter(struct trace *trace)
@ -3129,6 +3191,10 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
if (trace->syscalls.map)
trace__init_syscalls_bpf_map(trace);
if (trace->syscalls.prog_array.sys_enter)
trace__init_syscalls_bpf_prog_array_maps(trace);
if (trace->ev_qualifier_ids.nr > 0) {
err = trace__set_ev_qualifier_filter(trace);
if (err < 0)
@ -3754,6 +3820,8 @@ static void trace__set_bpf_map_filtered_pids(struct trace *trace)
static void trace__set_bpf_map_syscalls(struct trace *trace)
{
trace->syscalls.map = trace__find_bpf_map_by_name(trace, "syscalls");
trace->syscalls.prog_array.sys_enter = trace__find_bpf_map_by_name(trace, "syscalls_sys_enter");
trace->syscalls.prog_array.sys_exit = trace__find_bpf_map_by_name(trace, "syscalls_sys_exit");
}
static int trace__config(const char *var, const char *value, void *arg)

View File

@ -33,6 +33,20 @@ struct syscall {
bpf_map(syscalls, ARRAY, int, struct syscall, 512);
/*
* What to augment at entry?
*
* Pointer arg payloads (filenames, etc) passed from userspace to the kernel
*/
bpf_map(syscalls_sys_enter, PROG_ARRAY, u32, u32, 512);
/*
* What to augment at exit?
*
* Pointer arg payloads returned from the kernel (struct stat, etc) to userspace.
*/
bpf_map(syscalls_sys_exit, PROG_ARRAY, u32, u32, 512);
struct syscall_enter_args {
unsigned long long common_tp_fields;
long syscall_nr;