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