Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: 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>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
46996dd7 ff612055

+79 -17
+25
tools/perf/util/bpf-filter.c
··· 42 42 }; 43 43 bpf_map_update_elem(fd, &i, &entry, BPF_ANY); 44 44 i++; 45 + 46 + if (expr->op == PBF_OP_GROUP_BEGIN) { 47 + struct perf_bpf_filter_expr *group; 48 + 49 + list_for_each_entry(group, &expr->groups, list) { 50 + struct perf_bpf_filter_entry group_entry = { 51 + .op = group->op, 52 + .part = group->part, 53 + .flags = group->sample_flags, 54 + .value = group->val, 55 + }; 56 + bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY); 57 + i++; 58 + } 59 + 60 + memset(&entry, 0, sizeof(entry)); 61 + entry.op = PBF_OP_GROUP_END; 62 + bpf_map_update_elem(fd, &i, &entry, BPF_ANY); 63 + i++; 64 + } 45 65 } 46 66 67 + if (i > MAX_FILTERS) { 68 + pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS); 69 + return -1; 70 + } 47 71 prog = skel->progs.perf_sample_filter; 48 72 for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { 49 73 for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { ··· 113 89 expr->part = part; 114 90 expr->op = op; 115 91 expr->val = val; 92 + INIT_LIST_HEAD(&expr->groups); 116 93 } 117 94 return expr; 118 95 }
+1
tools/perf/util/bpf-filter.h
··· 8 8 9 9 struct perf_bpf_filter_expr { 10 10 struct list_head list; 11 + struct list_head groups; 11 12 enum perf_bpf_filter_op op; 12 13 int part; 13 14 unsigned long sample_flags;
+1
tools/perf/util/bpf-filter.l
··· 151 151 hops3 { return constant(PERF_MEM_HOPS_3); } 152 152 153 153 "," { return ','; } 154 + "||" { return BFT_LOGICAL_OR; } 154 155 155 156 {ident} { return error("ident"); } 156 157 . { return error("input"); }
+23 -2
tools/perf/util/bpf-filter.y
··· 28 28 struct perf_bpf_filter_expr *expr; 29 29 } 30 30 31 - %token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM 32 - %type <expr> filter_term 31 + %token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM BFT_LOGICAL_OR 32 + %type <expr> filter_term filter_expr 33 33 %destructor { free ($$); } <expr> 34 34 %type <sample> BFT_SAMPLE 35 35 %type <op> BFT_OP ··· 49 49 } 50 50 51 51 filter_term: 52 + filter_term BFT_LOGICAL_OR filter_expr 53 + { 54 + struct perf_bpf_filter_expr *expr; 55 + 56 + if ($1->op == PBF_OP_GROUP_BEGIN) { 57 + expr = $1; 58 + } else { 59 + expr = perf_bpf_filter_expr__new(0, 0, PBF_OP_GROUP_BEGIN, 1); 60 + list_add_tail(&$1->list, &expr->groups); 61 + } 62 + expr->val++; 63 + list_add_tail(&$3->list, &expr->groups); 64 + $$ = expr; 65 + } 66 + | 67 + filter_expr 68 + { 69 + $$ = $1; 70 + } 71 + 72 + filter_expr: 52 73 BFT_SAMPLE BFT_OP BFT_NUM 53 74 { 54 75 $$ = perf_bpf_filter_expr__new($1.type, $1.part, $2, $3);
+4 -2
tools/perf/util/bpf_skel/sample-filter.h
··· 1 1 #ifndef PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H 2 2 #define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H 3 3 4 - #define MAX_FILTERS 32 4 + #define MAX_FILTERS 64 5 5 6 6 /* supported filter operations */ 7 7 enum perf_bpf_filter_op { ··· 11 11 PBF_OP_GE, 12 12 PBF_OP_LT, 13 13 PBF_OP_LE, 14 - PBF_OP_AND 14 + PBF_OP_AND, 15 + PBF_OP_GROUP_BEGIN, 16 + PBF_OP_GROUP_END, 15 17 }; 16 18 17 19 /* BPF map entry for filtering */
+25 -13
tools/perf/util/bpf_skel/sample_filter.bpf.c
··· 99 99 return 0; 100 100 } 101 101 102 + #define CHECK_RESULT(data, op, val) \ 103 + if (!(data op val)) { \ 104 + if (!in_group) \ 105 + goto drop; \ 106 + } else if (in_group) { \ 107 + group_result = 1; \ 108 + } 109 + 102 110 /* BPF program to be called from perf event overflow handler */ 103 111 SEC("perf_event") 104 112 int perf_sample_filter(void *ctx) ··· 114 106 struct bpf_perf_event_data_kern *kctx; 115 107 struct perf_bpf_filter_entry *entry; 116 108 __u64 sample_data; 109 + int in_group = 0; 110 + int group_result = 0; 117 111 int i; 118 112 119 113 kctx = bpf_cast_to_kern_ctx(ctx); ··· 130 120 131 121 switch (entry->op) { 132 122 case PBF_OP_EQ: 133 - if (!(sample_data == entry->value)) 134 - goto drop; 123 + CHECK_RESULT(sample_data, ==, entry->value) 135 124 break; 136 125 case PBF_OP_NEQ: 137 - if (!(sample_data != entry->value)) 138 - goto drop; 126 + CHECK_RESULT(sample_data, !=, entry->value) 139 127 break; 140 128 case PBF_OP_GT: 141 - if (!(sample_data > entry->value)) 142 - goto drop; 129 + CHECK_RESULT(sample_data, >, entry->value) 143 130 break; 144 131 case PBF_OP_GE: 145 - if (!(sample_data >= entry->value)) 146 - goto drop; 132 + CHECK_RESULT(sample_data, >=, entry->value) 147 133 break; 148 134 case PBF_OP_LT: 149 - if (!(sample_data < entry->value)) 150 - goto drop; 135 + CHECK_RESULT(sample_data, <, entry->value) 151 136 break; 152 137 case PBF_OP_LE: 153 - if (!(sample_data <= entry->value)) 154 - goto drop; 138 + CHECK_RESULT(sample_data, <=, entry->value) 155 139 break; 156 140 case PBF_OP_AND: 157 - if (!(sample_data & entry->value)) 141 + CHECK_RESULT(sample_data, &, entry->value) 142 + break; 143 + case PBF_OP_GROUP_BEGIN: 144 + in_group = 1; 145 + group_result = 0; 146 + break; 147 + case PBF_OP_GROUP_END: 148 + if (group_result == 0) 158 149 goto drop; 150 + in_group = 0; 159 151 break; 160 152 } 161 153 }