perf bpf filter: Introduce basic BPF filter expression
This implements a tiny parser for the filter expressions used for BPF. Each expression will be converted to struct perf_bpf_filter_expr and be passed to a BPF map. For now, I'd like to start with the very basic comparisons like EQ or GT. The LHS should be a term for sample data and the RHS is a number. The expressions are connected by a comma. For example, period > 10000 ip < 0x1000000000000, cpu == 3 Signed-off-by: Namhyung Kim <namhyung@kernel.org> Acked-by: Jiri Olsa <jolsa@kernel.org> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Hao Luo <haoluo@google.com> Cc: Ian Rogers <irogers@google.com> Cc: Ingo Molnar <mingo@kernel.org> Cc: James Clark <james.clark@arm.com> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Leo Yan <leo.yan@linaro.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ravi Bangoria <ravi.bangoria@amd.com> Cc: Song Liu <song@kernel.org> Cc: Stephane Eranian <eranian@google.com> Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20230314234237.3008956-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
6e57f69f23
commit
990a71e904
|
@ -154,6 +154,9 @@ perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o
|
|||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_counter_cgroup.o
|
||||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_ftrace.o
|
||||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_off_cpu.o
|
||||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf-filter.o
|
||||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf-filter-flex.o
|
||||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf-filter-bison.o
|
||||
|
||||
ifeq ($(CONFIG_LIBTRACEEVENT),y)
|
||||
perf-$(CONFIG_PERF_BPF_SKEL) += bpf_lock_contention.o
|
||||
|
@ -267,6 +270,16 @@ $(OUTPUT)util/pmu-bison.c $(OUTPUT)util/pmu-bison.h: util/pmu.y
|
|||
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
|
||||
-o $(OUTPUT)util/pmu-bison.c -p perf_pmu_
|
||||
|
||||
$(OUTPUT)util/bpf-filter-flex.c $(OUTPUT)util/bpf-filter-flex.h: util/bpf-filter.l $(OUTPUT)util/bpf-filter-bison.c
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/bpf-filter-flex.c \
|
||||
--header-file=$(OUTPUT)util/bpf-filter-flex.h $(PARSER_DEBUG_FLEX) $<
|
||||
|
||||
$(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filter.y
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \
|
||||
-o $(OUTPUT)util/bpf-filter-bison.c -p perf_bpf_filter_
|
||||
|
||||
FLEX_GE_26 := $(shell expr $(shell $(FLEX) --version | sed -e 's/flex \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 26)
|
||||
ifeq ($(FLEX_GE_26),1)
|
||||
flex_flags := -Wno-switch-enum -Wno-switch-default -Wno-unused-function -Wno-redundant-decls -Wno-sign-compare -Wno-unused-parameter -Wno-missing-prototypes -Wno-missing-declarations
|
||||
|
@ -280,6 +293,7 @@ endif
|
|||
CFLAGS_parse-events-flex.o += $(flex_flags)
|
||||
CFLAGS_pmu-flex.o += $(flex_flags)
|
||||
CFLAGS_expr-flex.o += $(flex_flags)
|
||||
CFLAGS_bpf-filter-flex.o += $(flex_flags)
|
||||
|
||||
bison_flags := -DYYENABLE_NLS=0
|
||||
BISON_GE_35 := $(shell expr $(shell $(BISON) --version | grep bison | sed -e 's/.\+ \([0-9]\+\).\([0-9]\+\)/\1\2/g') \>\= 35)
|
||||
|
@ -291,10 +305,12 @@ endif
|
|||
CFLAGS_parse-events-bison.o += $(bison_flags)
|
||||
CFLAGS_pmu-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
|
||||
CFLAGS_expr-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
|
||||
CFLAGS_bpf-filter-bison.o += -DYYLTYPE_IS_TRIVIAL=0 $(bison_flags)
|
||||
|
||||
$(OUTPUT)util/parse-events.o: $(OUTPUT)util/parse-events-flex.c $(OUTPUT)util/parse-events-bison.c
|
||||
$(OUTPUT)util/pmu.o: $(OUTPUT)util/pmu-flex.c $(OUTPUT)util/pmu-bison.c
|
||||
$(OUTPUT)util/expr.o: $(OUTPUT)util/expr-flex.c $(OUTPUT)util/expr-bison.c
|
||||
$(OUTPUT)util/bpf-filter.o: $(OUTPUT)util/bpf-filter-flex.c $(OUTPUT)util/bpf-filter-bison.c
|
||||
|
||||
CFLAGS_bitmap.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
CFLAGS_find_bit.o += -Wno-unused-parameter -DETC_PERFCONFIG="BUILD_STR($(ETC_PERFCONFIG_SQ))"
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "util/bpf-filter.h"
|
||||
#include "util/bpf-filter-flex.h"
|
||||
#include "util/bpf-filter-bison.h"
|
||||
|
||||
struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags,
|
||||
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->op = op;
|
||||
expr->val = val;
|
||||
}
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef PERF_UTIL_BPF_FILTER_H
|
||||
#define PERF_UTIL_BPF_FILTER_H
|
||||
|
||||
#include <linux/list.h>
|
||||
|
||||
enum perf_bpf_filter_op {
|
||||
PBF_OP_EQ,
|
||||
PBF_OP_NEQ,
|
||||
PBF_OP_GT,
|
||||
PBF_OP_GE,
|
||||
PBF_OP_LT,
|
||||
PBF_OP_LE,
|
||||
PBF_OP_AND,
|
||||
};
|
||||
|
||||
struct perf_bpf_filter_expr {
|
||||
struct list_head list;
|
||||
enum perf_bpf_filter_op op;
|
||||
unsigned long sample_flags;
|
||||
unsigned long val;
|
||||
};
|
||||
|
||||
#ifdef HAVE_BPF_SKEL
|
||||
struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flags,
|
||||
enum perf_bpf_filter_op op,
|
||||
unsigned long val);
|
||||
int perf_bpf_filter__parse(struct list_head *expr_head, const char *str);
|
||||
#else /* !HAVE_BPF_SKEL */
|
||||
static inline int perf_bpf_filter__parse(struct list_head *expr_head __maybe_unused,
|
||||
const char *str __maybe_unused)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* HAVE_BPF_SKEL*/
|
||||
#endif /* PERF_UTIL_BPF_FILTER_H */
|
|
@ -0,0 +1,82 @@
|
|||
%option prefix="perf_bpf_filter_"
|
||||
%option noyywrap
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <linux/perf_event.h>
|
||||
|
||||
#include "bpf-filter.h"
|
||||
#include "bpf-filter-bison.h"
|
||||
|
||||
static int sample(unsigned long sample_flag)
|
||||
{
|
||||
perf_bpf_filter_lval.sample = sample_flag;
|
||||
return BFT_SAMPLE;
|
||||
}
|
||||
|
||||
static int operator(enum perf_bpf_filter_op op)
|
||||
{
|
||||
perf_bpf_filter_lval.op = op;
|
||||
return BFT_OP;
|
||||
}
|
||||
|
||||
static int value(int base)
|
||||
{
|
||||
long num;
|
||||
|
||||
errno = 0;
|
||||
num = strtoul(perf_bpf_filter_text, NULL, base);
|
||||
if (errno)
|
||||
return BFT_ERROR;
|
||||
|
||||
perf_bpf_filter_lval.num = num;
|
||||
return BFT_NUM;
|
||||
}
|
||||
|
||||
static int error(const char *str)
|
||||
{
|
||||
printf("perf_bpf_filter: Unexpected filter %s: %s\n", str, perf_bpf_filter_text);
|
||||
return BFT_ERROR;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
num_dec [0-9]+
|
||||
num_hex 0[Xx][0-9a-fA-F]+
|
||||
space [ \t]+
|
||||
ident [_a-zA-Z][_a-zA-Z0-9]+
|
||||
|
||||
%%
|
||||
|
||||
{num_dec} { return value(10); }
|
||||
{num_hex} { return value(16); }
|
||||
{space} { }
|
||||
|
||||
ip { return sample(PERF_SAMPLE_IP); }
|
||||
id { return sample(PERF_SAMPLE_ID); }
|
||||
tid { return sample(PERF_SAMPLE_TID); }
|
||||
cpu { return sample(PERF_SAMPLE_CPU); }
|
||||
time { return sample(PERF_SAMPLE_TIME); }
|
||||
addr { return sample(PERF_SAMPLE_ADDR); }
|
||||
period { return sample(PERF_SAMPLE_PERIOD); }
|
||||
txn { return sample(PERF_SAMPLE_TRANSACTION); }
|
||||
weight { return sample(PERF_SAMPLE_WEIGHT); }
|
||||
phys_addr { return sample(PERF_SAMPLE_PHYS_ADDR); }
|
||||
code_pgsz { return sample(PERF_SAMPLE_CODE_PAGE_SIZE); }
|
||||
data_pgsz { return sample(PERF_SAMPLE_DATA_PAGE_SIZE); }
|
||||
|
||||
"==" { return operator(PBF_OP_EQ); }
|
||||
"!=" { return operator(PBF_OP_NEQ); }
|
||||
">" { return operator(PBF_OP_GT); }
|
||||
"<" { return operator(PBF_OP_LT); }
|
||||
">=" { return operator(PBF_OP_GE); }
|
||||
"<=" { return operator(PBF_OP_LE); }
|
||||
"&" { return operator(PBF_OP_AND); }
|
||||
|
||||
"," { return ','; }
|
||||
|
||||
{ident} { return error("ident"); }
|
||||
. { return error("input"); }
|
||||
|
||||
%%
|
|
@ -0,0 +1,54 @@
|
|||
%parse-param {struct list_head *expr_head}
|
||||
%define parse.error verbose
|
||||
|
||||
%{
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/list.h>
|
||||
#include "bpf-filter.h"
|
||||
|
||||
static void perf_bpf_filter_error(struct list_head *expr __maybe_unused,
|
||||
char const *msg)
|
||||
{
|
||||
printf("perf_bpf_filter: %s\n", msg);
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
%union
|
||||
{
|
||||
unsigned long num;
|
||||
unsigned long sample;
|
||||
enum perf_bpf_filter_op op;
|
||||
struct perf_bpf_filter_expr *expr;
|
||||
}
|
||||
|
||||
%token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM
|
||||
%type <expr> filter_term
|
||||
%destructor { free ($$); } <expr>
|
||||
%type <sample> BFT_SAMPLE
|
||||
%type <op> BFT_OP
|
||||
%type <num> BFT_NUM
|
||||
|
||||
%%
|
||||
|
||||
filter:
|
||||
filter ',' filter_term
|
||||
{
|
||||
list_add_tail(&$3->list, expr_head);
|
||||
}
|
||||
|
|
||||
filter_term
|
||||
{
|
||||
list_add_tail(&$1->list, expr_head);
|
||||
}
|
||||
|
||||
filter_term:
|
||||
BFT_SAMPLE BFT_OP BFT_NUM
|
||||
{
|
||||
$$ = perf_bpf_filter_expr__new($1, $2, $3);
|
||||
}
|
||||
|
||||
%%
|
Loading…
Reference in New Issue