Skip to content

Commit 1cf938e

Browse files
committed
sync: sync to latest versitat version from kernel
Sync up to upstream commit: c8632acf193b ("Merge branch 'bpf-token'") This includes absolute value and top-n features support. Signed-off-by: Andrii Nakryiko <[email protected]>
1 parent ff14cbb commit 1cf938e

File tree

1 file changed

+76
-15
lines changed

1 file changed

+76
-15
lines changed

src/veristat.c

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <libelf.h>
1919
#include <gelf.h>
2020
#include <float.h>
21+
#include <math.h>
2122

2223
#ifndef ARRAY_SIZE
2324
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
@@ -99,6 +100,7 @@ struct stat_specs {
99100
enum stat_id ids[ALL_STATS_CNT];
100101
enum stat_variant variants[ALL_STATS_CNT];
101102
bool asc[ALL_STATS_CNT];
103+
bool abs[ALL_STATS_CNT];
102104
int lens[ALL_STATS_CNT * 3]; /* 3x for comparison mode */
103105
};
104106

@@ -133,6 +135,7 @@ struct filter {
133135
int stat_id;
134136
enum stat_variant stat_var;
135137
long value;
138+
bool abs;
136139
};
137140

138141
static struct env {
@@ -142,10 +145,12 @@ static struct env {
142145
bool debug;
143146
bool quiet;
144147
bool force_checkpoints;
148+
bool force_reg_invariants;
145149
enum resfmt out_fmt;
146150
bool show_version;
147151
bool comparison_mode;
148152
bool replay_mode;
153+
int top_n;
149154

150155
int log_level;
151156
int log_size;
@@ -210,15 +215,18 @@ static const struct argp_option opts[] = {
210215
{ "log-level", 'l', "LEVEL", 0, "Verifier log level (default 0 for normal mode, 1 for verbose mode)" },
211216
{ "log-fixed", OPT_LOG_FIXED, NULL, 0, "Disable verifier log rotation" },
212217
{ "log-size", OPT_LOG_SIZE, "BYTES", 0, "Customize verifier log size (default to 16MB)" },
213-
{ "test-states", 't', NULL, 0,
214-
"Force frequent BPF verifier state checkpointing (set BPF_F_TEST_STATE_FREQ program flag)" },
218+
{ "top-n", 'n', "N", 0, "Emit only up to first N results." },
215219
{ "quiet", 'q', NULL, 0, "Quiet mode" },
216220
{ "emit", 'e', "SPEC", 0, "Specify stats to be emitted" },
217221
{ "sort", 's', "SPEC", 0, "Specify sort order" },
218222
{ "output-format", 'o', "FMT", 0, "Result output format (table, csv), default is table." },
219223
{ "compare", 'C', NULL, 0, "Comparison mode" },
220224
{ "replay", 'R', NULL, 0, "Replay mode" },
221225
{ "filter", 'f', "FILTER", 0, "Filter expressions (or @filename for file with expressions)." },
226+
{ "test-states", 't', NULL, 0,
227+
"Force frequent BPF verifier state checkpointing (set BPF_F_TEST_STATE_FREQ program flag)" },
228+
{ "test-reg-invariants", 'r', NULL, 0,
229+
"Force BPF verifier failure on register invariant violation (BPF_F_TEST_REG_INVARIANTS program flag)" },
222230
{},
223231
};
224232

@@ -290,6 +298,16 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
290298
case 't':
291299
env.force_checkpoints = true;
292300
break;
301+
case 'r':
302+
env.force_reg_invariants = true;
303+
break;
304+
case 'n':
305+
errno = 0;
306+
env.top_n = strtol(arg, NULL, 10);
307+
if (errno) {
308+
fprintf(stderr, "invalid top N specifier: %s\n", arg);
309+
argp_usage(state);
310+
}
293311
case 'C':
294312
env.comparison_mode = true;
295313
break;
@@ -455,7 +473,8 @@ static struct {
455473
{ OP_EQ, "=" },
456474
};
457475

458-
static bool parse_stat_id_var(const char *name, size_t len, int *id, enum stat_variant *var);
476+
static bool parse_stat_id_var(const char *name, size_t len, int *id,
477+
enum stat_variant *var, bool *is_abs);
459478

460479
static int append_filter(struct filter **filters, int *cnt, const char *str)
461480
{
@@ -488,13 +507,14 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
488507
long val;
489508
const char *end = str;
490509
const char *op_str;
510+
bool is_abs;
491511

492512
op_str = operators[i].op_str;
493513
p = strstr(str, op_str);
494514
if (!p)
495515
continue;
496516

497-
if (!parse_stat_id_var(str, p - str, &id, &var)) {
517+
if (!parse_stat_id_var(str, p - str, &id, &var, &is_abs)) {
498518
fprintf(stderr, "Unrecognized stat name in '%s'!\n", str);
499519
return -EINVAL;
500520
}
@@ -533,6 +553,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
533553
f->stat_id = id;
534554
f->stat_var = var;
535555
f->op = operators[i].op_kind;
556+
f->abs = true;
536557
f->value = val;
537558

538559
*cnt += 1;
@@ -657,7 +678,8 @@ static struct stat_def {
657678
[MARK_READ_MAX_LEN] = { "Max mark read length", {"max_mark_read_len", "mark_read"}, },
658679
};
659680

660-
static bool parse_stat_id_var(const char *name, size_t len, int *id, enum stat_variant *var)
681+
static bool parse_stat_id_var(const char *name, size_t len, int *id,
682+
enum stat_variant *var, bool *is_abs)
661683
{
662684
static const char *var_sfxs[] = {
663685
[VARIANT_A] = "_a",
@@ -667,6 +689,14 @@ static bool parse_stat_id_var(const char *name, size_t len, int *id, enum stat_v
667689
};
668690
int i, j, k;
669691

692+
/* |<stat>| means we take absolute value of given stat */
693+
*is_abs = false;
694+
if (len > 2 && name[0] == '|' && name[len - 1] == '|') {
695+
*is_abs = true;
696+
name += 1;
697+
len -= 2;
698+
}
699+
670700
for (i = 0; i < ARRAY_SIZE(stat_defs); i++) {
671701
struct stat_def *def = &stat_defs[i];
672702
size_t alias_len, sfx_len;
@@ -722,7 +752,7 @@ static bool is_desc_sym(char c)
722752
static int parse_stat(const char *stat_name, struct stat_specs *specs)
723753
{
724754
int id;
725-
bool has_order = false, is_asc = false;
755+
bool has_order = false, is_asc = false, is_abs = false;
726756
size_t len = strlen(stat_name);
727757
enum stat_variant var;
728758

@@ -737,14 +767,15 @@ static int parse_stat(const char *stat_name, struct stat_specs *specs)
737767
len -= 1;
738768
}
739769

740-
if (!parse_stat_id_var(stat_name, len, &id, &var)) {
770+
if (!parse_stat_id_var(stat_name, len, &id, &var, &is_abs)) {
741771
fprintf(stderr, "Unrecognized stat name '%s'\n", stat_name);
742772
return -ESRCH;
743773
}
744774

745775
specs->ids[specs->spec_cnt] = id;
746776
specs->variants[specs->spec_cnt] = var;
747777
specs->asc[specs->spec_cnt] = has_order ? is_asc : stat_defs[id].asc_by_default;
778+
specs->abs[specs->spec_cnt] = is_abs;
748779
specs->spec_cnt++;
749780

750781
return 0;
@@ -997,6 +1028,8 @@ static int process_prog(const char *filename, struct bpf_object *obj, struct bpf
9971028

9981029
if (env.force_checkpoints)
9991030
bpf_program__set_flags(prog, bpf_program__flags(prog) | BPF_F_TEST_STATE_FREQ);
1031+
if (env.force_reg_invariants)
1032+
bpf_program__set_flags(prog, bpf_program__flags(prog) | BPF_F_TEST_REG_INVARIANTS);
10001033

10011034
err = bpf_object__load(obj);
10021035
env.progs_processed++;
@@ -1103,7 +1136,7 @@ static int process_obj(const char *filename)
11031136
}
11041137

11051138
static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
1106-
enum stat_id id, bool asc)
1139+
enum stat_id id, bool asc, bool abs)
11071140
{
11081141
int cmp = 0;
11091142

@@ -1124,6 +1157,11 @@ static int cmp_stat(const struct verif_stats *s1, const struct verif_stats *s2,
11241157
long v1 = s1->stats[id];
11251158
long v2 = s2->stats[id];
11261159

1160+
if (abs) {
1161+
v1 = v1 < 0 ? -v1 : v1;
1162+
v2 = v2 < 0 ? -v2 : v2;
1163+
}
1164+
11271165
if (v1 != v2)
11281166
cmp = v1 < v2 ? -1 : 1;
11291167
break;
@@ -1142,7 +1180,8 @@ static int cmp_prog_stats(const void *v1, const void *v2)
11421180
int i, cmp;
11431181

11441182
for (i = 0; i < env.sort_spec.spec_cnt; i++) {
1145-
cmp = cmp_stat(s1, s2, env.sort_spec.ids[i], env.sort_spec.asc[i]);
1183+
cmp = cmp_stat(s1, s2, env.sort_spec.ids[i],
1184+
env.sort_spec.asc[i], env.sort_spec.abs[i]);
11461185
if (cmp != 0)
11471186
return cmp;
11481187
}
@@ -1211,15 +1250,21 @@ static void fetch_join_stat_value(const struct verif_stats_join *s,
12111250

12121251
static int cmp_join_stat(const struct verif_stats_join *s1,
12131252
const struct verif_stats_join *s2,
1214-
enum stat_id id, enum stat_variant var, bool asc)
1253+
enum stat_id id, enum stat_variant var,
1254+
bool asc, bool abs)
12151255
{
12161256
const char *str1 = NULL, *str2 = NULL;
1217-
double v1, v2;
1257+
double v1 = 0.0, v2 = 0.0;
12181258
int cmp = 0;
12191259

12201260
fetch_join_stat_value(s1, id, var, &str1, &v1);
12211261
fetch_join_stat_value(s2, id, var, &str2, &v2);
12221262

1263+
if (abs) {
1264+
v1 = fabs(v1);
1265+
v2 = fabs(v2);
1266+
}
1267+
12231268
if (str1)
12241269
cmp = strcmp(str1, str2);
12251270
else if (v1 != v2)
@@ -1237,7 +1282,8 @@ static int cmp_join_stats(const void *v1, const void *v2)
12371282
cmp = cmp_join_stat(s1, s2,
12381283
env.sort_spec.ids[i],
12391284
env.sort_spec.variants[i],
1240-
env.sort_spec.asc[i]);
1285+
env.sort_spec.asc[i],
1286+
env.sort_spec.abs[i]);
12411287
if (cmp != 0)
12421288
return cmp;
12431289
}
@@ -1720,6 +1766,9 @@ static bool is_join_stat_filter_matched(struct filter *f, const struct verif_sta
17201766

17211767
fetch_join_stat_value(stats, f->stat_id, f->stat_var, &str, &value);
17221768

1769+
if (f->abs)
1770+
value = fabs(value);
1771+
17231772
switch (f->op) {
17241773
case OP_EQ: return value > f->value - eps && value < f->value + eps;
17251774
case OP_NEQ: return value < f->value - eps || value > f->value + eps;
@@ -1766,7 +1815,7 @@ static int handle_comparison_mode(void)
17661815
struct stat_specs base_specs = {}, comp_specs = {};
17671816
struct stat_specs tmp_sort_spec;
17681817
enum resfmt cur_fmt;
1769-
int err, i, j, last_idx;
1818+
int err, i, j, last_idx, cnt;
17701819

17711820
if (env.filename_cnt != 2) {
17721821
fprintf(stderr, "Comparison mode expects exactly two input CSV files!\n\n");
@@ -1879,7 +1928,7 @@ static int handle_comparison_mode(void)
18791928
env.join_stat_cnt += 1;
18801929
}
18811930

1882-
/* now sort joined results accorsing to sort spec */
1931+
/* now sort joined results according to sort spec */
18831932
qsort(env.join_stats, env.join_stat_cnt, sizeof(*env.join_stats), cmp_join_stats);
18841933

18851934
/* for human-readable table output we need to do extra pass to
@@ -1896,16 +1945,22 @@ static int handle_comparison_mode(void)
18961945
output_comp_headers(cur_fmt);
18971946

18981947
last_idx = -1;
1948+
cnt = 0;
18991949
for (i = 0; i < env.join_stat_cnt; i++) {
19001950
const struct verif_stats_join *join = &env.join_stats[i];
19011951

19021952
if (!should_output_join_stats(join))
19031953
continue;
19041954

1955+
if (env.top_n && cnt >= env.top_n)
1956+
break;
1957+
19051958
if (cur_fmt == RESFMT_TABLE_CALCLEN)
19061959
last_idx = i;
19071960

19081961
output_comp_stats(join, cur_fmt, i == last_idx);
1962+
1963+
cnt++;
19091964
}
19101965

19111966
if (cur_fmt == RESFMT_TABLE_CALCLEN) {
@@ -1920,6 +1975,9 @@ static bool is_stat_filter_matched(struct filter *f, const struct verif_stats *s
19201975
{
19211976
long value = stats->stats[f->stat_id];
19221977

1978+
if (f->abs)
1979+
value = value < 0 ? -value : value;
1980+
19231981
switch (f->op) {
19241982
case OP_EQ: return value == f->value;
19251983
case OP_NEQ: return value != f->value;
@@ -1964,7 +2022,7 @@ static bool should_output_stats(const struct verif_stats *stats)
19642022
static void output_prog_stats(void)
19652023
{
19662024
const struct verif_stats *stats;
1967-
int i, last_stat_idx = 0;
2025+
int i, last_stat_idx = 0, cnt = 0;
19682026

19692027
if (env.out_fmt == RESFMT_TABLE) {
19702028
/* calculate column widths */
@@ -1984,7 +2042,10 @@ static void output_prog_stats(void)
19842042
stats = &env.prog_stats[i];
19852043
if (!should_output_stats(stats))
19862044
continue;
2045+
if (env.top_n && cnt >= env.top_n)
2046+
break;
19872047
output_stats(stats, env.out_fmt, i == last_stat_idx);
2048+
cnt++;
19882049
}
19892050
}
19902051

0 commit comments

Comments
 (0)