selftests/bpf: consolidate and improve file/prog filtering in veristat
Slightly change rules of specifying file/prog glob filters. In practice it's quite often inconvenient to do `*/<prog-glob>` if that program glob is unique enough and won't accidentally match any file names. This patch changes the rules so that `-f <glob>` will apply specified glob to both file and program names. User still has all the control by doing '*/<prog-only-glob>' or '<file-only-glob/*'. We also now allow '/<prog-glob>' and '<file-glob/' (all matching wildcard is assumed if missing). Also, internally unify file-only and file+prog checks (should_process_file and should_process_prog are now should_process_file_prog that can handle prog name as optional). This makes maintaining and extending this code easier. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Link: https://lore.kernel.org/r/20221103055304.2904589-4-andrii@kernel.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
62d2c08bb9
commit
10b1b3f3e5
1 changed files with 66 additions and 63 deletions
|
@ -55,6 +55,7 @@ enum resfmt {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct filter {
|
struct filter {
|
||||||
|
char *any_glob;
|
||||||
char *file_glob;
|
char *file_glob;
|
||||||
char *prog_glob;
|
char *prog_glob;
|
||||||
};
|
};
|
||||||
|
@ -231,28 +232,6 @@ static bool glob_matches(const char *str, const char *pat)
|
||||||
return !*str && !*pat;
|
return !*str && !*pat;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool should_process_file(const char *filename)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (env.deny_filter_cnt > 0) {
|
|
||||||
for (i = 0; i < env.deny_filter_cnt; i++) {
|
|
||||||
if (glob_matches(filename, env.deny_filters[i].file_glob))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.allow_filter_cnt == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (i = 0; i < env.allow_filter_cnt; i++) {
|
|
||||||
if (glob_matches(filename, env.allow_filters[i].file_glob))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_bpf_obj_file(const char *path) {
|
static bool is_bpf_obj_file(const char *path) {
|
||||||
Elf64_Ehdr *ehdr;
|
Elf64_Ehdr *ehdr;
|
||||||
int fd, err = -EINVAL;
|
int fd, err = -EINVAL;
|
||||||
|
@ -285,38 +264,46 @@ cleanup:
|
||||||
return err == 0;
|
return err == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool should_process_prog(const char *path, const char *prog_name)
|
static bool should_process_file_prog(const char *filename, const char *prog_name)
|
||||||
{
|
{
|
||||||
const char *filename = basename(path);
|
struct filter *f;
|
||||||
int i;
|
int i, allow_cnt = 0;
|
||||||
|
|
||||||
if (env.deny_filter_cnt > 0) {
|
|
||||||
for (i = 0; i < env.deny_filter_cnt; i++) {
|
for (i = 0; i < env.deny_filter_cnt; i++) {
|
||||||
if (glob_matches(filename, env.deny_filters[i].file_glob))
|
f = &env.deny_filters[i];
|
||||||
return false;
|
|
||||||
if (!env.deny_filters[i].prog_glob)
|
|
||||||
continue;
|
|
||||||
if (glob_matches(prog_name, env.deny_filters[i].prog_glob))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env.allow_filter_cnt == 0)
|
if (f->any_glob && glob_matches(filename, f->any_glob))
|
||||||
return true;
|
return false;
|
||||||
|
if (f->any_glob && prog_name && glob_matches(prog_name, f->any_glob))
|
||||||
|
return false;
|
||||||
|
if (f->file_glob && glob_matches(filename, f->file_glob))
|
||||||
|
return false;
|
||||||
|
if (f->prog_glob && prog_name && glob_matches(prog_name, f->prog_glob))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < env.allow_filter_cnt; i++) {
|
for (i = 0; i < env.allow_filter_cnt; i++) {
|
||||||
if (!glob_matches(filename, env.allow_filters[i].file_glob))
|
f = &env.allow_filters[i];
|
||||||
continue;
|
allow_cnt++;
|
||||||
/* if filter specifies only filename glob part, it implicitly
|
|
||||||
* allows all progs within that file
|
if (f->any_glob) {
|
||||||
*/
|
if (glob_matches(filename, f->any_glob))
|
||||||
if (!env.allow_filters[i].prog_glob)
|
|
||||||
return true;
|
return true;
|
||||||
if (glob_matches(prog_name, env.allow_filters[i].prog_glob))
|
if (prog_name && glob_matches(prog_name, f->any_glob))
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (f->file_glob && !glob_matches(filename, f->file_glob))
|
||||||
|
continue;
|
||||||
|
if (f->prog_glob && prog_name && !glob_matches(prog_name, f->prog_glob))
|
||||||
|
continue;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
/* if there are no file/prog name allow filters, allow all progs,
|
||||||
|
* unless they are denied earlier explicitly
|
||||||
|
*/
|
||||||
|
return allow_cnt == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int append_filter(struct filter **filters, int *cnt, const char *str)
|
static int append_filter(struct filter **filters, int *cnt, const char *str)
|
||||||
|
@ -331,26 +318,40 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
|
||||||
*filters = tmp;
|
*filters = tmp;
|
||||||
|
|
||||||
f = &(*filters)[*cnt];
|
f = &(*filters)[*cnt];
|
||||||
f->file_glob = f->prog_glob = NULL;
|
memset(f, 0, sizeof(*f));
|
||||||
|
|
||||||
/* filter can be specified either as "<obj-glob>" or "<obj-glob>/<prog-glob>" */
|
/* File/prog filter can be specified either as '<glob>' or
|
||||||
|
* '<file-glob>/<prog-glob>'. In the former case <glob> is applied to
|
||||||
|
* both file and program names. This seems to be way more useful in
|
||||||
|
* practice. If user needs full control, they can use '/<prog-glob>'
|
||||||
|
* form to glob just program name, or '<file-glob>/' to glob only file
|
||||||
|
* name. But usually common <glob> seems to be the most useful and
|
||||||
|
* ergonomic way.
|
||||||
|
*/
|
||||||
p = strchr(str, '/');
|
p = strchr(str, '/');
|
||||||
if (!p) {
|
if (!p) {
|
||||||
f->file_glob = strdup(str);
|
f->any_glob = strdup(str);
|
||||||
if (!f->file_glob)
|
if (!f->any_glob)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
} else {
|
} else {
|
||||||
|
if (str != p) {
|
||||||
|
/* non-empty file glob */
|
||||||
f->file_glob = strndup(str, p - str);
|
f->file_glob = strndup(str, p - str);
|
||||||
f->prog_glob = strdup(p + 1);
|
if (!f->file_glob)
|
||||||
if (!f->file_glob || !f->prog_glob) {
|
|
||||||
free(f->file_glob);
|
|
||||||
free(f->prog_glob);
|
|
||||||
f->file_glob = f->prog_glob = NULL;
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
if (strlen(p + 1) > 0) {
|
||||||
|
/* non-empty prog glob */
|
||||||
|
f->prog_glob = strdup(p + 1);
|
||||||
|
if (!f->prog_glob) {
|
||||||
|
free(f->file_glob);
|
||||||
|
f->file_glob = NULL;
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*cnt = *cnt + 1;
|
*cnt += 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +547,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
|
||||||
int err = 0;
|
int err = 0;
|
||||||
void *tmp;
|
void *tmp;
|
||||||
|
|
||||||
if (!should_process_prog(filename, bpf_program__name(prog))) {
|
if (!should_process_file_prog(basename(filename), bpf_program__name(prog))) {
|
||||||
env.progs_skipped++;
|
env.progs_skipped++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -602,7 +603,7 @@ static int process_obj(const char *filename)
|
||||||
LIBBPF_OPTS(bpf_object_open_opts, opts);
|
LIBBPF_OPTS(bpf_object_open_opts, opts);
|
||||||
int err = 0, prog_cnt = 0;
|
int err = 0, prog_cnt = 0;
|
||||||
|
|
||||||
if (!should_process_file(basename(filename))) {
|
if (!should_process_file_prog(basename(filename), NULL)) {
|
||||||
if (env.verbose)
|
if (env.verbose)
|
||||||
printf("Skipping '%s' due to filters...\n", filename);
|
printf("Skipping '%s' due to filters...\n", filename);
|
||||||
env.files_skipped++;
|
env.files_skipped++;
|
||||||
|
@ -980,7 +981,7 @@ static int parse_stats_csv(const char *filename, struct stat_specs *specs,
|
||||||
* parsed entire line; if row should be ignored we pretend we
|
* parsed entire line; if row should be ignored we pretend we
|
||||||
* never parsed it
|
* never parsed it
|
||||||
*/
|
*/
|
||||||
if (!should_process_prog(st->file_name, st->prog_name)) {
|
if (!should_process_file_prog(st->file_name, st->prog_name)) {
|
||||||
free(st->file_name);
|
free(st->file_name);
|
||||||
free(st->prog_name);
|
free(st->prog_name);
|
||||||
*stat_cntp -= 1;
|
*stat_cntp -= 1;
|
||||||
|
@ -1391,11 +1392,13 @@ int main(int argc, char **argv)
|
||||||
free(env.filenames[i]);
|
free(env.filenames[i]);
|
||||||
free(env.filenames);
|
free(env.filenames);
|
||||||
for (i = 0; i < env.allow_filter_cnt; i++) {
|
for (i = 0; i < env.allow_filter_cnt; i++) {
|
||||||
|
free(env.allow_filters[i].any_glob);
|
||||||
free(env.allow_filters[i].file_glob);
|
free(env.allow_filters[i].file_glob);
|
||||||
free(env.allow_filters[i].prog_glob);
|
free(env.allow_filters[i].prog_glob);
|
||||||
}
|
}
|
||||||
free(env.allow_filters);
|
free(env.allow_filters);
|
||||||
for (i = 0; i < env.deny_filter_cnt; i++) {
|
for (i = 0; i < env.deny_filter_cnt; i++) {
|
||||||
|
free(env.deny_filters[i].any_glob);
|
||||||
free(env.deny_filters[i].file_glob);
|
free(env.deny_filters[i].file_glob);
|
||||||
free(env.deny_filters[i].prog_glob);
|
free(env.deny_filters[i].prog_glob);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue