Commit 10b1b3f3 authored by Andrii Nakryiko's avatar Andrii Nakryiko Committed by Alexei Starovoitov
Browse files

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: default avatarAndrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/r/20221103055304.2904589-4-andrii@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 62d2c08b
Loading
Loading
Loading
Loading
+65 −62
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ enum resfmt {
};

struct filter {
	char *any_glob;
	char *file_glob;
	char *prog_glob;
};
@@ -231,28 +232,6 @@ static bool glob_matches(const char *str, const char *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) {
	Elf64_Ehdr *ehdr;
	int fd, err = -EINVAL;
@@ -285,38 +264,46 @@ static bool is_bpf_obj_file(const char *path) {
	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);
	int i;
	struct filter *f;
	int i, allow_cnt = 0;

	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))
		f = &env.deny_filters[i];

		if (f->any_glob && glob_matches(filename, f->any_glob))
			return false;
			if (!env.deny_filters[i].prog_glob)
				continue;
			if (glob_matches(prog_name, env.deny_filters[i].prog_glob))
		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;
	}
	}

	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))
			continue;
		/* if filter specifies only filename glob part, it implicitly
		 * allows all progs within that file
		 */
		if (!env.allow_filters[i].prog_glob)
		f = &env.allow_filters[i];
		allow_cnt++;

		if (f->any_glob) {
			if (glob_matches(filename, f->any_glob))
				return true;
			if (prog_name && glob_matches(prog_name, f->any_glob))
				return true;
		if (glob_matches(prog_name, env.allow_filters[i].prog_glob))
		} 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 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)
@@ -331,26 +318,40 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
	*filters = tmp;

	f = &(*filters)[*cnt];
	f->file_glob = f->prog_glob = NULL;

	/* filter can be specified either as "<obj-glob>" or "<obj-glob>/<prog-glob>" */
	memset(f, 0, sizeof(*f));

	/* 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, '/');
	if (!p) {
		f->file_glob = strdup(str);
		if (!f->file_glob)
		f->any_glob = strdup(str);
		if (!f->any_glob)
			return -ENOMEM;
	} else {
		if (str != p) {
			/* non-empty file glob */
			f->file_glob = strndup(str, p - str);
			if (!f->file_glob)
				return -ENOMEM;
		}
		if (strlen(p + 1) > 0) {
			/* non-empty prog glob */
			f->prog_glob = strdup(p + 1);
		if (!f->file_glob || !f->prog_glob) {
			if (!f->prog_glob) {
				free(f->file_glob);
			free(f->prog_glob);
			f->file_glob = f->prog_glob = NULL;
				f->file_glob = NULL;
				return -ENOMEM;
			}
		}
	}

	*cnt = *cnt + 1;
	*cnt += 1;
	return 0;
}

@@ -546,7 +547,7 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
	int err = 0;
	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++;
		return 0;
	}
@@ -602,7 +603,7 @@ static int process_obj(const char *filename)
	LIBBPF_OPTS(bpf_object_open_opts, opts);
	int err = 0, prog_cnt = 0;

	if (!should_process_file(basename(filename))) {
	if (!should_process_file_prog(basename(filename), NULL)) {
		if (env.verbose)
			printf("Skipping '%s' due to filters...\n", filename);
		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
		 * 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->prog_name);
			*stat_cntp -= 1;
@@ -1391,11 +1392,13 @@ int main(int argc, char **argv)
		free(env.filenames[i]);
	free(env.filenames);
	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].prog_glob);
	}
	free(env.allow_filters);
	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].prog_glob);
	}