xref: /aosp_15_r20/external/linux-kselftest/tools/testing/selftests/bpf/veristat.c (revision 053f45be4e351dfd5e965df293cd45b779f579ee)
1*053f45beSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0
2*053f45beSAndroid Build Coastguard Worker /* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
3*053f45beSAndroid Build Coastguard Worker #define _GNU_SOURCE
4*053f45beSAndroid Build Coastguard Worker #include <argp.h>
5*053f45beSAndroid Build Coastguard Worker #include <string.h>
6*053f45beSAndroid Build Coastguard Worker #include <stdlib.h>
7*053f45beSAndroid Build Coastguard Worker #include <linux/compiler.h>
8*053f45beSAndroid Build Coastguard Worker #include <sched.h>
9*053f45beSAndroid Build Coastguard Worker #include <pthread.h>
10*053f45beSAndroid Build Coastguard Worker #include <dirent.h>
11*053f45beSAndroid Build Coastguard Worker #include <signal.h>
12*053f45beSAndroid Build Coastguard Worker #include <fcntl.h>
13*053f45beSAndroid Build Coastguard Worker #include <unistd.h>
14*053f45beSAndroid Build Coastguard Worker #include <sys/time.h>
15*053f45beSAndroid Build Coastguard Worker #include <sys/sysinfo.h>
16*053f45beSAndroid Build Coastguard Worker #include <sys/stat.h>
17*053f45beSAndroid Build Coastguard Worker #include <bpf/libbpf.h>
18*053f45beSAndroid Build Coastguard Worker #include <libelf.h>
19*053f45beSAndroid Build Coastguard Worker #include <gelf.h>
20*053f45beSAndroid Build Coastguard Worker 
21*053f45beSAndroid Build Coastguard Worker enum stat_id {
22*053f45beSAndroid Build Coastguard Worker 	VERDICT,
23*053f45beSAndroid Build Coastguard Worker 	DURATION,
24*053f45beSAndroid Build Coastguard Worker 	TOTAL_INSNS,
25*053f45beSAndroid Build Coastguard Worker 	TOTAL_STATES,
26*053f45beSAndroid Build Coastguard Worker 	PEAK_STATES,
27*053f45beSAndroid Build Coastguard Worker 	MAX_STATES_PER_INSN,
28*053f45beSAndroid Build Coastguard Worker 	MARK_READ_MAX_LEN,
29*053f45beSAndroid Build Coastguard Worker 
30*053f45beSAndroid Build Coastguard Worker 	FILE_NAME,
31*053f45beSAndroid Build Coastguard Worker 	PROG_NAME,
32*053f45beSAndroid Build Coastguard Worker 
33*053f45beSAndroid Build Coastguard Worker 	ALL_STATS_CNT,
34*053f45beSAndroid Build Coastguard Worker 	NUM_STATS_CNT = FILE_NAME - VERDICT,
35*053f45beSAndroid Build Coastguard Worker };
36*053f45beSAndroid Build Coastguard Worker 
37*053f45beSAndroid Build Coastguard Worker struct verif_stats {
38*053f45beSAndroid Build Coastguard Worker 	char *file_name;
39*053f45beSAndroid Build Coastguard Worker 	char *prog_name;
40*053f45beSAndroid Build Coastguard Worker 
41*053f45beSAndroid Build Coastguard Worker 	long stats[NUM_STATS_CNT];
42*053f45beSAndroid Build Coastguard Worker };
43*053f45beSAndroid Build Coastguard Worker 
44*053f45beSAndroid Build Coastguard Worker struct stat_specs {
45*053f45beSAndroid Build Coastguard Worker 	int spec_cnt;
46*053f45beSAndroid Build Coastguard Worker 	enum stat_id ids[ALL_STATS_CNT];
47*053f45beSAndroid Build Coastguard Worker 	bool asc[ALL_STATS_CNT];
48*053f45beSAndroid Build Coastguard Worker 	int lens[ALL_STATS_CNT * 3]; /* 3x for comparison mode */
49*053f45beSAndroid Build Coastguard Worker };
50*053f45beSAndroid Build Coastguard Worker 
51*053f45beSAndroid Build Coastguard Worker enum resfmt {
52*053f45beSAndroid Build Coastguard Worker 	RESFMT_TABLE,
53*053f45beSAndroid Build Coastguard Worker 	RESFMT_TABLE_CALCLEN, /* fake format to pre-calculate table's column widths */
54*053f45beSAndroid Build Coastguard Worker 	RESFMT_CSV,
55*053f45beSAndroid Build Coastguard Worker };
56*053f45beSAndroid Build Coastguard Worker 
57*053f45beSAndroid Build Coastguard Worker struct filter {
58*053f45beSAndroid Build Coastguard Worker 	char *file_glob;
59*053f45beSAndroid Build Coastguard Worker 	char *prog_glob;
60*053f45beSAndroid Build Coastguard Worker };
61*053f45beSAndroid Build Coastguard Worker 
62*053f45beSAndroid Build Coastguard Worker static struct env {
63*053f45beSAndroid Build Coastguard Worker 	char **filenames;
64*053f45beSAndroid Build Coastguard Worker 	int filename_cnt;
65*053f45beSAndroid Build Coastguard Worker 	bool verbose;
66*053f45beSAndroid Build Coastguard Worker 	bool quiet;
67*053f45beSAndroid Build Coastguard Worker 	int log_level;
68*053f45beSAndroid Build Coastguard Worker 	enum resfmt out_fmt;
69*053f45beSAndroid Build Coastguard Worker 	bool comparison_mode;
70*053f45beSAndroid Build Coastguard Worker 
71*053f45beSAndroid Build Coastguard Worker 	struct verif_stats *prog_stats;
72*053f45beSAndroid Build Coastguard Worker 	int prog_stat_cnt;
73*053f45beSAndroid Build Coastguard Worker 
74*053f45beSAndroid Build Coastguard Worker 	/* baseline_stats is allocated and used only in comparsion mode */
75*053f45beSAndroid Build Coastguard Worker 	struct verif_stats *baseline_stats;
76*053f45beSAndroid Build Coastguard Worker 	int baseline_stat_cnt;
77*053f45beSAndroid Build Coastguard Worker 
78*053f45beSAndroid Build Coastguard Worker 	struct stat_specs output_spec;
79*053f45beSAndroid Build Coastguard Worker 	struct stat_specs sort_spec;
80*053f45beSAndroid Build Coastguard Worker 
81*053f45beSAndroid Build Coastguard Worker 	struct filter *allow_filters;
82*053f45beSAndroid Build Coastguard Worker 	struct filter *deny_filters;
83*053f45beSAndroid Build Coastguard Worker 	int allow_filter_cnt;
84*053f45beSAndroid Build Coastguard Worker 	int deny_filter_cnt;
85*053f45beSAndroid Build Coastguard Worker 
86*053f45beSAndroid Build Coastguard Worker 	int files_processed;
87*053f45beSAndroid Build Coastguard Worker 	int files_skipped;
88*053f45beSAndroid Build Coastguard Worker 	int progs_processed;
89*053f45beSAndroid Build Coastguard Worker 	int progs_skipped;
90*053f45beSAndroid Build Coastguard Worker } env;
91*053f45beSAndroid Build Coastguard Worker 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)92*053f45beSAndroid Build Coastguard Worker static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
93*053f45beSAndroid Build Coastguard Worker {
94*053f45beSAndroid Build Coastguard Worker 	if (!env.verbose)
95*053f45beSAndroid Build Coastguard Worker 		return 0;
96*053f45beSAndroid Build Coastguard Worker 	if (level == LIBBPF_DEBUG /* && !env.verbose */)
97*053f45beSAndroid Build Coastguard Worker 		return 0;
98*053f45beSAndroid Build Coastguard Worker 	return vfprintf(stderr, format, args);
99*053f45beSAndroid Build Coastguard Worker }
100*053f45beSAndroid Build Coastguard Worker 
101*053f45beSAndroid Build Coastguard Worker const char *argp_program_version = "veristat";
102*053f45beSAndroid Build Coastguard Worker const char *argp_program_bug_address = "<[email protected]>";
103*053f45beSAndroid Build Coastguard Worker const char argp_program_doc[] =
104*053f45beSAndroid Build Coastguard Worker "veristat    BPF verifier stats collection and comparison tool.\n"
105*053f45beSAndroid Build Coastguard Worker "\n"
106*053f45beSAndroid Build Coastguard Worker "USAGE: veristat <obj-file> [<obj-file>...]\n"
107*053f45beSAndroid Build Coastguard Worker "   OR: veristat -C <baseline.csv> <comparison.csv>\n";
108*053f45beSAndroid Build Coastguard Worker 
109*053f45beSAndroid Build Coastguard Worker static const struct argp_option opts[] = {
110*053f45beSAndroid Build Coastguard Worker 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
111*053f45beSAndroid Build Coastguard Worker 	{ "verbose", 'v', NULL, 0, "Verbose mode" },
112*053f45beSAndroid Build Coastguard Worker 	{ "log-level", 'l', "LEVEL", 0, "Verifier log level (default 0 for normal mode, 1 for verbose mode)" },
113*053f45beSAndroid Build Coastguard Worker 	{ "quiet", 'q', NULL, 0, "Quiet mode" },
114*053f45beSAndroid Build Coastguard Worker 	{ "emit", 'e', "SPEC", 0, "Specify stats to be emitted" },
115*053f45beSAndroid Build Coastguard Worker 	{ "sort", 's', "SPEC", 0, "Specify sort order" },
116*053f45beSAndroid Build Coastguard Worker 	{ "output-format", 'o', "FMT", 0, "Result output format (table, csv), default is table." },
117*053f45beSAndroid Build Coastguard Worker 	{ "compare", 'C', NULL, 0, "Comparison mode" },
118*053f45beSAndroid Build Coastguard Worker 	{ "filter", 'f', "FILTER", 0, "Filter expressions (or @filename for file with expressions)." },
119*053f45beSAndroid Build Coastguard Worker 	{},
120*053f45beSAndroid Build Coastguard Worker };
121*053f45beSAndroid Build Coastguard Worker 
122*053f45beSAndroid Build Coastguard Worker static int parse_stats(const char *stats_str, struct stat_specs *specs);
123*053f45beSAndroid Build Coastguard Worker static int append_filter(struct filter **filters, int *cnt, const char *str);
124*053f45beSAndroid Build Coastguard Worker static int append_filter_file(const char *path);
125*053f45beSAndroid Build Coastguard Worker 
parse_arg(int key,char * arg,struct argp_state * state)126*053f45beSAndroid Build Coastguard Worker static error_t parse_arg(int key, char *arg, struct argp_state *state)
127*053f45beSAndroid Build Coastguard Worker {
128*053f45beSAndroid Build Coastguard Worker 	void *tmp;
129*053f45beSAndroid Build Coastguard Worker 	int err;
130*053f45beSAndroid Build Coastguard Worker 
131*053f45beSAndroid Build Coastguard Worker 	switch (key) {
132*053f45beSAndroid Build Coastguard Worker 	case 'h':
133*053f45beSAndroid Build Coastguard Worker 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
134*053f45beSAndroid Build Coastguard Worker 		break;
135*053f45beSAndroid Build Coastguard Worker 	case 'v':
136*053f45beSAndroid Build Coastguard Worker 		env.verbose = true;
137*053f45beSAndroid Build Coastguard Worker 		break;
138*053f45beSAndroid Build Coastguard Worker 	case 'q':
139*053f45beSAndroid Build Coastguard Worker 		env.quiet = true;
140*053f45beSAndroid Build Coastguard Worker 		break;
141*053f45beSAndroid Build Coastguard Worker 	case 'e':
142*053f45beSAndroid Build Coastguard Worker 		err = parse_stats(arg, &env.output_spec);
143*053f45beSAndroid Build Coastguard Worker 		if (err)
144*053f45beSAndroid Build Coastguard Worker 			return err;
145*053f45beSAndroid Build Coastguard Worker 		break;
146*053f45beSAndroid Build Coastguard Worker 	case 's':
147*053f45beSAndroid Build Coastguard Worker 		err = parse_stats(arg, &env.sort_spec);
148*053f45beSAndroid Build Coastguard Worker 		if (err)
149*053f45beSAndroid Build Coastguard Worker 			return err;
150*053f45beSAndroid Build Coastguard Worker 		break;
151*053f45beSAndroid Build Coastguard Worker 	case 'o':
152*053f45beSAndroid Build Coastguard Worker 		if (strcmp(arg, "table") == 0) {
153*053f45beSAndroid Build Coastguard Worker 			env.out_fmt = RESFMT_TABLE;
154*053f45beSAndroid Build Coastguard Worker 		} else if (strcmp(arg, "csv") == 0) {
155*053f45beSAndroid Build Coastguard Worker 			env.out_fmt = RESFMT_CSV;
156*053f45beSAndroid Build Coastguard Worker 		} else {
157*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Unrecognized output format '%s'\n", arg);
158*053f45beSAndroid Build Coastguard Worker 			return -EINVAL;
159*053f45beSAndroid Build Coastguard Worker 		}
160*053f45beSAndroid Build Coastguard Worker 		break;
161*053f45beSAndroid Build Coastguard Worker 	case 'l':
162*053f45beSAndroid Build Coastguard Worker 		errno = 0;
163*053f45beSAndroid Build Coastguard Worker 		env.log_level = strtol(arg, NULL, 10);
164*053f45beSAndroid Build Coastguard Worker 		if (errno) {
165*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "invalid log level: %s\n", arg);
166*053f45beSAndroid Build Coastguard Worker 			argp_usage(state);
167*053f45beSAndroid Build Coastguard Worker 		}
168*053f45beSAndroid Build Coastguard Worker 		break;
169*053f45beSAndroid Build Coastguard Worker 	case 'C':
170*053f45beSAndroid Build Coastguard Worker 		env.comparison_mode = true;
171*053f45beSAndroid Build Coastguard Worker 		break;
172*053f45beSAndroid Build Coastguard Worker 	case 'f':
173*053f45beSAndroid Build Coastguard Worker 		if (arg[0] == '@')
174*053f45beSAndroid Build Coastguard Worker 			err = append_filter_file(arg + 1);
175*053f45beSAndroid Build Coastguard Worker 		else if (arg[0] == '!')
176*053f45beSAndroid Build Coastguard Worker 			err = append_filter(&env.deny_filters, &env.deny_filter_cnt, arg + 1);
177*053f45beSAndroid Build Coastguard Worker 		else
178*053f45beSAndroid Build Coastguard Worker 			err = append_filter(&env.allow_filters, &env.allow_filter_cnt, arg);
179*053f45beSAndroid Build Coastguard Worker 		if (err) {
180*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Failed to collect program filter expressions: %d\n", err);
181*053f45beSAndroid Build Coastguard Worker 			return err;
182*053f45beSAndroid Build Coastguard Worker 		}
183*053f45beSAndroid Build Coastguard Worker 		break;
184*053f45beSAndroid Build Coastguard Worker 	case ARGP_KEY_ARG:
185*053f45beSAndroid Build Coastguard Worker 		tmp = realloc(env.filenames, (env.filename_cnt + 1) * sizeof(*env.filenames));
186*053f45beSAndroid Build Coastguard Worker 		if (!tmp)
187*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
188*053f45beSAndroid Build Coastguard Worker 		env.filenames = tmp;
189*053f45beSAndroid Build Coastguard Worker 		env.filenames[env.filename_cnt] = strdup(arg);
190*053f45beSAndroid Build Coastguard Worker 		if (!env.filenames[env.filename_cnt])
191*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
192*053f45beSAndroid Build Coastguard Worker 		env.filename_cnt++;
193*053f45beSAndroid Build Coastguard Worker 		break;
194*053f45beSAndroid Build Coastguard Worker 	default:
195*053f45beSAndroid Build Coastguard Worker 		return ARGP_ERR_UNKNOWN;
196*053f45beSAndroid Build Coastguard Worker 	}
197*053f45beSAndroid Build Coastguard Worker 	return 0;
198*053f45beSAndroid Build Coastguard Worker }
199*053f45beSAndroid Build Coastguard Worker 
200*053f45beSAndroid Build Coastguard Worker static const struct argp argp = {
201*053f45beSAndroid Build Coastguard Worker 	.options = opts,
202*053f45beSAndroid Build Coastguard Worker 	.parser = parse_arg,
203*053f45beSAndroid Build Coastguard Worker 	.doc = argp_program_doc,
204*053f45beSAndroid Build Coastguard Worker };
205*053f45beSAndroid Build Coastguard Worker 
206*053f45beSAndroid Build Coastguard Worker 
207*053f45beSAndroid Build Coastguard Worker /* Adapted from perf/util/string.c */
glob_matches(const char * str,const char * pat)208*053f45beSAndroid Build Coastguard Worker static bool glob_matches(const char *str, const char *pat)
209*053f45beSAndroid Build Coastguard Worker {
210*053f45beSAndroid Build Coastguard Worker 	while (*str && *pat && *pat != '*') {
211*053f45beSAndroid Build Coastguard Worker 		if (*str != *pat)
212*053f45beSAndroid Build Coastguard Worker 			return false;
213*053f45beSAndroid Build Coastguard Worker 		str++;
214*053f45beSAndroid Build Coastguard Worker 		pat++;
215*053f45beSAndroid Build Coastguard Worker 	}
216*053f45beSAndroid Build Coastguard Worker 	/* Check wild card */
217*053f45beSAndroid Build Coastguard Worker 	if (*pat == '*') {
218*053f45beSAndroid Build Coastguard Worker 		while (*pat == '*')
219*053f45beSAndroid Build Coastguard Worker 			pat++;
220*053f45beSAndroid Build Coastguard Worker 		if (!*pat) /* Tail wild card matches all */
221*053f45beSAndroid Build Coastguard Worker 			return true;
222*053f45beSAndroid Build Coastguard Worker 		while (*str)
223*053f45beSAndroid Build Coastguard Worker 			if (glob_matches(str++, pat))
224*053f45beSAndroid Build Coastguard Worker 				return true;
225*053f45beSAndroid Build Coastguard Worker 	}
226*053f45beSAndroid Build Coastguard Worker 	return !*str && !*pat;
227*053f45beSAndroid Build Coastguard Worker }
228*053f45beSAndroid Build Coastguard Worker 
should_process_file(const char * filename)229*053f45beSAndroid Build Coastguard Worker static bool should_process_file(const char *filename)
230*053f45beSAndroid Build Coastguard Worker {
231*053f45beSAndroid Build Coastguard Worker 	int i;
232*053f45beSAndroid Build Coastguard Worker 
233*053f45beSAndroid Build Coastguard Worker 	if (env.deny_filter_cnt > 0) {
234*053f45beSAndroid Build Coastguard Worker 		for (i = 0; i < env.deny_filter_cnt; i++) {
235*053f45beSAndroid Build Coastguard Worker 			if (glob_matches(filename, env.deny_filters[i].file_glob))
236*053f45beSAndroid Build Coastguard Worker 				return false;
237*053f45beSAndroid Build Coastguard Worker 		}
238*053f45beSAndroid Build Coastguard Worker 	}
239*053f45beSAndroid Build Coastguard Worker 
240*053f45beSAndroid Build Coastguard Worker 	if (env.allow_filter_cnt == 0)
241*053f45beSAndroid Build Coastguard Worker 		return true;
242*053f45beSAndroid Build Coastguard Worker 
243*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.allow_filter_cnt; i++) {
244*053f45beSAndroid Build Coastguard Worker 		if (glob_matches(filename, env.allow_filters[i].file_glob))
245*053f45beSAndroid Build Coastguard Worker 			return true;
246*053f45beSAndroid Build Coastguard Worker 	}
247*053f45beSAndroid Build Coastguard Worker 
248*053f45beSAndroid Build Coastguard Worker 	return false;
249*053f45beSAndroid Build Coastguard Worker }
250*053f45beSAndroid Build Coastguard Worker 
is_bpf_obj_file(const char * path)251*053f45beSAndroid Build Coastguard Worker static bool is_bpf_obj_file(const char *path) {
252*053f45beSAndroid Build Coastguard Worker 	Elf64_Ehdr *ehdr;
253*053f45beSAndroid Build Coastguard Worker 	int fd, err = -EINVAL;
254*053f45beSAndroid Build Coastguard Worker 	Elf *elf = NULL;
255*053f45beSAndroid Build Coastguard Worker 
256*053f45beSAndroid Build Coastguard Worker 	fd = open(path, O_RDONLY | O_CLOEXEC);
257*053f45beSAndroid Build Coastguard Worker 	if (fd < 0)
258*053f45beSAndroid Build Coastguard Worker 		return true; /* we'll fail later and propagate error */
259*053f45beSAndroid Build Coastguard Worker 
260*053f45beSAndroid Build Coastguard Worker 	/* ensure libelf is initialized */
261*053f45beSAndroid Build Coastguard Worker 	(void)elf_version(EV_CURRENT);
262*053f45beSAndroid Build Coastguard Worker 
263*053f45beSAndroid Build Coastguard Worker 	elf = elf_begin(fd, ELF_C_READ, NULL);
264*053f45beSAndroid Build Coastguard Worker 	if (!elf)
265*053f45beSAndroid Build Coastguard Worker 		goto cleanup;
266*053f45beSAndroid Build Coastguard Worker 
267*053f45beSAndroid Build Coastguard Worker 	if (elf_kind(elf) != ELF_K_ELF || gelf_getclass(elf) != ELFCLASS64)
268*053f45beSAndroid Build Coastguard Worker 		goto cleanup;
269*053f45beSAndroid Build Coastguard Worker 
270*053f45beSAndroid Build Coastguard Worker 	ehdr = elf64_getehdr(elf);
271*053f45beSAndroid Build Coastguard Worker 	/* Old LLVM set e_machine to EM_NONE */
272*053f45beSAndroid Build Coastguard Worker 	if (!ehdr || ehdr->e_type != ET_REL || (ehdr->e_machine && ehdr->e_machine != EM_BPF))
273*053f45beSAndroid Build Coastguard Worker 		goto cleanup;
274*053f45beSAndroid Build Coastguard Worker 
275*053f45beSAndroid Build Coastguard Worker 	err = 0;
276*053f45beSAndroid Build Coastguard Worker cleanup:
277*053f45beSAndroid Build Coastguard Worker 	if (elf)
278*053f45beSAndroid Build Coastguard Worker 		elf_end(elf);
279*053f45beSAndroid Build Coastguard Worker 	close(fd);
280*053f45beSAndroid Build Coastguard Worker 	return err == 0;
281*053f45beSAndroid Build Coastguard Worker }
282*053f45beSAndroid Build Coastguard Worker 
should_process_prog(const char * path,const char * prog_name)283*053f45beSAndroid Build Coastguard Worker static bool should_process_prog(const char *path, const char *prog_name)
284*053f45beSAndroid Build Coastguard Worker {
285*053f45beSAndroid Build Coastguard Worker 	const char *filename = basename(path);
286*053f45beSAndroid Build Coastguard Worker 	int i;
287*053f45beSAndroid Build Coastguard Worker 
288*053f45beSAndroid Build Coastguard Worker 	if (env.deny_filter_cnt > 0) {
289*053f45beSAndroid Build Coastguard Worker 		for (i = 0; i < env.deny_filter_cnt; i++) {
290*053f45beSAndroid Build Coastguard Worker 			if (glob_matches(filename, env.deny_filters[i].file_glob))
291*053f45beSAndroid Build Coastguard Worker 				return false;
292*053f45beSAndroid Build Coastguard Worker 			if (!env.deny_filters[i].prog_glob)
293*053f45beSAndroid Build Coastguard Worker 				continue;
294*053f45beSAndroid Build Coastguard Worker 			if (glob_matches(prog_name, env.deny_filters[i].prog_glob))
295*053f45beSAndroid Build Coastguard Worker 				return false;
296*053f45beSAndroid Build Coastguard Worker 		}
297*053f45beSAndroid Build Coastguard Worker 	}
298*053f45beSAndroid Build Coastguard Worker 
299*053f45beSAndroid Build Coastguard Worker 	if (env.allow_filter_cnt == 0)
300*053f45beSAndroid Build Coastguard Worker 		return true;
301*053f45beSAndroid Build Coastguard Worker 
302*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.allow_filter_cnt; i++) {
303*053f45beSAndroid Build Coastguard Worker 		if (!glob_matches(filename, env.allow_filters[i].file_glob))
304*053f45beSAndroid Build Coastguard Worker 			continue;
305*053f45beSAndroid Build Coastguard Worker 		/* if filter specifies only filename glob part, it implicitly
306*053f45beSAndroid Build Coastguard Worker 		 * allows all progs within that file
307*053f45beSAndroid Build Coastguard Worker 		 */
308*053f45beSAndroid Build Coastguard Worker 		if (!env.allow_filters[i].prog_glob)
309*053f45beSAndroid Build Coastguard Worker 			return true;
310*053f45beSAndroid Build Coastguard Worker 		if (glob_matches(prog_name, env.allow_filters[i].prog_glob))
311*053f45beSAndroid Build Coastguard Worker 			return true;
312*053f45beSAndroid Build Coastguard Worker 	}
313*053f45beSAndroid Build Coastguard Worker 
314*053f45beSAndroid Build Coastguard Worker 	return false;
315*053f45beSAndroid Build Coastguard Worker }
316*053f45beSAndroid Build Coastguard Worker 
append_filter(struct filter ** filters,int * cnt,const char * str)317*053f45beSAndroid Build Coastguard Worker static int append_filter(struct filter **filters, int *cnt, const char *str)
318*053f45beSAndroid Build Coastguard Worker {
319*053f45beSAndroid Build Coastguard Worker 	struct filter *f;
320*053f45beSAndroid Build Coastguard Worker 	void *tmp;
321*053f45beSAndroid Build Coastguard Worker 	const char *p;
322*053f45beSAndroid Build Coastguard Worker 
323*053f45beSAndroid Build Coastguard Worker 	tmp = realloc(*filters, (*cnt + 1) * sizeof(**filters));
324*053f45beSAndroid Build Coastguard Worker 	if (!tmp)
325*053f45beSAndroid Build Coastguard Worker 		return -ENOMEM;
326*053f45beSAndroid Build Coastguard Worker 	*filters = tmp;
327*053f45beSAndroid Build Coastguard Worker 
328*053f45beSAndroid Build Coastguard Worker 	f = &(*filters)[*cnt];
329*053f45beSAndroid Build Coastguard Worker 	f->file_glob = f->prog_glob = NULL;
330*053f45beSAndroid Build Coastguard Worker 
331*053f45beSAndroid Build Coastguard Worker 	/* filter can be specified either as "<obj-glob>" or "<obj-glob>/<prog-glob>" */
332*053f45beSAndroid Build Coastguard Worker 	p = strchr(str, '/');
333*053f45beSAndroid Build Coastguard Worker 	if (!p) {
334*053f45beSAndroid Build Coastguard Worker 		f->file_glob = strdup(str);
335*053f45beSAndroid Build Coastguard Worker 		if (!f->file_glob)
336*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
337*053f45beSAndroid Build Coastguard Worker 	} else {
338*053f45beSAndroid Build Coastguard Worker 		f->file_glob = strndup(str, p - str);
339*053f45beSAndroid Build Coastguard Worker 		f->prog_glob = strdup(p + 1);
340*053f45beSAndroid Build Coastguard Worker 		if (!f->file_glob || !f->prog_glob) {
341*053f45beSAndroid Build Coastguard Worker 			free(f->file_glob);
342*053f45beSAndroid Build Coastguard Worker 			free(f->prog_glob);
343*053f45beSAndroid Build Coastguard Worker 			f->file_glob = f->prog_glob = NULL;
344*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
345*053f45beSAndroid Build Coastguard Worker 		}
346*053f45beSAndroid Build Coastguard Worker 	}
347*053f45beSAndroid Build Coastguard Worker 
348*053f45beSAndroid Build Coastguard Worker 	*cnt = *cnt + 1;
349*053f45beSAndroid Build Coastguard Worker 	return 0;
350*053f45beSAndroid Build Coastguard Worker }
351*053f45beSAndroid Build Coastguard Worker 
append_filter_file(const char * path)352*053f45beSAndroid Build Coastguard Worker static int append_filter_file(const char *path)
353*053f45beSAndroid Build Coastguard Worker {
354*053f45beSAndroid Build Coastguard Worker 	char buf[1024];
355*053f45beSAndroid Build Coastguard Worker 	FILE *f;
356*053f45beSAndroid Build Coastguard Worker 	int err = 0;
357*053f45beSAndroid Build Coastguard Worker 
358*053f45beSAndroid Build Coastguard Worker 	f = fopen(path, "r");
359*053f45beSAndroid Build Coastguard Worker 	if (!f) {
360*053f45beSAndroid Build Coastguard Worker 		err = -errno;
361*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Failed to open filters in '%s': %d\n", path, err);
362*053f45beSAndroid Build Coastguard Worker 		return err;
363*053f45beSAndroid Build Coastguard Worker 	}
364*053f45beSAndroid Build Coastguard Worker 
365*053f45beSAndroid Build Coastguard Worker 	while (fscanf(f, " %1023[^\n]\n", buf) == 1) {
366*053f45beSAndroid Build Coastguard Worker 		/* lines starting with # are comments, skip them */
367*053f45beSAndroid Build Coastguard Worker 		if (buf[0] == '\0' || buf[0] == '#')
368*053f45beSAndroid Build Coastguard Worker 			continue;
369*053f45beSAndroid Build Coastguard Worker 		/* lines starting with ! are negative match filters */
370*053f45beSAndroid Build Coastguard Worker 		if (buf[0] == '!')
371*053f45beSAndroid Build Coastguard Worker 			err = append_filter(&env.deny_filters, &env.deny_filter_cnt, buf + 1);
372*053f45beSAndroid Build Coastguard Worker 		else
373*053f45beSAndroid Build Coastguard Worker 			err = append_filter(&env.allow_filters, &env.allow_filter_cnt, buf);
374*053f45beSAndroid Build Coastguard Worker 		if (err)
375*053f45beSAndroid Build Coastguard Worker 			goto cleanup;
376*053f45beSAndroid Build Coastguard Worker 	}
377*053f45beSAndroid Build Coastguard Worker 
378*053f45beSAndroid Build Coastguard Worker cleanup:
379*053f45beSAndroid Build Coastguard Worker 	fclose(f);
380*053f45beSAndroid Build Coastguard Worker 	return err;
381*053f45beSAndroid Build Coastguard Worker }
382*053f45beSAndroid Build Coastguard Worker 
383*053f45beSAndroid Build Coastguard Worker static const struct stat_specs default_output_spec = {
384*053f45beSAndroid Build Coastguard Worker 	.spec_cnt = 7,
385*053f45beSAndroid Build Coastguard Worker 	.ids = {
386*053f45beSAndroid Build Coastguard Worker 		FILE_NAME, PROG_NAME, VERDICT, DURATION,
387*053f45beSAndroid Build Coastguard Worker 		TOTAL_INSNS, TOTAL_STATES, PEAK_STATES,
388*053f45beSAndroid Build Coastguard Worker 	},
389*053f45beSAndroid Build Coastguard Worker };
390*053f45beSAndroid Build Coastguard Worker 
391*053f45beSAndroid Build Coastguard Worker static const struct stat_specs default_sort_spec = {
392*053f45beSAndroid Build Coastguard Worker 	.spec_cnt = 2,
393*053f45beSAndroid Build Coastguard Worker 	.ids = {
394*053f45beSAndroid Build Coastguard Worker 		FILE_NAME, PROG_NAME,
395*053f45beSAndroid Build Coastguard Worker 	},
396*053f45beSAndroid Build Coastguard Worker 	.asc = { true, true, },
397*053f45beSAndroid Build Coastguard Worker };
398*053f45beSAndroid Build Coastguard Worker 
399*053f45beSAndroid Build Coastguard Worker static struct stat_def {
400*053f45beSAndroid Build Coastguard Worker 	const char *header;
401*053f45beSAndroid Build Coastguard Worker 	const char *names[4];
402*053f45beSAndroid Build Coastguard Worker 	bool asc_by_default;
403*053f45beSAndroid Build Coastguard Worker } stat_defs[] = {
404*053f45beSAndroid Build Coastguard Worker 	[FILE_NAME] = { "File", {"file_name", "filename", "file"}, true /* asc */ },
405*053f45beSAndroid Build Coastguard Worker 	[PROG_NAME] = { "Program", {"prog_name", "progname", "prog"}, true /* asc */ },
406*053f45beSAndroid Build Coastguard Worker 	[VERDICT] = { "Verdict", {"verdict"}, true /* asc: failure, success */ },
407*053f45beSAndroid Build Coastguard Worker 	[DURATION] = { "Duration (us)", {"duration", "dur"}, },
408*053f45beSAndroid Build Coastguard Worker 	[TOTAL_INSNS] = { "Total insns", {"total_insns", "insns"}, },
409*053f45beSAndroid Build Coastguard Worker 	[TOTAL_STATES] = { "Total states", {"total_states", "states"}, },
410*053f45beSAndroid Build Coastguard Worker 	[PEAK_STATES] = { "Peak states", {"peak_states"}, },
411*053f45beSAndroid Build Coastguard Worker 	[MAX_STATES_PER_INSN] = { "Max states per insn", {"max_states_per_insn"}, },
412*053f45beSAndroid Build Coastguard Worker 	[MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
413*053f45beSAndroid Build Coastguard Worker };
414*053f45beSAndroid Build Coastguard Worker 
parse_stat(const char * stat_name,struct stat_specs * specs)415*053f45beSAndroid Build Coastguard Worker static int parse_stat(const char *stat_name, struct stat_specs *specs)
416*053f45beSAndroid Build Coastguard Worker {
417*053f45beSAndroid Build Coastguard Worker 	int id, i;
418*053f45beSAndroid Build Coastguard Worker 
419*053f45beSAndroid Build Coastguard Worker 	if (specs->spec_cnt >= ARRAY_SIZE(specs->ids)) {
420*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Can't specify more than %zd stats\n", ARRAY_SIZE(specs->ids));
421*053f45beSAndroid Build Coastguard Worker 		return -E2BIG;
422*053f45beSAndroid Build Coastguard Worker 	}
423*053f45beSAndroid Build Coastguard Worker 
424*053f45beSAndroid Build Coastguard Worker 	for (id = 0; id < ARRAY_SIZE(stat_defs); id++) {
425*053f45beSAndroid Build Coastguard Worker 		struct stat_def *def = &stat_defs[id];
426*053f45beSAndroid Build Coastguard Worker 
427*053f45beSAndroid Build Coastguard Worker 		for (i = 0; i < ARRAY_SIZE(stat_defs[id].names); i++) {
428*053f45beSAndroid Build Coastguard Worker 			if (!def->names[i] || strcmp(def->names[i], stat_name) != 0)
429*053f45beSAndroid Build Coastguard Worker 				continue;
430*053f45beSAndroid Build Coastguard Worker 
431*053f45beSAndroid Build Coastguard Worker 			specs->ids[specs->spec_cnt] = id;
432*053f45beSAndroid Build Coastguard Worker 			specs->asc[specs->spec_cnt] = def->asc_by_default;
433*053f45beSAndroid Build Coastguard Worker 			specs->spec_cnt++;
434*053f45beSAndroid Build Coastguard Worker 
435*053f45beSAndroid Build Coastguard Worker 			return 0;
436*053f45beSAndroid Build Coastguard Worker 		}
437*053f45beSAndroid Build Coastguard Worker 	}
438*053f45beSAndroid Build Coastguard Worker 
439*053f45beSAndroid Build Coastguard Worker 	fprintf(stderr, "Unrecognized stat name '%s'\n", stat_name);
440*053f45beSAndroid Build Coastguard Worker 	return -ESRCH;
441*053f45beSAndroid Build Coastguard Worker }
442*053f45beSAndroid Build Coastguard Worker 
parse_stats(const char * stats_str,struct stat_specs * specs)443*053f45beSAndroid Build Coastguard Worker static int parse_stats(const char *stats_str, struct stat_specs *specs)
444*053f45beSAndroid Build Coastguard Worker {
445*053f45beSAndroid Build Coastguard Worker 	char *input, *state = NULL, *next;
446*053f45beSAndroid Build Coastguard Worker 	int err;
447*053f45beSAndroid Build Coastguard Worker 
448*053f45beSAndroid Build Coastguard Worker 	input = strdup(stats_str);
449*053f45beSAndroid Build Coastguard Worker 	if (!input)
450*053f45beSAndroid Build Coastguard Worker 		return -ENOMEM;
451*053f45beSAndroid Build Coastguard Worker 
452*053f45beSAndroid Build Coastguard Worker 	while ((next = strtok_r(state ? NULL : input, ",", &state))) {
453*053f45beSAndroid Build Coastguard Worker 		err = parse_stat(next, specs);
454*053f45beSAndroid Build Coastguard Worker 		if (err)
455*053f45beSAndroid Build Coastguard Worker 			return err;
456*053f45beSAndroid Build Coastguard Worker 	}
457*053f45beSAndroid Build Coastguard Worker 
458*053f45beSAndroid Build Coastguard Worker 	return 0;
459*053f45beSAndroid Build Coastguard Worker }
460*053f45beSAndroid Build Coastguard Worker 
free_verif_stats(struct verif_stats * stats,size_t stat_cnt)461*053f45beSAndroid Build Coastguard Worker static void free_verif_stats(struct verif_stats *stats, size_t stat_cnt)
462*053f45beSAndroid Build Coastguard Worker {
463*053f45beSAndroid Build Coastguard Worker 	int i;
464*053f45beSAndroid Build Coastguard Worker 
465*053f45beSAndroid Build Coastguard Worker 	if (!stats)
466*053f45beSAndroid Build Coastguard Worker 		return;
467*053f45beSAndroid Build Coastguard Worker 
468*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < stat_cnt; i++) {
469*053f45beSAndroid Build Coastguard Worker 		free(stats[i].file_name);
470*053f45beSAndroid Build Coastguard Worker 		free(stats[i].prog_name);
471*053f45beSAndroid Build Coastguard Worker 	}
472*053f45beSAndroid Build Coastguard Worker 	free(stats);
473*053f45beSAndroid Build Coastguard Worker }
474*053f45beSAndroid Build Coastguard Worker 
475*053f45beSAndroid Build Coastguard Worker static char verif_log_buf[64 * 1024];
476*053f45beSAndroid Build Coastguard Worker 
477*053f45beSAndroid Build Coastguard Worker #define MAX_PARSED_LOG_LINES 100
478*053f45beSAndroid Build Coastguard Worker 
parse_verif_log(char * const buf,size_t buf_sz,struct verif_stats * s)479*053f45beSAndroid Build Coastguard Worker static int parse_verif_log(char * const buf, size_t buf_sz, struct verif_stats *s)
480*053f45beSAndroid Build Coastguard Worker {
481*053f45beSAndroid Build Coastguard Worker 	const char *cur;
482*053f45beSAndroid Build Coastguard Worker 	int pos, lines;
483*053f45beSAndroid Build Coastguard Worker 
484*053f45beSAndroid Build Coastguard Worker 	buf[buf_sz - 1] = '\0';
485*053f45beSAndroid Build Coastguard Worker 
486*053f45beSAndroid Build Coastguard Worker 	for (pos = strlen(buf) - 1, lines = 0; pos >= 0 && lines < MAX_PARSED_LOG_LINES; lines++) {
487*053f45beSAndroid Build Coastguard Worker 		/* find previous endline or otherwise take the start of log buf */
488*053f45beSAndroid Build Coastguard Worker 		for (cur = &buf[pos]; cur > buf && cur[0] != '\n'; cur--, pos--) {
489*053f45beSAndroid Build Coastguard Worker 		}
490*053f45beSAndroid Build Coastguard Worker 		/* next time start from end of previous line (or pos goes to <0) */
491*053f45beSAndroid Build Coastguard Worker 		pos--;
492*053f45beSAndroid Build Coastguard Worker 		/* if we found endline, point right after endline symbol;
493*053f45beSAndroid Build Coastguard Worker 		 * otherwise, stay at the beginning of log buf
494*053f45beSAndroid Build Coastguard Worker 		 */
495*053f45beSAndroid Build Coastguard Worker 		if (cur[0] == '\n')
496*053f45beSAndroid Build Coastguard Worker 			cur++;
497*053f45beSAndroid Build Coastguard Worker 
498*053f45beSAndroid Build Coastguard Worker 		if (1 == sscanf(cur, "verification time %ld usec\n", &s->stats[DURATION]))
499*053f45beSAndroid Build Coastguard Worker 			continue;
500*053f45beSAndroid Build Coastguard Worker 		if (6 == sscanf(cur, "processed %ld insns (limit %*d) max_states_per_insn %ld total_states %ld peak_states %ld mark_read %ld",
501*053f45beSAndroid Build Coastguard Worker 				&s->stats[TOTAL_INSNS],
502*053f45beSAndroid Build Coastguard Worker 				&s->stats[MAX_STATES_PER_INSN],
503*053f45beSAndroid Build Coastguard Worker 				&s->stats[TOTAL_STATES],
504*053f45beSAndroid Build Coastguard Worker 				&s->stats[PEAK_STATES],
505*053f45beSAndroid Build Coastguard Worker 				&s->stats[MARK_READ_MAX_LEN]))
506*053f45beSAndroid Build Coastguard Worker 			continue;
507*053f45beSAndroid Build Coastguard Worker 	}
508*053f45beSAndroid Build Coastguard Worker 
509*053f45beSAndroid Build Coastguard Worker 	return 0;
510*053f45beSAndroid Build Coastguard Worker }
511*053f45beSAndroid Build Coastguard Worker 
process_prog(const char * filename,struct bpf_object * obj,struct bpf_program * prog)512*053f45beSAndroid Build Coastguard Worker static int process_prog(const char *filename, struct bpf_object *obj, struct bpf_program *prog)
513*053f45beSAndroid Build Coastguard Worker {
514*053f45beSAndroid Build Coastguard Worker 	const char *prog_name = bpf_program__name(prog);
515*053f45beSAndroid Build Coastguard Worker 	size_t buf_sz = sizeof(verif_log_buf);
516*053f45beSAndroid Build Coastguard Worker 	char *buf = verif_log_buf;
517*053f45beSAndroid Build Coastguard Worker 	struct verif_stats *stats;
518*053f45beSAndroid Build Coastguard Worker 	int err = 0;
519*053f45beSAndroid Build Coastguard Worker 	void *tmp;
520*053f45beSAndroid Build Coastguard Worker 
521*053f45beSAndroid Build Coastguard Worker 	if (!should_process_prog(filename, bpf_program__name(prog))) {
522*053f45beSAndroid Build Coastguard Worker 		env.progs_skipped++;
523*053f45beSAndroid Build Coastguard Worker 		return 0;
524*053f45beSAndroid Build Coastguard Worker 	}
525*053f45beSAndroid Build Coastguard Worker 
526*053f45beSAndroid Build Coastguard Worker 	tmp = realloc(env.prog_stats, (env.prog_stat_cnt + 1) * sizeof(*env.prog_stats));
527*053f45beSAndroid Build Coastguard Worker 	if (!tmp)
528*053f45beSAndroid Build Coastguard Worker 		return -ENOMEM;
529*053f45beSAndroid Build Coastguard Worker 	env.prog_stats = tmp;
530*053f45beSAndroid Build Coastguard Worker 	stats = &env.prog_stats[env.prog_stat_cnt++];
531*053f45beSAndroid Build Coastguard Worker 	memset(stats, 0, sizeof(*stats));
532*053f45beSAndroid Build Coastguard Worker 
533*053f45beSAndroid Build Coastguard Worker 	if (env.verbose) {
534*053f45beSAndroid Build Coastguard Worker 		buf_sz = 16 * 1024 * 1024;
535*053f45beSAndroid Build Coastguard Worker 		buf = malloc(buf_sz);
536*053f45beSAndroid Build Coastguard Worker 		if (!buf)
537*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
538*053f45beSAndroid Build Coastguard Worker 		bpf_program__set_log_buf(prog, buf, buf_sz);
539*053f45beSAndroid Build Coastguard Worker 		bpf_program__set_log_level(prog, env.log_level | 4); /* stats + log */
540*053f45beSAndroid Build Coastguard Worker 	} else {
541*053f45beSAndroid Build Coastguard Worker 		bpf_program__set_log_buf(prog, buf, buf_sz);
542*053f45beSAndroid Build Coastguard Worker 		bpf_program__set_log_level(prog, 4); /* only verifier stats */
543*053f45beSAndroid Build Coastguard Worker 	}
544*053f45beSAndroid Build Coastguard Worker 	verif_log_buf[0] = '\0';
545*053f45beSAndroid Build Coastguard Worker 
546*053f45beSAndroid Build Coastguard Worker 	err = bpf_object__load(obj);
547*053f45beSAndroid Build Coastguard Worker 	env.progs_processed++;
548*053f45beSAndroid Build Coastguard Worker 
549*053f45beSAndroid Build Coastguard Worker 	stats->file_name = strdup(basename(filename));
550*053f45beSAndroid Build Coastguard Worker 	stats->prog_name = strdup(bpf_program__name(prog));
551*053f45beSAndroid Build Coastguard Worker 	stats->stats[VERDICT] = err == 0; /* 1 - success, 0 - failure */
552*053f45beSAndroid Build Coastguard Worker 	parse_verif_log(buf, buf_sz, stats);
553*053f45beSAndroid Build Coastguard Worker 
554*053f45beSAndroid Build Coastguard Worker 	if (env.verbose) {
555*053f45beSAndroid Build Coastguard Worker 		printf("PROCESSING %s/%s, DURATION US: %ld, VERDICT: %s, VERIFIER LOG:\n%s\n",
556*053f45beSAndroid Build Coastguard Worker 		       filename, prog_name, stats->stats[DURATION],
557*053f45beSAndroid Build Coastguard Worker 		       err ? "failure" : "success", buf);
558*053f45beSAndroid Build Coastguard Worker 	}
559*053f45beSAndroid Build Coastguard Worker 
560*053f45beSAndroid Build Coastguard Worker 	if (verif_log_buf != buf)
561*053f45beSAndroid Build Coastguard Worker 		free(buf);
562*053f45beSAndroid Build Coastguard Worker 
563*053f45beSAndroid Build Coastguard Worker 	return 0;
564*053f45beSAndroid Build Coastguard Worker };
565*053f45beSAndroid Build Coastguard Worker 
process_obj(const char * filename)566*053f45beSAndroid Build Coastguard Worker static int process_obj(const char *filename)
567*053f45beSAndroid Build Coastguard Worker {
568*053f45beSAndroid Build Coastguard Worker 	struct bpf_object *obj = NULL, *tobj;
569*053f45beSAndroid Build Coastguard Worker 	struct bpf_program *prog, *tprog, *lprog;
570*053f45beSAndroid Build Coastguard Worker 	libbpf_print_fn_t old_libbpf_print_fn;
571*053f45beSAndroid Build Coastguard Worker 	LIBBPF_OPTS(bpf_object_open_opts, opts);
572*053f45beSAndroid Build Coastguard Worker 	int err = 0, prog_cnt = 0;
573*053f45beSAndroid Build Coastguard Worker 
574*053f45beSAndroid Build Coastguard Worker 	if (!should_process_file(basename(filename))) {
575*053f45beSAndroid Build Coastguard Worker 		if (env.verbose)
576*053f45beSAndroid Build Coastguard Worker 			printf("Skipping '%s' due to filters...\n", filename);
577*053f45beSAndroid Build Coastguard Worker 		env.files_skipped++;
578*053f45beSAndroid Build Coastguard Worker 		return 0;
579*053f45beSAndroid Build Coastguard Worker 	}
580*053f45beSAndroid Build Coastguard Worker 	if (!is_bpf_obj_file(filename)) {
581*053f45beSAndroid Build Coastguard Worker 		if (env.verbose)
582*053f45beSAndroid Build Coastguard Worker 			printf("Skipping '%s' as it's not a BPF object file...\n", filename);
583*053f45beSAndroid Build Coastguard Worker 		env.files_skipped++;
584*053f45beSAndroid Build Coastguard Worker 		return 0;
585*053f45beSAndroid Build Coastguard Worker 	}
586*053f45beSAndroid Build Coastguard Worker 
587*053f45beSAndroid Build Coastguard Worker 	if (!env.quiet && env.out_fmt == RESFMT_TABLE)
588*053f45beSAndroid Build Coastguard Worker 		printf("Processing '%s'...\n", basename(filename));
589*053f45beSAndroid Build Coastguard Worker 
590*053f45beSAndroid Build Coastguard Worker 	old_libbpf_print_fn = libbpf_set_print(libbpf_print_fn);
591*053f45beSAndroid Build Coastguard Worker 	obj = bpf_object__open_file(filename, &opts);
592*053f45beSAndroid Build Coastguard Worker 	if (!obj) {
593*053f45beSAndroid Build Coastguard Worker 		/* if libbpf can't open BPF object file, it could be because
594*053f45beSAndroid Build Coastguard Worker 		 * that BPF object file is incomplete and has to be statically
595*053f45beSAndroid Build Coastguard Worker 		 * linked into a final BPF object file; instead of bailing
596*053f45beSAndroid Build Coastguard Worker 		 * out, report it into stderr, mark it as skipped, and
597*053f45beSAndroid Build Coastguard Worker 		 * proceeed
598*053f45beSAndroid Build Coastguard Worker 		 */
599*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Failed to open '%s': %d\n", filename, -errno);
600*053f45beSAndroid Build Coastguard Worker 		env.files_skipped++;
601*053f45beSAndroid Build Coastguard Worker 		err = 0;
602*053f45beSAndroid Build Coastguard Worker 		goto cleanup;
603*053f45beSAndroid Build Coastguard Worker 	}
604*053f45beSAndroid Build Coastguard Worker 
605*053f45beSAndroid Build Coastguard Worker 	env.files_processed++;
606*053f45beSAndroid Build Coastguard Worker 
607*053f45beSAndroid Build Coastguard Worker 	bpf_object__for_each_program(prog, obj) {
608*053f45beSAndroid Build Coastguard Worker 		prog_cnt++;
609*053f45beSAndroid Build Coastguard Worker 	}
610*053f45beSAndroid Build Coastguard Worker 
611*053f45beSAndroid Build Coastguard Worker 	if (prog_cnt == 1) {
612*053f45beSAndroid Build Coastguard Worker 		prog = bpf_object__next_program(obj, NULL);
613*053f45beSAndroid Build Coastguard Worker 		bpf_program__set_autoload(prog, true);
614*053f45beSAndroid Build Coastguard Worker 		process_prog(filename, obj, prog);
615*053f45beSAndroid Build Coastguard Worker 		goto cleanup;
616*053f45beSAndroid Build Coastguard Worker 	}
617*053f45beSAndroid Build Coastguard Worker 
618*053f45beSAndroid Build Coastguard Worker 	bpf_object__for_each_program(prog, obj) {
619*053f45beSAndroid Build Coastguard Worker 		const char *prog_name = bpf_program__name(prog);
620*053f45beSAndroid Build Coastguard Worker 
621*053f45beSAndroid Build Coastguard Worker 		tobj = bpf_object__open_file(filename, &opts);
622*053f45beSAndroid Build Coastguard Worker 		if (!tobj) {
623*053f45beSAndroid Build Coastguard Worker 			err = -errno;
624*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Failed to open '%s': %d\n", filename, err);
625*053f45beSAndroid Build Coastguard Worker 			goto cleanup;
626*053f45beSAndroid Build Coastguard Worker 		}
627*053f45beSAndroid Build Coastguard Worker 
628*053f45beSAndroid Build Coastguard Worker 		bpf_object__for_each_program(tprog, tobj) {
629*053f45beSAndroid Build Coastguard Worker 			const char *tprog_name = bpf_program__name(tprog);
630*053f45beSAndroid Build Coastguard Worker 
631*053f45beSAndroid Build Coastguard Worker 			if (strcmp(prog_name, tprog_name) == 0) {
632*053f45beSAndroid Build Coastguard Worker 				bpf_program__set_autoload(tprog, true);
633*053f45beSAndroid Build Coastguard Worker 				lprog = tprog;
634*053f45beSAndroid Build Coastguard Worker 			} else {
635*053f45beSAndroid Build Coastguard Worker 				bpf_program__set_autoload(tprog, false);
636*053f45beSAndroid Build Coastguard Worker 			}
637*053f45beSAndroid Build Coastguard Worker 		}
638*053f45beSAndroid Build Coastguard Worker 
639*053f45beSAndroid Build Coastguard Worker 		process_prog(filename, tobj, lprog);
640*053f45beSAndroid Build Coastguard Worker 		bpf_object__close(tobj);
641*053f45beSAndroid Build Coastguard Worker 	}
642*053f45beSAndroid Build Coastguard Worker 
643*053f45beSAndroid Build Coastguard Worker cleanup:
644*053f45beSAndroid Build Coastguard Worker 	bpf_object__close(obj);
645*053f45beSAndroid Build Coastguard Worker 	libbpf_set_print(old_libbpf_print_fn);
646*053f45beSAndroid Build Coastguard Worker 	return err;
647*053f45beSAndroid Build Coastguard Worker }
648*053f45beSAndroid Build Coastguard Worker 
cmp_stat(const struct verif_stats * s1,const struct verif_stats * s2,enum stat_id id,bool asc)649*053f45beSAndroid Build Coastguard Worker static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
650*053f45beSAndroid Build Coastguard Worker 		    enum stat_id id, bool asc)
651*053f45beSAndroid Build Coastguard Worker {
652*053f45beSAndroid Build Coastguard Worker 	int cmp = 0;
653*053f45beSAndroid Build Coastguard Worker 
654*053f45beSAndroid Build Coastguard Worker 	switch (id) {
655*053f45beSAndroid Build Coastguard Worker 	case FILE_NAME:
656*053f45beSAndroid Build Coastguard Worker 		cmp = strcmp(s1->file_name, s2->file_name);
657*053f45beSAndroid Build Coastguard Worker 		break;
658*053f45beSAndroid Build Coastguard Worker 	case PROG_NAME:
659*053f45beSAndroid Build Coastguard Worker 		cmp = strcmp(s1->prog_name, s2->prog_name);
660*053f45beSAndroid Build Coastguard Worker 		break;
661*053f45beSAndroid Build Coastguard Worker 	case VERDICT:
662*053f45beSAndroid Build Coastguard Worker 	case DURATION:
663*053f45beSAndroid Build Coastguard Worker 	case TOTAL_INSNS:
664*053f45beSAndroid Build Coastguard Worker 	case TOTAL_STATES:
665*053f45beSAndroid Build Coastguard Worker 	case PEAK_STATES:
666*053f45beSAndroid Build Coastguard Worker 	case MAX_STATES_PER_INSN:
667*053f45beSAndroid Build Coastguard Worker 	case MARK_READ_MAX_LEN: {
668*053f45beSAndroid Build Coastguard Worker 		long v1 = s1->stats[id];
669*053f45beSAndroid Build Coastguard Worker 		long v2 = s2->stats[id];
670*053f45beSAndroid Build Coastguard Worker 
671*053f45beSAndroid Build Coastguard Worker 		if (v1 != v2)
672*053f45beSAndroid Build Coastguard Worker 			cmp = v1 < v2 ? -1 : 1;
673*053f45beSAndroid Build Coastguard Worker 		break;
674*053f45beSAndroid Build Coastguard Worker 	}
675*053f45beSAndroid Build Coastguard Worker 	default:
676*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Unrecognized stat #%d\n", id);
677*053f45beSAndroid Build Coastguard Worker 		exit(1);
678*053f45beSAndroid Build Coastguard Worker 	}
679*053f45beSAndroid Build Coastguard Worker 
680*053f45beSAndroid Build Coastguard Worker 	return asc ? cmp : -cmp;
681*053f45beSAndroid Build Coastguard Worker }
682*053f45beSAndroid Build Coastguard Worker 
cmp_prog_stats(const void * v1,const void * v2)683*053f45beSAndroid Build Coastguard Worker static int cmp_prog_stats(const void *v1, const void *v2)
684*053f45beSAndroid Build Coastguard Worker {
685*053f45beSAndroid Build Coastguard Worker 	const struct verif_stats *s1 = v1, *s2 = v2;
686*053f45beSAndroid Build Coastguard Worker 	int i, cmp;
687*053f45beSAndroid Build Coastguard Worker 
688*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.sort_spec.spec_cnt; i++) {
689*053f45beSAndroid Build Coastguard Worker 		cmp = cmp_stat(s1, s2, env.sort_spec.ids[i], env.sort_spec.asc[i]);
690*053f45beSAndroid Build Coastguard Worker 		if (cmp != 0)
691*053f45beSAndroid Build Coastguard Worker 			return cmp;
692*053f45beSAndroid Build Coastguard Worker 	}
693*053f45beSAndroid Build Coastguard Worker 
694*053f45beSAndroid Build Coastguard Worker 	return 0;
695*053f45beSAndroid Build Coastguard Worker }
696*053f45beSAndroid Build Coastguard Worker 
697*053f45beSAndroid Build Coastguard Worker #define HEADER_CHAR '-'
698*053f45beSAndroid Build Coastguard Worker #define COLUMN_SEP "  "
699*053f45beSAndroid Build Coastguard Worker 
output_header_underlines(void)700*053f45beSAndroid Build Coastguard Worker static void output_header_underlines(void)
701*053f45beSAndroid Build Coastguard Worker {
702*053f45beSAndroid Build Coastguard Worker 	int i, j, len;
703*053f45beSAndroid Build Coastguard Worker 
704*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.output_spec.spec_cnt; i++) {
705*053f45beSAndroid Build Coastguard Worker 		len = env.output_spec.lens[i];
706*053f45beSAndroid Build Coastguard Worker 
707*053f45beSAndroid Build Coastguard Worker 		printf("%s", i == 0 ? "" : COLUMN_SEP);
708*053f45beSAndroid Build Coastguard Worker 		for (j = 0; j < len; j++)
709*053f45beSAndroid Build Coastguard Worker 			printf("%c", HEADER_CHAR);
710*053f45beSAndroid Build Coastguard Worker 	}
711*053f45beSAndroid Build Coastguard Worker 	printf("\n");
712*053f45beSAndroid Build Coastguard Worker }
713*053f45beSAndroid Build Coastguard Worker 
output_headers(enum resfmt fmt)714*053f45beSAndroid Build Coastguard Worker static void output_headers(enum resfmt fmt)
715*053f45beSAndroid Build Coastguard Worker {
716*053f45beSAndroid Build Coastguard Worker 	int i, len;
717*053f45beSAndroid Build Coastguard Worker 
718*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.output_spec.spec_cnt; i++) {
719*053f45beSAndroid Build Coastguard Worker 		int id = env.output_spec.ids[i];
720*053f45beSAndroid Build Coastguard Worker 		int *max_len = &env.output_spec.lens[i];
721*053f45beSAndroid Build Coastguard Worker 
722*053f45beSAndroid Build Coastguard Worker 		switch (fmt) {
723*053f45beSAndroid Build Coastguard Worker 		case RESFMT_TABLE_CALCLEN:
724*053f45beSAndroid Build Coastguard Worker 			len = snprintf(NULL, 0, "%s", stat_defs[id].header);
725*053f45beSAndroid Build Coastguard Worker 			if (len > *max_len)
726*053f45beSAndroid Build Coastguard Worker 				*max_len = len;
727*053f45beSAndroid Build Coastguard Worker 			break;
728*053f45beSAndroid Build Coastguard Worker 		case RESFMT_TABLE:
729*053f45beSAndroid Build Coastguard Worker 			printf("%s%-*s", i == 0 ? "" : COLUMN_SEP,  *max_len, stat_defs[id].header);
730*053f45beSAndroid Build Coastguard Worker 			if (i == env.output_spec.spec_cnt - 1)
731*053f45beSAndroid Build Coastguard Worker 				printf("\n");
732*053f45beSAndroid Build Coastguard Worker 			break;
733*053f45beSAndroid Build Coastguard Worker 		case RESFMT_CSV:
734*053f45beSAndroid Build Coastguard Worker 			printf("%s%s", i == 0 ? "" : ",", stat_defs[id].names[0]);
735*053f45beSAndroid Build Coastguard Worker 			if (i == env.output_spec.spec_cnt - 1)
736*053f45beSAndroid Build Coastguard Worker 				printf("\n");
737*053f45beSAndroid Build Coastguard Worker 			break;
738*053f45beSAndroid Build Coastguard Worker 		}
739*053f45beSAndroid Build Coastguard Worker 	}
740*053f45beSAndroid Build Coastguard Worker 
741*053f45beSAndroid Build Coastguard Worker 	if (fmt == RESFMT_TABLE)
742*053f45beSAndroid Build Coastguard Worker 		output_header_underlines();
743*053f45beSAndroid Build Coastguard Worker }
744*053f45beSAndroid Build Coastguard Worker 
prepare_value(const struct verif_stats * s,enum stat_id id,const char ** str,long * val)745*053f45beSAndroid Build Coastguard Worker static void prepare_value(const struct verif_stats *s, enum stat_id id,
746*053f45beSAndroid Build Coastguard Worker 			  const char **str, long *val)
747*053f45beSAndroid Build Coastguard Worker {
748*053f45beSAndroid Build Coastguard Worker 	switch (id) {
749*053f45beSAndroid Build Coastguard Worker 	case FILE_NAME:
750*053f45beSAndroid Build Coastguard Worker 		*str = s->file_name;
751*053f45beSAndroid Build Coastguard Worker 		break;
752*053f45beSAndroid Build Coastguard Worker 	case PROG_NAME:
753*053f45beSAndroid Build Coastguard Worker 		*str = s->prog_name;
754*053f45beSAndroid Build Coastguard Worker 		break;
755*053f45beSAndroid Build Coastguard Worker 	case VERDICT:
756*053f45beSAndroid Build Coastguard Worker 		*str = s->stats[VERDICT] ? "success" : "failure";
757*053f45beSAndroid Build Coastguard Worker 		break;
758*053f45beSAndroid Build Coastguard Worker 	case DURATION:
759*053f45beSAndroid Build Coastguard Worker 	case TOTAL_INSNS:
760*053f45beSAndroid Build Coastguard Worker 	case TOTAL_STATES:
761*053f45beSAndroid Build Coastguard Worker 	case PEAK_STATES:
762*053f45beSAndroid Build Coastguard Worker 	case MAX_STATES_PER_INSN:
763*053f45beSAndroid Build Coastguard Worker 	case MARK_READ_MAX_LEN:
764*053f45beSAndroid Build Coastguard Worker 		*val = s->stats[id];
765*053f45beSAndroid Build Coastguard Worker 		break;
766*053f45beSAndroid Build Coastguard Worker 	default:
767*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Unrecognized stat #%d\n", id);
768*053f45beSAndroid Build Coastguard Worker 		exit(1);
769*053f45beSAndroid Build Coastguard Worker 	}
770*053f45beSAndroid Build Coastguard Worker }
771*053f45beSAndroid Build Coastguard Worker 
output_stats(const struct verif_stats * s,enum resfmt fmt,bool last)772*053f45beSAndroid Build Coastguard Worker static void output_stats(const struct verif_stats *s, enum resfmt fmt, bool last)
773*053f45beSAndroid Build Coastguard Worker {
774*053f45beSAndroid Build Coastguard Worker 	int i;
775*053f45beSAndroid Build Coastguard Worker 
776*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.output_spec.spec_cnt; i++) {
777*053f45beSAndroid Build Coastguard Worker 		int id = env.output_spec.ids[i];
778*053f45beSAndroid Build Coastguard Worker 		int *max_len = &env.output_spec.lens[i], len;
779*053f45beSAndroid Build Coastguard Worker 		const char *str = NULL;
780*053f45beSAndroid Build Coastguard Worker 		long val = 0;
781*053f45beSAndroid Build Coastguard Worker 
782*053f45beSAndroid Build Coastguard Worker 		prepare_value(s, id, &str, &val);
783*053f45beSAndroid Build Coastguard Worker 
784*053f45beSAndroid Build Coastguard Worker 		switch (fmt) {
785*053f45beSAndroid Build Coastguard Worker 		case RESFMT_TABLE_CALCLEN:
786*053f45beSAndroid Build Coastguard Worker 			if (str)
787*053f45beSAndroid Build Coastguard Worker 				len = snprintf(NULL, 0, "%s", str);
788*053f45beSAndroid Build Coastguard Worker 			else
789*053f45beSAndroid Build Coastguard Worker 				len = snprintf(NULL, 0, "%ld", val);
790*053f45beSAndroid Build Coastguard Worker 			if (len > *max_len)
791*053f45beSAndroid Build Coastguard Worker 				*max_len = len;
792*053f45beSAndroid Build Coastguard Worker 			break;
793*053f45beSAndroid Build Coastguard Worker 		case RESFMT_TABLE:
794*053f45beSAndroid Build Coastguard Worker 			if (str)
795*053f45beSAndroid Build Coastguard Worker 				printf("%s%-*s", i == 0 ? "" : COLUMN_SEP, *max_len, str);
796*053f45beSAndroid Build Coastguard Worker 			else
797*053f45beSAndroid Build Coastguard Worker 				printf("%s%*ld", i == 0 ? "" : COLUMN_SEP,  *max_len, val);
798*053f45beSAndroid Build Coastguard Worker 			if (i == env.output_spec.spec_cnt - 1)
799*053f45beSAndroid Build Coastguard Worker 				printf("\n");
800*053f45beSAndroid Build Coastguard Worker 			break;
801*053f45beSAndroid Build Coastguard Worker 		case RESFMT_CSV:
802*053f45beSAndroid Build Coastguard Worker 			if (str)
803*053f45beSAndroid Build Coastguard Worker 				printf("%s%s", i == 0 ? "" : ",", str);
804*053f45beSAndroid Build Coastguard Worker 			else
805*053f45beSAndroid Build Coastguard Worker 				printf("%s%ld", i == 0 ? "" : ",", val);
806*053f45beSAndroid Build Coastguard Worker 			if (i == env.output_spec.spec_cnt - 1)
807*053f45beSAndroid Build Coastguard Worker 				printf("\n");
808*053f45beSAndroid Build Coastguard Worker 			break;
809*053f45beSAndroid Build Coastguard Worker 		}
810*053f45beSAndroid Build Coastguard Worker 	}
811*053f45beSAndroid Build Coastguard Worker 
812*053f45beSAndroid Build Coastguard Worker 	if (last && fmt == RESFMT_TABLE) {
813*053f45beSAndroid Build Coastguard Worker 		output_header_underlines();
814*053f45beSAndroid Build Coastguard Worker 		printf("Done. Processed %d files, %d programs. Skipped %d files, %d programs.\n",
815*053f45beSAndroid Build Coastguard Worker 		       env.files_processed, env.files_skipped, env.progs_processed, env.progs_skipped);
816*053f45beSAndroid Build Coastguard Worker 	}
817*053f45beSAndroid Build Coastguard Worker }
818*053f45beSAndroid Build Coastguard Worker 
handle_verif_mode(void)819*053f45beSAndroid Build Coastguard Worker static int handle_verif_mode(void)
820*053f45beSAndroid Build Coastguard Worker {
821*053f45beSAndroid Build Coastguard Worker 	int i, err;
822*053f45beSAndroid Build Coastguard Worker 
823*053f45beSAndroid Build Coastguard Worker 	if (env.filename_cnt == 0) {
824*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Please provide path to BPF object file!\n");
825*053f45beSAndroid Build Coastguard Worker 		argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat");
826*053f45beSAndroid Build Coastguard Worker 		return -EINVAL;
827*053f45beSAndroid Build Coastguard Worker 	}
828*053f45beSAndroid Build Coastguard Worker 
829*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.filename_cnt; i++) {
830*053f45beSAndroid Build Coastguard Worker 		err = process_obj(env.filenames[i]);
831*053f45beSAndroid Build Coastguard Worker 		if (err) {
832*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Failed to process '%s': %d\n", env.filenames[i], err);
833*053f45beSAndroid Build Coastguard Worker 			return err;
834*053f45beSAndroid Build Coastguard Worker 		}
835*053f45beSAndroid Build Coastguard Worker 	}
836*053f45beSAndroid Build Coastguard Worker 
837*053f45beSAndroid Build Coastguard Worker 	qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats);
838*053f45beSAndroid Build Coastguard Worker 
839*053f45beSAndroid Build Coastguard Worker 	if (env.out_fmt == RESFMT_TABLE) {
840*053f45beSAndroid Build Coastguard Worker 		/* calculate column widths */
841*053f45beSAndroid Build Coastguard Worker 		output_headers(RESFMT_TABLE_CALCLEN);
842*053f45beSAndroid Build Coastguard Worker 		for (i = 0; i < env.prog_stat_cnt; i++)
843*053f45beSAndroid Build Coastguard Worker 			output_stats(&env.prog_stats[i], RESFMT_TABLE_CALCLEN, false);
844*053f45beSAndroid Build Coastguard Worker 	}
845*053f45beSAndroid Build Coastguard Worker 
846*053f45beSAndroid Build Coastguard Worker 	/* actually output the table */
847*053f45beSAndroid Build Coastguard Worker 	output_headers(env.out_fmt);
848*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.prog_stat_cnt; i++) {
849*053f45beSAndroid Build Coastguard Worker 		output_stats(&env.prog_stats[i], env.out_fmt, i == env.prog_stat_cnt - 1);
850*053f45beSAndroid Build Coastguard Worker 	}
851*053f45beSAndroid Build Coastguard Worker 
852*053f45beSAndroid Build Coastguard Worker 	return 0;
853*053f45beSAndroid Build Coastguard Worker }
854*053f45beSAndroid Build Coastguard Worker 
parse_stat_value(const char * str,enum stat_id id,struct verif_stats * st)855*053f45beSAndroid Build Coastguard Worker static int parse_stat_value(const char *str, enum stat_id id, struct verif_stats *st)
856*053f45beSAndroid Build Coastguard Worker {
857*053f45beSAndroid Build Coastguard Worker 	switch (id) {
858*053f45beSAndroid Build Coastguard Worker 	case FILE_NAME:
859*053f45beSAndroid Build Coastguard Worker 		st->file_name = strdup(str);
860*053f45beSAndroid Build Coastguard Worker 		if (!st->file_name)
861*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
862*053f45beSAndroid Build Coastguard Worker 		break;
863*053f45beSAndroid Build Coastguard Worker 	case PROG_NAME:
864*053f45beSAndroid Build Coastguard Worker 		st->prog_name = strdup(str);
865*053f45beSAndroid Build Coastguard Worker 		if (!st->prog_name)
866*053f45beSAndroid Build Coastguard Worker 			return -ENOMEM;
867*053f45beSAndroid Build Coastguard Worker 		break;
868*053f45beSAndroid Build Coastguard Worker 	case VERDICT:
869*053f45beSAndroid Build Coastguard Worker 		if (strcmp(str, "success") == 0) {
870*053f45beSAndroid Build Coastguard Worker 			st->stats[VERDICT] = true;
871*053f45beSAndroid Build Coastguard Worker 		} else if (strcmp(str, "failure") == 0) {
872*053f45beSAndroid Build Coastguard Worker 			st->stats[VERDICT] = false;
873*053f45beSAndroid Build Coastguard Worker 		} else {
874*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Unrecognized verification verdict '%s'\n", str);
875*053f45beSAndroid Build Coastguard Worker 			return -EINVAL;
876*053f45beSAndroid Build Coastguard Worker 		}
877*053f45beSAndroid Build Coastguard Worker 		break;
878*053f45beSAndroid Build Coastguard Worker 	case DURATION:
879*053f45beSAndroid Build Coastguard Worker 	case TOTAL_INSNS:
880*053f45beSAndroid Build Coastguard Worker 	case TOTAL_STATES:
881*053f45beSAndroid Build Coastguard Worker 	case PEAK_STATES:
882*053f45beSAndroid Build Coastguard Worker 	case MAX_STATES_PER_INSN:
883*053f45beSAndroid Build Coastguard Worker 	case MARK_READ_MAX_LEN: {
884*053f45beSAndroid Build Coastguard Worker 		long val;
885*053f45beSAndroid Build Coastguard Worker 		int err, n;
886*053f45beSAndroid Build Coastguard Worker 
887*053f45beSAndroid Build Coastguard Worker 		if (sscanf(str, "%ld %n", &val, &n) != 1 || n != strlen(str)) {
888*053f45beSAndroid Build Coastguard Worker 			err = -errno;
889*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Failed to parse '%s' as integer\n", str);
890*053f45beSAndroid Build Coastguard Worker 			return err;
891*053f45beSAndroid Build Coastguard Worker 		}
892*053f45beSAndroid Build Coastguard Worker 
893*053f45beSAndroid Build Coastguard Worker 		st->stats[id] = val;
894*053f45beSAndroid Build Coastguard Worker 		break;
895*053f45beSAndroid Build Coastguard Worker 	}
896*053f45beSAndroid Build Coastguard Worker 	default:
897*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Unrecognized stat #%d\n", id);
898*053f45beSAndroid Build Coastguard Worker 		return -EINVAL;
899*053f45beSAndroid Build Coastguard Worker 	}
900*053f45beSAndroid Build Coastguard Worker 	return 0;
901*053f45beSAndroid Build Coastguard Worker }
902*053f45beSAndroid Build Coastguard Worker 
parse_stats_csv(const char * filename,struct stat_specs * specs,struct verif_stats ** statsp,int * stat_cntp)903*053f45beSAndroid Build Coastguard Worker static int parse_stats_csv(const char *filename, struct stat_specs *specs,
904*053f45beSAndroid Build Coastguard Worker 			   struct verif_stats **statsp, int *stat_cntp)
905*053f45beSAndroid Build Coastguard Worker {
906*053f45beSAndroid Build Coastguard Worker 	char line[4096];
907*053f45beSAndroid Build Coastguard Worker 	FILE *f;
908*053f45beSAndroid Build Coastguard Worker 	int err = 0;
909*053f45beSAndroid Build Coastguard Worker 	bool header = true;
910*053f45beSAndroid Build Coastguard Worker 
911*053f45beSAndroid Build Coastguard Worker 	f = fopen(filename, "r");
912*053f45beSAndroid Build Coastguard Worker 	if (!f) {
913*053f45beSAndroid Build Coastguard Worker 		err = -errno;
914*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Failed to open '%s': %d\n", filename, err);
915*053f45beSAndroid Build Coastguard Worker 		return err;
916*053f45beSAndroid Build Coastguard Worker 	}
917*053f45beSAndroid Build Coastguard Worker 
918*053f45beSAndroid Build Coastguard Worker 	*stat_cntp = 0;
919*053f45beSAndroid Build Coastguard Worker 
920*053f45beSAndroid Build Coastguard Worker 	while (fgets(line, sizeof(line), f)) {
921*053f45beSAndroid Build Coastguard Worker 		char *input = line, *state = NULL, *next;
922*053f45beSAndroid Build Coastguard Worker 		struct verif_stats *st = NULL;
923*053f45beSAndroid Build Coastguard Worker 		int col = 0;
924*053f45beSAndroid Build Coastguard Worker 
925*053f45beSAndroid Build Coastguard Worker 		if (!header) {
926*053f45beSAndroid Build Coastguard Worker 			void *tmp;
927*053f45beSAndroid Build Coastguard Worker 
928*053f45beSAndroid Build Coastguard Worker 			tmp = realloc(*statsp, (*stat_cntp + 1) * sizeof(**statsp));
929*053f45beSAndroid Build Coastguard Worker 			if (!tmp) {
930*053f45beSAndroid Build Coastguard Worker 				err = -ENOMEM;
931*053f45beSAndroid Build Coastguard Worker 				goto cleanup;
932*053f45beSAndroid Build Coastguard Worker 			}
933*053f45beSAndroid Build Coastguard Worker 			*statsp = tmp;
934*053f45beSAndroid Build Coastguard Worker 
935*053f45beSAndroid Build Coastguard Worker 			st = &(*statsp)[*stat_cntp];
936*053f45beSAndroid Build Coastguard Worker 			memset(st, 0, sizeof(*st));
937*053f45beSAndroid Build Coastguard Worker 
938*053f45beSAndroid Build Coastguard Worker 			*stat_cntp += 1;
939*053f45beSAndroid Build Coastguard Worker 		}
940*053f45beSAndroid Build Coastguard Worker 
941*053f45beSAndroid Build Coastguard Worker 		while ((next = strtok_r(state ? NULL : input, ",\n", &state))) {
942*053f45beSAndroid Build Coastguard Worker 			if (header) {
943*053f45beSAndroid Build Coastguard Worker 				/* for the first line, set up spec stats */
944*053f45beSAndroid Build Coastguard Worker 				err = parse_stat(next, specs);
945*053f45beSAndroid Build Coastguard Worker 				if (err)
946*053f45beSAndroid Build Coastguard Worker 					goto cleanup;
947*053f45beSAndroid Build Coastguard Worker 				continue;
948*053f45beSAndroid Build Coastguard Worker 			}
949*053f45beSAndroid Build Coastguard Worker 
950*053f45beSAndroid Build Coastguard Worker 			/* for all other lines, parse values based on spec */
951*053f45beSAndroid Build Coastguard Worker 			if (col >= specs->spec_cnt) {
952*053f45beSAndroid Build Coastguard Worker 				fprintf(stderr, "Found extraneous column #%d in row #%d of '%s'\n",
953*053f45beSAndroid Build Coastguard Worker 					col, *stat_cntp, filename);
954*053f45beSAndroid Build Coastguard Worker 				err = -EINVAL;
955*053f45beSAndroid Build Coastguard Worker 				goto cleanup;
956*053f45beSAndroid Build Coastguard Worker 			}
957*053f45beSAndroid Build Coastguard Worker 			err = parse_stat_value(next, specs->ids[col], st);
958*053f45beSAndroid Build Coastguard Worker 			if (err)
959*053f45beSAndroid Build Coastguard Worker 				goto cleanup;
960*053f45beSAndroid Build Coastguard Worker 			col++;
961*053f45beSAndroid Build Coastguard Worker 		}
962*053f45beSAndroid Build Coastguard Worker 
963*053f45beSAndroid Build Coastguard Worker 		if (header) {
964*053f45beSAndroid Build Coastguard Worker 			header = false;
965*053f45beSAndroid Build Coastguard Worker 			continue;
966*053f45beSAndroid Build Coastguard Worker 		}
967*053f45beSAndroid Build Coastguard Worker 
968*053f45beSAndroid Build Coastguard Worker 		if (col < specs->spec_cnt) {
969*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Not enough columns in row #%d in '%s'\n",
970*053f45beSAndroid Build Coastguard Worker 				*stat_cntp, filename);
971*053f45beSAndroid Build Coastguard Worker 			err = -EINVAL;
972*053f45beSAndroid Build Coastguard Worker 			goto cleanup;
973*053f45beSAndroid Build Coastguard Worker 		}
974*053f45beSAndroid Build Coastguard Worker 
975*053f45beSAndroid Build Coastguard Worker 		if (!st->file_name || !st->prog_name) {
976*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Row #%d in '%s' is missing file and/or program name\n",
977*053f45beSAndroid Build Coastguard Worker 				*stat_cntp, filename);
978*053f45beSAndroid Build Coastguard Worker 			err = -EINVAL;
979*053f45beSAndroid Build Coastguard Worker 			goto cleanup;
980*053f45beSAndroid Build Coastguard Worker 		}
981*053f45beSAndroid Build Coastguard Worker 
982*053f45beSAndroid Build Coastguard Worker 		/* in comparison mode we can only check filters after we
983*053f45beSAndroid Build Coastguard Worker 		 * parsed entire line; if row should be ignored we pretend we
984*053f45beSAndroid Build Coastguard Worker 		 * never parsed it
985*053f45beSAndroid Build Coastguard Worker 		 */
986*053f45beSAndroid Build Coastguard Worker 		if (!should_process_prog(st->file_name, st->prog_name)) {
987*053f45beSAndroid Build Coastguard Worker 			free(st->file_name);
988*053f45beSAndroid Build Coastguard Worker 			free(st->prog_name);
989*053f45beSAndroid Build Coastguard Worker 			*stat_cntp -= 1;
990*053f45beSAndroid Build Coastguard Worker 		}
991*053f45beSAndroid Build Coastguard Worker 	}
992*053f45beSAndroid Build Coastguard Worker 
993*053f45beSAndroid Build Coastguard Worker 	if (!feof(f)) {
994*053f45beSAndroid Build Coastguard Worker 		err = -errno;
995*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Failed I/O for '%s': %d\n", filename, err);
996*053f45beSAndroid Build Coastguard Worker 	}
997*053f45beSAndroid Build Coastguard Worker 
998*053f45beSAndroid Build Coastguard Worker cleanup:
999*053f45beSAndroid Build Coastguard Worker 	fclose(f);
1000*053f45beSAndroid Build Coastguard Worker 	return err;
1001*053f45beSAndroid Build Coastguard Worker }
1002*053f45beSAndroid Build Coastguard Worker 
1003*053f45beSAndroid Build Coastguard Worker /* empty/zero stats for mismatched rows */
1004*053f45beSAndroid Build Coastguard Worker static const struct verif_stats fallback_stats = { .file_name = "", .prog_name = "" };
1005*053f45beSAndroid Build Coastguard Worker 
is_key_stat(enum stat_id id)1006*053f45beSAndroid Build Coastguard Worker static bool is_key_stat(enum stat_id id)
1007*053f45beSAndroid Build Coastguard Worker {
1008*053f45beSAndroid Build Coastguard Worker 	return id == FILE_NAME || id == PROG_NAME;
1009*053f45beSAndroid Build Coastguard Worker }
1010*053f45beSAndroid Build Coastguard Worker 
output_comp_header_underlines(void)1011*053f45beSAndroid Build Coastguard Worker static void output_comp_header_underlines(void)
1012*053f45beSAndroid Build Coastguard Worker {
1013*053f45beSAndroid Build Coastguard Worker 	int i, j, k;
1014*053f45beSAndroid Build Coastguard Worker 
1015*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.output_spec.spec_cnt; i++) {
1016*053f45beSAndroid Build Coastguard Worker 		int id = env.output_spec.ids[i];
1017*053f45beSAndroid Build Coastguard Worker 		int max_j = is_key_stat(id) ? 1 : 3;
1018*053f45beSAndroid Build Coastguard Worker 
1019*053f45beSAndroid Build Coastguard Worker 		for (j = 0; j < max_j; j++) {
1020*053f45beSAndroid Build Coastguard Worker 			int len = env.output_spec.lens[3 * i + j];
1021*053f45beSAndroid Build Coastguard Worker 
1022*053f45beSAndroid Build Coastguard Worker 			printf("%s", i + j == 0 ? "" : COLUMN_SEP);
1023*053f45beSAndroid Build Coastguard Worker 
1024*053f45beSAndroid Build Coastguard Worker 			for (k = 0; k < len; k++)
1025*053f45beSAndroid Build Coastguard Worker 				printf("%c", HEADER_CHAR);
1026*053f45beSAndroid Build Coastguard Worker 		}
1027*053f45beSAndroid Build Coastguard Worker 	}
1028*053f45beSAndroid Build Coastguard Worker 	printf("\n");
1029*053f45beSAndroid Build Coastguard Worker }
1030*053f45beSAndroid Build Coastguard Worker 
output_comp_headers(enum resfmt fmt)1031*053f45beSAndroid Build Coastguard Worker static void output_comp_headers(enum resfmt fmt)
1032*053f45beSAndroid Build Coastguard Worker {
1033*053f45beSAndroid Build Coastguard Worker 	static const char *table_sfxs[3] = {" (A)", " (B)", " (DIFF)"};
1034*053f45beSAndroid Build Coastguard Worker 	static const char *name_sfxs[3] = {"_base", "_comp", "_diff"};
1035*053f45beSAndroid Build Coastguard Worker 	int i, j, len;
1036*053f45beSAndroid Build Coastguard Worker 
1037*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.output_spec.spec_cnt; i++) {
1038*053f45beSAndroid Build Coastguard Worker 		int id = env.output_spec.ids[i];
1039*053f45beSAndroid Build Coastguard Worker 		/* key stats don't have A/B/DIFF columns, they are common for both data sets */
1040*053f45beSAndroid Build Coastguard Worker 		int max_j = is_key_stat(id) ? 1 : 3;
1041*053f45beSAndroid Build Coastguard Worker 
1042*053f45beSAndroid Build Coastguard Worker 		for (j = 0; j < max_j; j++) {
1043*053f45beSAndroid Build Coastguard Worker 			int *max_len = &env.output_spec.lens[3 * i + j];
1044*053f45beSAndroid Build Coastguard Worker 			bool last = (i == env.output_spec.spec_cnt - 1) && (j == max_j - 1);
1045*053f45beSAndroid Build Coastguard Worker 			const char *sfx;
1046*053f45beSAndroid Build Coastguard Worker 
1047*053f45beSAndroid Build Coastguard Worker 			switch (fmt) {
1048*053f45beSAndroid Build Coastguard Worker 			case RESFMT_TABLE_CALCLEN:
1049*053f45beSAndroid Build Coastguard Worker 				sfx = is_key_stat(id) ? "" : table_sfxs[j];
1050*053f45beSAndroid Build Coastguard Worker 				len = snprintf(NULL, 0, "%s%s", stat_defs[id].header, sfx);
1051*053f45beSAndroid Build Coastguard Worker 				if (len > *max_len)
1052*053f45beSAndroid Build Coastguard Worker 					*max_len = len;
1053*053f45beSAndroid Build Coastguard Worker 				break;
1054*053f45beSAndroid Build Coastguard Worker 			case RESFMT_TABLE:
1055*053f45beSAndroid Build Coastguard Worker 				sfx = is_key_stat(id) ? "" : table_sfxs[j];
1056*053f45beSAndroid Build Coastguard Worker 				printf("%s%-*s%s", i + j == 0 ? "" : COLUMN_SEP,
1057*053f45beSAndroid Build Coastguard Worker 				       *max_len - (int)strlen(sfx), stat_defs[id].header, sfx);
1058*053f45beSAndroid Build Coastguard Worker 				if (last)
1059*053f45beSAndroid Build Coastguard Worker 					printf("\n");
1060*053f45beSAndroid Build Coastguard Worker 				break;
1061*053f45beSAndroid Build Coastguard Worker 			case RESFMT_CSV:
1062*053f45beSAndroid Build Coastguard Worker 				sfx = is_key_stat(id) ? "" : name_sfxs[j];
1063*053f45beSAndroid Build Coastguard Worker 				printf("%s%s%s", i + j == 0 ? "" : ",", stat_defs[id].names[0], sfx);
1064*053f45beSAndroid Build Coastguard Worker 				if (last)
1065*053f45beSAndroid Build Coastguard Worker 					printf("\n");
1066*053f45beSAndroid Build Coastguard Worker 				break;
1067*053f45beSAndroid Build Coastguard Worker 			}
1068*053f45beSAndroid Build Coastguard Worker 		}
1069*053f45beSAndroid Build Coastguard Worker 	}
1070*053f45beSAndroid Build Coastguard Worker 
1071*053f45beSAndroid Build Coastguard Worker 	if (fmt == RESFMT_TABLE)
1072*053f45beSAndroid Build Coastguard Worker 		output_comp_header_underlines();
1073*053f45beSAndroid Build Coastguard Worker }
1074*053f45beSAndroid Build Coastguard Worker 
output_comp_stats(const struct verif_stats * base,const struct verif_stats * comp,enum resfmt fmt,bool last)1075*053f45beSAndroid Build Coastguard Worker static void output_comp_stats(const struct verif_stats *base, const struct verif_stats *comp,
1076*053f45beSAndroid Build Coastguard Worker 			      enum resfmt fmt, bool last)
1077*053f45beSAndroid Build Coastguard Worker {
1078*053f45beSAndroid Build Coastguard Worker 	char base_buf[1024] = {}, comp_buf[1024] = {}, diff_buf[1024] = {};
1079*053f45beSAndroid Build Coastguard Worker 	int i;
1080*053f45beSAndroid Build Coastguard Worker 
1081*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.output_spec.spec_cnt; i++) {
1082*053f45beSAndroid Build Coastguard Worker 		int id = env.output_spec.ids[i], len;
1083*053f45beSAndroid Build Coastguard Worker 		int *max_len_base = &env.output_spec.lens[3 * i + 0];
1084*053f45beSAndroid Build Coastguard Worker 		int *max_len_comp = &env.output_spec.lens[3 * i + 1];
1085*053f45beSAndroid Build Coastguard Worker 		int *max_len_diff = &env.output_spec.lens[3 * i + 2];
1086*053f45beSAndroid Build Coastguard Worker 		const char *base_str = NULL, *comp_str = NULL;
1087*053f45beSAndroid Build Coastguard Worker 		long base_val = 0, comp_val = 0, diff_val = 0;
1088*053f45beSAndroid Build Coastguard Worker 
1089*053f45beSAndroid Build Coastguard Worker 		prepare_value(base, id, &base_str, &base_val);
1090*053f45beSAndroid Build Coastguard Worker 		prepare_value(comp, id, &comp_str, &comp_val);
1091*053f45beSAndroid Build Coastguard Worker 
1092*053f45beSAndroid Build Coastguard Worker 		/* normalize all the outputs to be in string buffers for simplicity */
1093*053f45beSAndroid Build Coastguard Worker 		if (is_key_stat(id)) {
1094*053f45beSAndroid Build Coastguard Worker 			/* key stats (file and program name) are always strings */
1095*053f45beSAndroid Build Coastguard Worker 			if (base != &fallback_stats)
1096*053f45beSAndroid Build Coastguard Worker 				snprintf(base_buf, sizeof(base_buf), "%s", base_str);
1097*053f45beSAndroid Build Coastguard Worker 			else
1098*053f45beSAndroid Build Coastguard Worker 				snprintf(base_buf, sizeof(base_buf), "%s", comp_str);
1099*053f45beSAndroid Build Coastguard Worker 		} else if (base_str) {
1100*053f45beSAndroid Build Coastguard Worker 			snprintf(base_buf, sizeof(base_buf), "%s", base_str);
1101*053f45beSAndroid Build Coastguard Worker 			snprintf(comp_buf, sizeof(comp_buf), "%s", comp_str);
1102*053f45beSAndroid Build Coastguard Worker 			if (strcmp(base_str, comp_str) == 0)
1103*053f45beSAndroid Build Coastguard Worker 				snprintf(diff_buf, sizeof(diff_buf), "%s", "MATCH");
1104*053f45beSAndroid Build Coastguard Worker 			else
1105*053f45beSAndroid Build Coastguard Worker 				snprintf(diff_buf, sizeof(diff_buf), "%s", "MISMATCH");
1106*053f45beSAndroid Build Coastguard Worker 		} else {
1107*053f45beSAndroid Build Coastguard Worker 			snprintf(base_buf, sizeof(base_buf), "%ld", base_val);
1108*053f45beSAndroid Build Coastguard Worker 			snprintf(comp_buf, sizeof(comp_buf), "%ld", comp_val);
1109*053f45beSAndroid Build Coastguard Worker 
1110*053f45beSAndroid Build Coastguard Worker 			diff_val = comp_val - base_val;
1111*053f45beSAndroid Build Coastguard Worker 			if (base == &fallback_stats || comp == &fallback_stats || base_val == 0) {
1112*053f45beSAndroid Build Coastguard Worker 				snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)",
1113*053f45beSAndroid Build Coastguard Worker 					 diff_val, comp_val < base_val ? -100.0 : 100.0);
1114*053f45beSAndroid Build Coastguard Worker 			} else {
1115*053f45beSAndroid Build Coastguard Worker 				snprintf(diff_buf, sizeof(diff_buf), "%+ld (%+.2lf%%)",
1116*053f45beSAndroid Build Coastguard Worker 					 diff_val, diff_val * 100.0 / base_val);
1117*053f45beSAndroid Build Coastguard Worker 			}
1118*053f45beSAndroid Build Coastguard Worker 		}
1119*053f45beSAndroid Build Coastguard Worker 
1120*053f45beSAndroid Build Coastguard Worker 		switch (fmt) {
1121*053f45beSAndroid Build Coastguard Worker 		case RESFMT_TABLE_CALCLEN:
1122*053f45beSAndroid Build Coastguard Worker 			len = strlen(base_buf);
1123*053f45beSAndroid Build Coastguard Worker 			if (len > *max_len_base)
1124*053f45beSAndroid Build Coastguard Worker 				*max_len_base = len;
1125*053f45beSAndroid Build Coastguard Worker 			if (!is_key_stat(id)) {
1126*053f45beSAndroid Build Coastguard Worker 				len = strlen(comp_buf);
1127*053f45beSAndroid Build Coastguard Worker 				if (len > *max_len_comp)
1128*053f45beSAndroid Build Coastguard Worker 					*max_len_comp = len;
1129*053f45beSAndroid Build Coastguard Worker 				len = strlen(diff_buf);
1130*053f45beSAndroid Build Coastguard Worker 				if (len > *max_len_diff)
1131*053f45beSAndroid Build Coastguard Worker 					*max_len_diff = len;
1132*053f45beSAndroid Build Coastguard Worker 			}
1133*053f45beSAndroid Build Coastguard Worker 			break;
1134*053f45beSAndroid Build Coastguard Worker 		case RESFMT_TABLE: {
1135*053f45beSAndroid Build Coastguard Worker 			/* string outputs are left-aligned, number outputs are right-aligned */
1136*053f45beSAndroid Build Coastguard Worker 			const char *fmt = base_str ? "%s%-*s" : "%s%*s";
1137*053f45beSAndroid Build Coastguard Worker 
1138*053f45beSAndroid Build Coastguard Worker 			printf(fmt, i == 0 ? "" : COLUMN_SEP, *max_len_base, base_buf);
1139*053f45beSAndroid Build Coastguard Worker 			if (!is_key_stat(id)) {
1140*053f45beSAndroid Build Coastguard Worker 				printf(fmt, COLUMN_SEP, *max_len_comp, comp_buf);
1141*053f45beSAndroid Build Coastguard Worker 				printf(fmt, COLUMN_SEP, *max_len_diff, diff_buf);
1142*053f45beSAndroid Build Coastguard Worker 			}
1143*053f45beSAndroid Build Coastguard Worker 			if (i == env.output_spec.spec_cnt - 1)
1144*053f45beSAndroid Build Coastguard Worker 				printf("\n");
1145*053f45beSAndroid Build Coastguard Worker 			break;
1146*053f45beSAndroid Build Coastguard Worker 		}
1147*053f45beSAndroid Build Coastguard Worker 		case RESFMT_CSV:
1148*053f45beSAndroid Build Coastguard Worker 			printf("%s%s", i == 0 ? "" : ",", base_buf);
1149*053f45beSAndroid Build Coastguard Worker 			if (!is_key_stat(id)) {
1150*053f45beSAndroid Build Coastguard Worker 				printf("%s%s", i == 0 ? "" : ",", comp_buf);
1151*053f45beSAndroid Build Coastguard Worker 				printf("%s%s", i == 0 ? "" : ",", diff_buf);
1152*053f45beSAndroid Build Coastguard Worker 			}
1153*053f45beSAndroid Build Coastguard Worker 			if (i == env.output_spec.spec_cnt - 1)
1154*053f45beSAndroid Build Coastguard Worker 				printf("\n");
1155*053f45beSAndroid Build Coastguard Worker 			break;
1156*053f45beSAndroid Build Coastguard Worker 		}
1157*053f45beSAndroid Build Coastguard Worker 	}
1158*053f45beSAndroid Build Coastguard Worker 
1159*053f45beSAndroid Build Coastguard Worker 	if (last && fmt == RESFMT_TABLE)
1160*053f45beSAndroid Build Coastguard Worker 		output_comp_header_underlines();
1161*053f45beSAndroid Build Coastguard Worker }
1162*053f45beSAndroid Build Coastguard Worker 
cmp_stats_key(const struct verif_stats * base,const struct verif_stats * comp)1163*053f45beSAndroid Build Coastguard Worker static int cmp_stats_key(const struct verif_stats *base, const struct verif_stats *comp)
1164*053f45beSAndroid Build Coastguard Worker {
1165*053f45beSAndroid Build Coastguard Worker 	int r;
1166*053f45beSAndroid Build Coastguard Worker 
1167*053f45beSAndroid Build Coastguard Worker 	r = strcmp(base->file_name, comp->file_name);
1168*053f45beSAndroid Build Coastguard Worker 	if (r != 0)
1169*053f45beSAndroid Build Coastguard Worker 		return r;
1170*053f45beSAndroid Build Coastguard Worker 	return strcmp(base->prog_name, comp->prog_name);
1171*053f45beSAndroid Build Coastguard Worker }
1172*053f45beSAndroid Build Coastguard Worker 
handle_comparison_mode(void)1173*053f45beSAndroid Build Coastguard Worker static int handle_comparison_mode(void)
1174*053f45beSAndroid Build Coastguard Worker {
1175*053f45beSAndroid Build Coastguard Worker 	struct stat_specs base_specs = {}, comp_specs = {};
1176*053f45beSAndroid Build Coastguard Worker 	enum resfmt cur_fmt;
1177*053f45beSAndroid Build Coastguard Worker 	int err, i, j;
1178*053f45beSAndroid Build Coastguard Worker 
1179*053f45beSAndroid Build Coastguard Worker 	if (env.filename_cnt != 2) {
1180*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Comparison mode expects exactly two input CSV files!\n");
1181*053f45beSAndroid Build Coastguard Worker 		argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat");
1182*053f45beSAndroid Build Coastguard Worker 		return -EINVAL;
1183*053f45beSAndroid Build Coastguard Worker 	}
1184*053f45beSAndroid Build Coastguard Worker 
1185*053f45beSAndroid Build Coastguard Worker 	err = parse_stats_csv(env.filenames[0], &base_specs,
1186*053f45beSAndroid Build Coastguard Worker 			      &env.baseline_stats, &env.baseline_stat_cnt);
1187*053f45beSAndroid Build Coastguard Worker 	if (err) {
1188*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Failed to parse stats from '%s': %d\n", env.filenames[0], err);
1189*053f45beSAndroid Build Coastguard Worker 		return err;
1190*053f45beSAndroid Build Coastguard Worker 	}
1191*053f45beSAndroid Build Coastguard Worker 	err = parse_stats_csv(env.filenames[1], &comp_specs,
1192*053f45beSAndroid Build Coastguard Worker 			      &env.prog_stats, &env.prog_stat_cnt);
1193*053f45beSAndroid Build Coastguard Worker 	if (err) {
1194*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Failed to parse stats from '%s': %d\n", env.filenames[1], err);
1195*053f45beSAndroid Build Coastguard Worker 		return err;
1196*053f45beSAndroid Build Coastguard Worker 	}
1197*053f45beSAndroid Build Coastguard Worker 
1198*053f45beSAndroid Build Coastguard Worker 	/* To keep it simple we validate that the set and order of stats in
1199*053f45beSAndroid Build Coastguard Worker 	 * both CSVs are exactly the same. This can be lifted with a bit more
1200*053f45beSAndroid Build Coastguard Worker 	 * pre-processing later.
1201*053f45beSAndroid Build Coastguard Worker 	 */
1202*053f45beSAndroid Build Coastguard Worker 	if (base_specs.spec_cnt != comp_specs.spec_cnt) {
1203*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Number of stats in '%s' and '%s' differs (%d != %d)!\n",
1204*053f45beSAndroid Build Coastguard Worker 			env.filenames[0], env.filenames[1],
1205*053f45beSAndroid Build Coastguard Worker 			base_specs.spec_cnt, comp_specs.spec_cnt);
1206*053f45beSAndroid Build Coastguard Worker 		return -EINVAL;
1207*053f45beSAndroid Build Coastguard Worker 	}
1208*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < base_specs.spec_cnt; i++) {
1209*053f45beSAndroid Build Coastguard Worker 		if (base_specs.ids[i] != comp_specs.ids[i]) {
1210*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Stats composition differs between '%s' and '%s' (%s != %s)!\n",
1211*053f45beSAndroid Build Coastguard Worker 				env.filenames[0], env.filenames[1],
1212*053f45beSAndroid Build Coastguard Worker 				stat_defs[base_specs.ids[i]].names[0],
1213*053f45beSAndroid Build Coastguard Worker 				stat_defs[comp_specs.ids[i]].names[0]);
1214*053f45beSAndroid Build Coastguard Worker 			return -EINVAL;
1215*053f45beSAndroid Build Coastguard Worker 		}
1216*053f45beSAndroid Build Coastguard Worker 	}
1217*053f45beSAndroid Build Coastguard Worker 
1218*053f45beSAndroid Build Coastguard Worker 	qsort(env.prog_stats, env.prog_stat_cnt, sizeof(*env.prog_stats), cmp_prog_stats);
1219*053f45beSAndroid Build Coastguard Worker 	qsort(env.baseline_stats, env.baseline_stat_cnt, sizeof(*env.baseline_stats), cmp_prog_stats);
1220*053f45beSAndroid Build Coastguard Worker 
1221*053f45beSAndroid Build Coastguard Worker 	/* for human-readable table output we need to do extra pass to
1222*053f45beSAndroid Build Coastguard Worker 	 * calculate column widths, so we substitute current output format
1223*053f45beSAndroid Build Coastguard Worker 	 * with RESFMT_TABLE_CALCLEN and later revert it back to RESFMT_TABLE
1224*053f45beSAndroid Build Coastguard Worker 	 * and do everything again.
1225*053f45beSAndroid Build Coastguard Worker 	 */
1226*053f45beSAndroid Build Coastguard Worker 	if (env.out_fmt == RESFMT_TABLE)
1227*053f45beSAndroid Build Coastguard Worker 		cur_fmt = RESFMT_TABLE_CALCLEN;
1228*053f45beSAndroid Build Coastguard Worker 	else
1229*053f45beSAndroid Build Coastguard Worker 		cur_fmt = env.out_fmt;
1230*053f45beSAndroid Build Coastguard Worker 
1231*053f45beSAndroid Build Coastguard Worker one_more_time:
1232*053f45beSAndroid Build Coastguard Worker 	output_comp_headers(cur_fmt);
1233*053f45beSAndroid Build Coastguard Worker 
1234*053f45beSAndroid Build Coastguard Worker 	/* If baseline and comparison datasets have different subset of rows
1235*053f45beSAndroid Build Coastguard Worker 	 * (we match by 'object + prog' as a unique key) then assume
1236*053f45beSAndroid Build Coastguard Worker 	 * empty/missing/zero value for rows that are missing in the opposite
1237*053f45beSAndroid Build Coastguard Worker 	 * data set
1238*053f45beSAndroid Build Coastguard Worker 	 */
1239*053f45beSAndroid Build Coastguard Worker 	i = j = 0;
1240*053f45beSAndroid Build Coastguard Worker 	while (i < env.baseline_stat_cnt || j < env.prog_stat_cnt) {
1241*053f45beSAndroid Build Coastguard Worker 		bool last = (i == env.baseline_stat_cnt - 1) || (j == env.prog_stat_cnt - 1);
1242*053f45beSAndroid Build Coastguard Worker 		const struct verif_stats *base, *comp;
1243*053f45beSAndroid Build Coastguard Worker 		int r;
1244*053f45beSAndroid Build Coastguard Worker 
1245*053f45beSAndroid Build Coastguard Worker 		base = i < env.baseline_stat_cnt ? &env.baseline_stats[i] : &fallback_stats;
1246*053f45beSAndroid Build Coastguard Worker 		comp = j < env.prog_stat_cnt ? &env.prog_stats[j] : &fallback_stats;
1247*053f45beSAndroid Build Coastguard Worker 
1248*053f45beSAndroid Build Coastguard Worker 		if (!base->file_name || !base->prog_name) {
1249*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Entry #%d in '%s' doesn't have file and/or program name specified!\n",
1250*053f45beSAndroid Build Coastguard Worker 				i, env.filenames[0]);
1251*053f45beSAndroid Build Coastguard Worker 			return -EINVAL;
1252*053f45beSAndroid Build Coastguard Worker 		}
1253*053f45beSAndroid Build Coastguard Worker 		if (!comp->file_name || !comp->prog_name) {
1254*053f45beSAndroid Build Coastguard Worker 			fprintf(stderr, "Entry #%d in '%s' doesn't have file and/or program name specified!\n",
1255*053f45beSAndroid Build Coastguard Worker 				j, env.filenames[1]);
1256*053f45beSAndroid Build Coastguard Worker 			return -EINVAL;
1257*053f45beSAndroid Build Coastguard Worker 		}
1258*053f45beSAndroid Build Coastguard Worker 
1259*053f45beSAndroid Build Coastguard Worker 		r = cmp_stats_key(base, comp);
1260*053f45beSAndroid Build Coastguard Worker 		if (r == 0) {
1261*053f45beSAndroid Build Coastguard Worker 			output_comp_stats(base, comp, cur_fmt, last);
1262*053f45beSAndroid Build Coastguard Worker 			i++;
1263*053f45beSAndroid Build Coastguard Worker 			j++;
1264*053f45beSAndroid Build Coastguard Worker 		} else if (comp == &fallback_stats || r < 0) {
1265*053f45beSAndroid Build Coastguard Worker 			output_comp_stats(base, &fallback_stats, cur_fmt, last);
1266*053f45beSAndroid Build Coastguard Worker 			i++;
1267*053f45beSAndroid Build Coastguard Worker 		} else {
1268*053f45beSAndroid Build Coastguard Worker 			output_comp_stats(&fallback_stats, comp, cur_fmt, last);
1269*053f45beSAndroid Build Coastguard Worker 			j++;
1270*053f45beSAndroid Build Coastguard Worker 		}
1271*053f45beSAndroid Build Coastguard Worker 	}
1272*053f45beSAndroid Build Coastguard Worker 
1273*053f45beSAndroid Build Coastguard Worker 	if (cur_fmt == RESFMT_TABLE_CALCLEN) {
1274*053f45beSAndroid Build Coastguard Worker 		cur_fmt = RESFMT_TABLE;
1275*053f45beSAndroid Build Coastguard Worker 		goto one_more_time; /* ... this time with feeling */
1276*053f45beSAndroid Build Coastguard Worker 	}
1277*053f45beSAndroid Build Coastguard Worker 
1278*053f45beSAndroid Build Coastguard Worker 	return 0;
1279*053f45beSAndroid Build Coastguard Worker }
1280*053f45beSAndroid Build Coastguard Worker 
main(int argc,char ** argv)1281*053f45beSAndroid Build Coastguard Worker int main(int argc, char **argv)
1282*053f45beSAndroid Build Coastguard Worker {
1283*053f45beSAndroid Build Coastguard Worker 	int err = 0, i;
1284*053f45beSAndroid Build Coastguard Worker 
1285*053f45beSAndroid Build Coastguard Worker 	if (argp_parse(&argp, argc, argv, 0, NULL, NULL))
1286*053f45beSAndroid Build Coastguard Worker 		return 1;
1287*053f45beSAndroid Build Coastguard Worker 
1288*053f45beSAndroid Build Coastguard Worker 	if (env.verbose && env.quiet) {
1289*053f45beSAndroid Build Coastguard Worker 		fprintf(stderr, "Verbose and quiet modes are incompatible, please specify just one or neither!\n");
1290*053f45beSAndroid Build Coastguard Worker 		argp_help(&argp, stderr, ARGP_HELP_USAGE, "veristat");
1291*053f45beSAndroid Build Coastguard Worker 		return 1;
1292*053f45beSAndroid Build Coastguard Worker 	}
1293*053f45beSAndroid Build Coastguard Worker 	if (env.verbose && env.log_level == 0)
1294*053f45beSAndroid Build Coastguard Worker 		env.log_level = 1;
1295*053f45beSAndroid Build Coastguard Worker 
1296*053f45beSAndroid Build Coastguard Worker 	if (env.output_spec.spec_cnt == 0)
1297*053f45beSAndroid Build Coastguard Worker 		env.output_spec = default_output_spec;
1298*053f45beSAndroid Build Coastguard Worker 	if (env.sort_spec.spec_cnt == 0)
1299*053f45beSAndroid Build Coastguard Worker 		env.sort_spec = default_sort_spec;
1300*053f45beSAndroid Build Coastguard Worker 
1301*053f45beSAndroid Build Coastguard Worker 	if (env.comparison_mode)
1302*053f45beSAndroid Build Coastguard Worker 		err = handle_comparison_mode();
1303*053f45beSAndroid Build Coastguard Worker 	else
1304*053f45beSAndroid Build Coastguard Worker 		err = handle_verif_mode();
1305*053f45beSAndroid Build Coastguard Worker 
1306*053f45beSAndroid Build Coastguard Worker 	free_verif_stats(env.prog_stats, env.prog_stat_cnt);
1307*053f45beSAndroid Build Coastguard Worker 	free_verif_stats(env.baseline_stats, env.baseline_stat_cnt);
1308*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.filename_cnt; i++)
1309*053f45beSAndroid Build Coastguard Worker 		free(env.filenames[i]);
1310*053f45beSAndroid Build Coastguard Worker 	free(env.filenames);
1311*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.allow_filter_cnt; i++) {
1312*053f45beSAndroid Build Coastguard Worker 		free(env.allow_filters[i].file_glob);
1313*053f45beSAndroid Build Coastguard Worker 		free(env.allow_filters[i].prog_glob);
1314*053f45beSAndroid Build Coastguard Worker 	}
1315*053f45beSAndroid Build Coastguard Worker 	free(env.allow_filters);
1316*053f45beSAndroid Build Coastguard Worker 	for (i = 0; i < env.deny_filter_cnt; i++) {
1317*053f45beSAndroid Build Coastguard Worker 		free(env.deny_filters[i].file_glob);
1318*053f45beSAndroid Build Coastguard Worker 		free(env.deny_filters[i].prog_glob);
1319*053f45beSAndroid Build Coastguard Worker 	}
1320*053f45beSAndroid Build Coastguard Worker 	free(env.deny_filters);
1321*053f45beSAndroid Build Coastguard Worker 	return -err;
1322*053f45beSAndroid Build Coastguard Worker }
1323