1 /* SPDX-License-Identifier: GPL-2.0 */ 2 #include <stdlib.h> 3 4 #include <bpf/bpf.h> 5 #include <linux/err.h> 6 #include <internal/xyarray.h> 7 8 #include "util/debug.h" 9 #include "util/evsel.h" 10 11 #include "util/bpf-filter.h" 12 #include <util/bpf-filter-flex.h> 13 #include <util/bpf-filter-bison.h> 14 15 #include "bpf_skel/sample-filter.h" 16 #include "bpf_skel/sample_filter.skel.h" 17 18 #define FD(e, x, y) (*(int *)xyarray__entry(e->core.fd, x, y)) 19 20 #define __PERF_SAMPLE_TYPE(tt, st, opt) { tt, #st, opt } 21 #define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PBF_TERM_##_st, PERF_SAMPLE_##_st, opt) 22 23 static const struct perf_sample_info { 24 enum perf_bpf_filter_term type; 25 const char *name; 26 const char *option; 27 } sample_table[] = { 28 /* default sample flags */ 29 PERF_SAMPLE_TYPE(IP, NULL), 30 PERF_SAMPLE_TYPE(TID, NULL), 31 PERF_SAMPLE_TYPE(PERIOD, NULL), 32 /* flags mostly set by default, but still have options */ 33 PERF_SAMPLE_TYPE(ID, "--sample-identifier"), 34 PERF_SAMPLE_TYPE(CPU, "--sample-cpu"), 35 PERF_SAMPLE_TYPE(TIME, "-T"), 36 /* optional sample flags */ 37 PERF_SAMPLE_TYPE(ADDR, "-d"), 38 PERF_SAMPLE_TYPE(DATA_SRC, "-d"), 39 PERF_SAMPLE_TYPE(PHYS_ADDR, "--phys-data"), 40 PERF_SAMPLE_TYPE(WEIGHT, "-W"), 41 PERF_SAMPLE_TYPE(WEIGHT_STRUCT, "-W"), 42 PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"), 43 PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"), 44 PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"), 45 }; 46 47 static const struct perf_sample_info *get_sample_info(enum perf_bpf_filter_term type) 48 { 49 size_t i; 50 51 for (i = 0; i < ARRAY_SIZE(sample_table); i++) { 52 if (sample_table[i].type == type) 53 return &sample_table[i]; 54 } 55 return NULL; 56 } 57 58 static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr *expr) 59 { 60 const struct perf_sample_info *info; 61 62 if (expr->term >= PBF_TERM_SAMPLE_START && expr->term <= PBF_TERM_SAMPLE_END && 63 (evsel->core.attr.sample_type & (1 << (expr->term - PBF_TERM_SAMPLE_START)))) 64 return 0; 65 66 if (expr->term == PBF_TERM_UID || expr->term == PBF_TERM_GID) { 67 /* Not dependent on the sample_type as computed from a BPF helper. */ 68 return 0; 69 } 70 71 if (expr->op == PBF_OP_GROUP_BEGIN) { 72 struct perf_bpf_filter_expr *group; 73 74 list_for_each_entry(group, &expr->groups, list) { 75 if (check_sample_flags(evsel, group) < 0) 76 return -1; 77 } 78 return 0; 79 } 80 81 info = get_sample_info(expr->term); 82 if (info == NULL) { 83 pr_err("Error: %s event does not have sample flags %d\n", 84 evsel__name(evsel), expr->term); 85 return -1; 86 } 87 88 pr_err("Error: %s event does not have %s\n", evsel__name(evsel), info->name); 89 if (info->option) 90 pr_err(" Hint: please add %s option to perf record\n", info->option); 91 return -1; 92 } 93 94 int perf_bpf_filter__prepare(struct evsel *evsel) 95 { 96 int i, x, y, fd; 97 struct sample_filter_bpf *skel; 98 struct bpf_program *prog; 99 struct bpf_link *link; 100 struct perf_bpf_filter_expr *expr; 101 102 skel = sample_filter_bpf__open_and_load(); 103 if (!skel) { 104 pr_err("Failed to load perf sample-filter BPF skeleton\n"); 105 return -1; 106 } 107 108 i = 0; 109 fd = bpf_map__fd(skel->maps.filters); 110 list_for_each_entry(expr, &evsel->bpf_filters, list) { 111 struct perf_bpf_filter_entry entry = { 112 .op = expr->op, 113 .part = expr->part, 114 .term = expr->term, 115 .value = expr->val, 116 }; 117 118 if (check_sample_flags(evsel, expr) < 0) 119 return -1; 120 121 bpf_map_update_elem(fd, &i, &entry, BPF_ANY); 122 i++; 123 124 if (expr->op == PBF_OP_GROUP_BEGIN) { 125 struct perf_bpf_filter_expr *group; 126 127 list_for_each_entry(group, &expr->groups, list) { 128 struct perf_bpf_filter_entry group_entry = { 129 .op = group->op, 130 .part = group->part, 131 .term = group->term, 132 .value = group->val, 133 }; 134 bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY); 135 i++; 136 } 137 138 memset(&entry, 0, sizeof(entry)); 139 entry.op = PBF_OP_GROUP_END; 140 bpf_map_update_elem(fd, &i, &entry, BPF_ANY); 141 i++; 142 } 143 } 144 145 if (i > MAX_FILTERS) { 146 pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS); 147 return -1; 148 } 149 prog = skel->progs.perf_sample_filter; 150 for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { 151 for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { 152 link = bpf_program__attach_perf_event(prog, FD(evsel, x, y)); 153 if (IS_ERR(link)) { 154 pr_err("Failed to attach perf sample-filter program\n"); 155 return PTR_ERR(link); 156 } 157 } 158 } 159 evsel->bpf_skel = skel; 160 return 0; 161 } 162 163 int perf_bpf_filter__destroy(struct evsel *evsel) 164 { 165 struct perf_bpf_filter_expr *expr, *tmp; 166 167 list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) { 168 list_del(&expr->list); 169 free(expr); 170 } 171 sample_filter_bpf__destroy(evsel->bpf_skel); 172 return 0; 173 } 174 175 u64 perf_bpf_filter__lost_count(struct evsel *evsel) 176 { 177 struct sample_filter_bpf *skel = evsel->bpf_skel; 178 179 return skel ? skel->bss->dropped : 0; 180 } 181 182 struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term term, 183 int part, 184 enum perf_bpf_filter_op op, 185 unsigned long val) 186 { 187 struct perf_bpf_filter_expr *expr; 188 189 expr = malloc(sizeof(*expr)); 190 if (expr != NULL) { 191 expr->term = term; 192 expr->part = part; 193 expr->op = op; 194 expr->val = val; 195 INIT_LIST_HEAD(&expr->groups); 196 } 197 return expr; 198 } 199 200 int perf_bpf_filter__parse(struct list_head *expr_head, const char *str) 201 { 202 YY_BUFFER_STATE buffer; 203 int ret; 204 205 buffer = perf_bpf_filter__scan_string(str); 206 207 ret = perf_bpf_filter_parse(expr_head); 208 209 perf_bpf_filter__flush_buffer(buffer); 210 perf_bpf_filter__delete_buffer(buffer); 211 perf_bpf_filter_lex_destroy(); 212 213 return ret; 214 } 215
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.