Commit 46996dd7 authored by Namhyung Kim's avatar Namhyung Kim Committed by Arnaldo Carvalho de Melo

perf bpf filter: Add logical OR operator

It supports two or more expressions connected as a group and the group
result is considered true when one of them returns true.  The new group
operators (GROUP_BEGIN and GROUP_END) are added to setup and check the
condition.  As it doesn't allow nested groups, the condition is saved
in local variables.

For example, the following is to get samples only if the data source
memory level is L2 cache or the weight value is greater than 30.

  $ sudo ./perf record -adW -e cpu/mem-loads/pp \
  > --filter 'mem_lvl == l2 || weight > 30' -- sleep 1

  $ sudo ./perf script -F data_src,weight
     10668100842 |OP LOAD|LVL L3 or L3 hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A		    47
     11868100242 |OP LOAD|LVL LFB/MAB or LFB/MAB hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A      57
     10668100842 |OP LOAD|LVL L3 or L3 hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A                56
     10650100842 |OP LOAD|LVL L3 or L3 hit|SNP None|TLB L2 miss|LCK No|BLK  N/A                    144
     10468100442 |OP LOAD|LVL L2 or L2 hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A                16
     10468100442 |OP LOAD|LVL L2 or L2 hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A                20
     11868100242 |OP LOAD|LVL LFB/MAB or LFB/MAB hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A     189
     1026a100142 |OP LOAD|LVL L1 or L1 hit|SNP None|TLB L1 or L2 hit|LCK Yes|BLK  N/A              193
     10468100442 |OP LOAD|LVL L2 or L2 hit|SNP None|TLB L1 or L2 hit|LCK No|BLK  N/A                18
     ...
Signed-off-by: default avatarNamhyung Kim <namhyung@kernel.org>
Acked-by: default avatarJiri 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.orgSigned-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent ff612055
...@@ -42,8 +42,32 @@ int perf_bpf_filter__prepare(struct evsel *evsel) ...@@ -42,8 +42,32 @@ int perf_bpf_filter__prepare(struct evsel *evsel)
}; };
bpf_map_update_elem(fd, &i, &entry, BPF_ANY); bpf_map_update_elem(fd, &i, &entry, BPF_ANY);
i++; 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; prog = skel->progs.perf_sample_filter;
for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) {
for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) {
...@@ -89,6 +113,7 @@ struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flag ...@@ -89,6 +113,7 @@ struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(unsigned long sample_flag
expr->part = part; expr->part = part;
expr->op = op; expr->op = op;
expr->val = val; expr->val = val;
INIT_LIST_HEAD(&expr->groups);
} }
return expr; return expr;
} }
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
struct perf_bpf_filter_expr { struct perf_bpf_filter_expr {
struct list_head list; struct list_head list;
struct list_head groups;
enum perf_bpf_filter_op op; enum perf_bpf_filter_op op;
int part; int part;
unsigned long sample_flags; unsigned long sample_flags;
......
...@@ -151,6 +151,7 @@ hops2 { return constant(PERF_MEM_HOPS_2); } ...@@ -151,6 +151,7 @@ hops2 { return constant(PERF_MEM_HOPS_2); }
hops3 { return constant(PERF_MEM_HOPS_3); } hops3 { return constant(PERF_MEM_HOPS_3); }
"," { return ','; } "," { return ','; }
"||" { return BFT_LOGICAL_OR; }
{ident} { return error("ident"); } {ident} { return error("ident"); }
. { return error("input"); } . { return error("input"); }
......
...@@ -28,8 +28,8 @@ static void perf_bpf_filter_error(struct list_head *expr __maybe_unused, ...@@ -28,8 +28,8 @@ static void perf_bpf_filter_error(struct list_head *expr __maybe_unused,
struct perf_bpf_filter_expr *expr; struct perf_bpf_filter_expr *expr;
} }
%token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM %token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM BFT_LOGICAL_OR
%type <expr> filter_term %type <expr> filter_term filter_expr
%destructor { free ($$); } <expr> %destructor { free ($$); } <expr>
%type <sample> BFT_SAMPLE %type <sample> BFT_SAMPLE
%type <op> BFT_OP %type <op> BFT_OP
...@@ -49,6 +49,27 @@ filter_term ...@@ -49,6 +49,27 @@ filter_term
} }
filter_term: filter_term:
filter_term BFT_LOGICAL_OR filter_expr
{
struct perf_bpf_filter_expr *expr;
if ($1->op == PBF_OP_GROUP_BEGIN) {
expr = $1;
} else {
expr = perf_bpf_filter_expr__new(0, 0, PBF_OP_GROUP_BEGIN, 1);
list_add_tail(&$1->list, &expr->groups);
}
expr->val++;
list_add_tail(&$3->list, &expr->groups);
$$ = expr;
}
|
filter_expr
{
$$ = $1;
}
filter_expr:
BFT_SAMPLE BFT_OP BFT_NUM BFT_SAMPLE BFT_OP BFT_NUM
{ {
$$ = perf_bpf_filter_expr__new($1.type, $1.part, $2, $3); $$ = perf_bpf_filter_expr__new($1.type, $1.part, $2, $3);
......
#ifndef PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H #ifndef PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H
#define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H #define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H
#define MAX_FILTERS 32 #define MAX_FILTERS 64
/* supported filter operations */ /* supported filter operations */
enum perf_bpf_filter_op { enum perf_bpf_filter_op {
...@@ -11,7 +11,9 @@ enum perf_bpf_filter_op { ...@@ -11,7 +11,9 @@ enum perf_bpf_filter_op {
PBF_OP_GE, PBF_OP_GE,
PBF_OP_LT, PBF_OP_LT,
PBF_OP_LE, PBF_OP_LE,
PBF_OP_AND PBF_OP_AND,
PBF_OP_GROUP_BEGIN,
PBF_OP_GROUP_END,
}; };
/* BPF map entry for filtering */ /* BPF map entry for filtering */
......
...@@ -99,6 +99,14 @@ static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx, ...@@ -99,6 +99,14 @@ static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx,
return 0; return 0;
} }
#define CHECK_RESULT(data, op, val) \
if (!(data op val)) { \
if (!in_group) \
goto drop; \
} else if (in_group) { \
group_result = 1; \
}
/* BPF program to be called from perf event overflow handler */ /* BPF program to be called from perf event overflow handler */
SEC("perf_event") SEC("perf_event")
int perf_sample_filter(void *ctx) int perf_sample_filter(void *ctx)
...@@ -106,6 +114,8 @@ int perf_sample_filter(void *ctx) ...@@ -106,6 +114,8 @@ int perf_sample_filter(void *ctx)
struct bpf_perf_event_data_kern *kctx; struct bpf_perf_event_data_kern *kctx;
struct perf_bpf_filter_entry *entry; struct perf_bpf_filter_entry *entry;
__u64 sample_data; __u64 sample_data;
int in_group = 0;
int group_result = 0;
int i; int i;
kctx = bpf_cast_to_kern_ctx(ctx); kctx = bpf_cast_to_kern_ctx(ctx);
...@@ -120,32 +130,34 @@ int perf_sample_filter(void *ctx) ...@@ -120,32 +130,34 @@ int perf_sample_filter(void *ctx)
switch (entry->op) { switch (entry->op) {
case PBF_OP_EQ: case PBF_OP_EQ:
if (!(sample_data == entry->value)) CHECK_RESULT(sample_data, ==, entry->value)
goto drop;
break; break;
case PBF_OP_NEQ: case PBF_OP_NEQ:
if (!(sample_data != entry->value)) CHECK_RESULT(sample_data, !=, entry->value)
goto drop;
break; break;
case PBF_OP_GT: case PBF_OP_GT:
if (!(sample_data > entry->value)) CHECK_RESULT(sample_data, >, entry->value)
goto drop;
break; break;
case PBF_OP_GE: case PBF_OP_GE:
if (!(sample_data >= entry->value)) CHECK_RESULT(sample_data, >=, entry->value)
goto drop;
break; break;
case PBF_OP_LT: case PBF_OP_LT:
if (!(sample_data < entry->value)) CHECK_RESULT(sample_data, <, entry->value)
goto drop;
break; break;
case PBF_OP_LE: case PBF_OP_LE:
if (!(sample_data <= entry->value)) CHECK_RESULT(sample_data, <=, entry->value)
goto drop;
break; break;
case PBF_OP_AND: case PBF_OP_AND:
if (!(sample_data & entry->value)) CHECK_RESULT(sample_data, &, entry->value)
break;
case PBF_OP_GROUP_BEGIN:
in_group = 1;
group_result = 0;
break;
case PBF_OP_GROUP_END:
if (group_result == 0)
goto drop; goto drop;
in_group = 0;
break; break;
} }
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment