diff options
Diffstat (limited to 'tools/perf/util/bpf-filter.c')
-rw-r--r-- | tools/perf/util/bpf-filter.c | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c new file mode 100644 index 000000000000..0b30688d78a7 --- /dev/null +++ b/tools/perf/util/bpf-filter.c @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <stdlib.h> + +#include <bpf/bpf.h> +#include <linux/err.h> +#include <internal/xyarray.h> + +#include "util/debug.h" +#include "util/evsel.h" + +#include "util/bpf-filter.h" +#include "util/bpf-filter-flex.h" +#include "util/bpf-filter-bison.h" + +#include "bpf_skel/sample-filter.h" +#include "bpf_skel/sample_filter.skel.h" + +#define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) + +#define __PERF_SAMPLE_TYPE(st, opt) { st, #st, opt } +#define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PERF_SAMPLE_##_st, opt) + +static const struct perf_sample_info { + u64 type; + const char *name; + const char *option; +} sample_table[] = { + /* default sample flags */ + PERF_SAMPLE_TYPE(IP, NULL), + PERF_SAMPLE_TYPE(TID, NULL), + PERF_SAMPLE_TYPE(PERIOD, NULL), + /* flags mostly set by default, but still have options */ + PERF_SAMPLE_TYPE(ID, "--sample-identifier"), + PERF_SAMPLE_TYPE(CPU, "--sample-cpu"), + PERF_SAMPLE_TYPE(TIME, "-T"), + /* optional sample flags */ + PERF_SAMPLE_TYPE(ADDR, "-d"), + PERF_SAMPLE_TYPE(DATA_SRC, "-d"), + PERF_SAMPLE_TYPE(PHYS_ADDR, "--phys-data"), + PERF_SAMPLE_TYPE(WEIGHT, "-W"), + PERF_SAMPLE_TYPE(WEIGHT_STRUCT, "-W"), + PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"), + PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"), + PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"), +}; + +static const struct perf_sample_info *get_sample_info(u64 flags) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(sample_table); i++) { + if (sample_table[i].type == flags) + return &sample_table[i]; + } + return NULL; +} + +static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *expr) +{ + const struct perf_sample_info *info; + + if (evsel->core.attr.sample_type & expr->sample_flags) + return 0; + + info = get_sample_info(expr->sample_flags); + if (info == NULL) { + pr_err("Error: %s event does not have sample flags %lx\n", + evsel__name(evsel), expr->sample_flags); + return -1; + } + + pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name); + if (info->option) + pr_err(" Hint: please add %s option to perf record\n", info->option); + return -1; +} + +int perf_bpf_filter__prepare(struct evsel *evsel) +{ + int i, x, y, fd; + struct sample_filter_bpf *skel; + struct bpf_program *prog; + struct bpf_link *link; + struct perf_bpf_filter_expr *expr; + + skel = sample_filter_bpf__open_and_load(); + if (!skel) { + pr_err("Failed to load perf sample-filter BPF skeleton\n"); + return -1; + } + + i = 0; + fd = bpf_map__fd(skel->maps.filters); + list_for_each_entry(expr, &evsel->bpf_filters, list) { + struct perf_bpf_filter_entry entry = { + .op = expr->op, + .part = expr->part, + .flags = expr->sample_flags, + .value = expr->val, + }; + + if (check_sample_flags(evsel, expr) < 0) + return -1; + + bpf_map_update_elem(fd, &i, &entry, BPF_ANY); + i++; + + if (expr->op == PBF_OP_GROUP_BEGIN) { + struct perf_bpf_filter_expr *group; + + list_for_each_entry(group, &expr->groups, list) { + struct perf_bpf_filter_entry group_entry = { + .op = group->op, + .part = group->part, + .flags = group->sample_flags, + .value = group->val, + }; + bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY); + i++; + } + + memset(&entry, 0, sizeof(entry)); + entry.op = PBF_OP_GROUP_END; + bpf_map_update_elem(fd, &i, &entry, BPF_ANY); + i++; + } + } + + if (i > MAX_FILTERS) { + pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS); + return -1; + } + prog = skel->progs.perf_sample_filter; + for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { + for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { + link = bpf_program__attach_perf_event(prog, FD(evsel, x, y)); + if (IS_ERR(link)) { + pr_err("Failed to attach perf sample-filter program\n"); + return PTR_ERR(link); + } + } + } + evsel->bpf_skel = skel; + return 0; +} + +int perf_bpf_filter__destroy(struct evsel *evsel) +{ + struct perf_bpf_filter_expr *expr, *tmp; + + list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) { + list_del(&expr->list); + free(expr); + } + sample_filter_bpf__destroy(evsel->bpf_skel); + return 0; +} + +u64 perf_bpf_filter__lost_count(struct evsel *evsel) +{ + struct sample_filter_bpf *skel = evsel->bpf_skel; + + return skel ? skel->bss->dropped : 0; +} + +struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags, int part, + enum perf_bpf_filter_op op, + unsigned long val) +{ + struct perf_bpf_filter_expr *expr; + + expr = malloc(sizeof(*expr)); + if (expr != NULL) { + expr->sample_flags = sample_flags; + expr->part = part; + expr->op = op; + expr->val = val; + INIT_LIST_HEAD(&expr->groups); + } + return expr; +} + +int perf_bpf_filter__parse(struct list_head *expr_head, const char *str) +{ + YY_BUFFER_STATE buffer; + int ret; + + buffer = perf_bpf_filter__scan_string(str); + + ret = perf_bpf_filter_parse(expr_head); + + perf_bpf_filter__flush_buffer(buffer); + perf_bpf_filter__delete_buffer(buffer); + perf_bpf_filter_lex_destroy(); + + return ret; +} |